So I tinkered with the way ksh(1) tracks memory allocation, trying to make it faster in the general case. One approach used a RB tree, I wrote since a simple hash table implementation which seems to work rather well.
But the actual problem I'd first like to solve is a corner case. I use HISTSIZE=20000, and when the actual line count in my histfile approaches 25000 (1.25 * HISTSIZE), ksh(1) has a hard time handling it. The main reason is that it calls afree() ~5000 times in a loop, with afree() traversing the APERM freelist, which contains >20000 elements. This is expensive. For history lines, we don't actually need to keep track of allocations using an area, history lines are private to history.c and no gc/whatever is needed there. So here's a diff that just uses strdup(3)/free(3). Comments? ok? Index: history.c =================================================================== RCS file: /d/cvs/src/bin/ksh/history.c,v retrieving revision 1.64 diff -u -p -p -u -r1.64 history.c --- history.c 11 Aug 2017 19:37:58 -0000 1.64 +++ history.c 15 Aug 2017 01:14:58 -0000 @@ -428,7 +428,7 @@ histbackup(void) if (histptr >= history && last_line != hist_source->line) { hist_source->line--; - afree(*histptr, APERM); + free(*histptr); histptr--; last_line = hist_source->line; } @@ -613,14 +613,15 @@ histsave(int lno, const char *cmd, int d #endif } - c = str_save(cmd, APERM); + if ((c = strdup(cmd)) == NULL) + internal_errorf(1, "unable to allocate memory"); if ((cp = strrchr(c, '\n')) != NULL) *cp = '\0'; if (histptr < history + histsize - 1) histptr++; else { /* remove oldest command */ - afree(*history, APERM); + free(*history); memmove(history, history + 1, (histsize - 1) * sizeof(*history)); } -- jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF DDCC 0DFA 74AE 1524 E7EE