d2a2afcd6c5c19257f8ed2f746c3f94474f4ab78 braney Tue Sep 1 13:37:34 2026 -0700 hgTracks: let hg.conf set how big a step malloc takes, refs #38225 hgTracks loads tracks in parallel threads, and each thread grows its own memory pool in the 128 kB steps glibc uses by default. A heavy render asks the kernel to enlarge a pool about 133,000 times, work that does nothing for the reader. The new hg.conf setting mallocTopPad is a number of bytes. When it is set, cfgSetMallocTopPad() passes it to mallopt(M_TOP_PAD) before anything else in main() has a chance to allocate, so the heap grows in that size step instead. Absent or zero, glibc is left alone and nothing changes. Measured with the #38094 harness over eight sessions and two positions, at 32 MB: renders take 0.926 of the time they did, against a noise floor of 1.006. With the setting absent the same build measures 1.004, inside that noise floor. Through apache on a sandbox it measures 0.906. The cost is about 65 MB more resident memory on a heavy session and 8 to 20 MB on an ordinary view, where it also saves no measurable time; the saving and the cost are the same effect and appear together. The rendered image is byte identical either way. Off by default, so a server opts in. diff --git src/hg/hgTracks/renderMain.c src/hg/hgTracks/renderMain.c index 71ef4e7b212..362089b3054 100644 --- src/hg/hgTracks/renderMain.c +++ src/hg/hgTracks/renderMain.c @@ -6,30 +6,31 @@ #include "common.h" #include "linefile.h" #include "hash.h" #include "htmshell.h" #include "options.h" #include "errAbort.h" #include "portable.h" #include "cheapcgi.h" #include "ra.h" #include "hdb.h" #include "net.h" #include "hgTracks.h" #include "imageV2.h" #include "botDelay.h" +#include "hgConfig.h" static void usage() /* Print out usage and exit - just temporary. */ { errAbort( "hgRenderTracks - execute enough of browser to render a list of tracks\n" "usage:\n" " hgTrackRender track.ra cart.ra output.ps\n" "where track.ra is a trackDb.ra entry flattened out to include stuff inherited from parent\n" "and cart.ra is a file with the cart settings. Both .ra files should just have a single stanza\n" ); } static struct optionSpec options[] = { {NULL, 0}, @@ -102,30 +103,34 @@ verboseTime(2, "After load"); /* Initialize global image box. */ int sideSliceWidth = 0; // Just being explicit if (withLeftLabels) sideSliceWidth = (insideX - gfxBorder*3) + 2; theImgBox = imgBoxStart(database,chromName,winStart,winEnd,(!revCmplDisp),sideSliceWidth,tl.picWidth); makeActiveImage(trackList, NULL); verboseTime(2,"After makeActiveImage"); } boolean issueBotWarning; int main(int argc, char *argv[]) { +// First, before anything has a chance to allocate: read hg.conf and set how +// big a step the C library takes when it grows the heap. refs #38225 +cfgSetMallocTopPad(); + cgiSpoof(&argc, argv); if(argc == 1) { enteredMainTime = clock1000(); issueBotWarning = earlyBotCheck(enteredMainTime, "hgRenderTracks", delayFraction, 0, 0, "html"); // CGI call // htmlPushEarlyHandlers(); XXXX do I need to do this? hPrintDisable(); oldVars = hashNew(10); struct cart *cart = cartForSession(hUserCookie(), excludeVars, oldVars); // setup approriate CGI variables which tell hgTracks code what to do. cartSetBoolean(cart, "hgt.trackImgOnly", TRUE);