aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2021-06-30 02:12:27 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2021-06-30 08:01:29 +0200
commit6cf6f1eaee1f6be2b936c2ff0e5852c00740edb4 (patch)
tree155241f4286faca0cd1c79633dfcb527d90b2fe0
parentf99800758e24ff159808ca0b44064f548ed77a26 (diff)
downloadbusybox-w32-6cf6f1eaee1f6be2b936c2ff0e5852c00740edb4.tar.gz
busybox-w32-6cf6f1eaee1f6be2b936c2ff0e5852c00740edb4.tar.bz2
busybox-w32-6cf6f1eaee1f6be2b936c2ff0e5852c00740edb4.zip
awk: remove custom pool allocator for temporary awk variables
It seems to be designed to reduce overhead of malloc's auxiliary data, by allocating at least 64 variables as a block. With "struct var" being about 20-32 bytes long (32/64 bits), malloc overhead for one temporary indeed is high, ~33% more memory used than needed. function old new delta evaluate 3137 3145 +8 modprobe_main 798 803 +5 exec_builtin 1414 1419 +5 awk_printf 476 481 +5 as_regex 132 137 +5 EMSG_INTERNAL_ERROR 15 - -15 nvfree 169 116 -53 nvalloc 145 - -145 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 5/1 up/down: 28/-213) Total: -185 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--editors/awk.c164
1 files changed, 61 insertions, 103 deletions
diff --git a/editors/awk.c b/editors/awk.c
index a4cd3cf93..35c11ec58 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -93,7 +93,6 @@ enum {
93}; 93};
94 94
95#define MAXVARFMT 240 95#define MAXVARFMT 240
96#define MINNVBLOCK 64
97 96
98/* variable flags */ 97/* variable flags */
99#define VF_NUMBER 0x0001 /* 1 = primary type is number */ 98#define VF_NUMBER 0x0001 /* 1 = primary type is number */
@@ -120,8 +119,8 @@ typedef struct walker_list {
120/* Variable */ 119/* Variable */
121typedef struct var_s { 120typedef struct var_s {
122 unsigned type; /* flags */ 121 unsigned type; /* flags */
123 double number;
124 char *string; 122 char *string;
123 double number;
125 union { 124 union {
126 int aidx; /* func arg idx (for compilation stage) */ 125 int aidx; /* func arg idx (for compilation stage) */
127 struct xhash_s *array; /* array ptr */ 126 struct xhash_s *array; /* array ptr */
@@ -192,15 +191,6 @@ typedef struct node_s {
192 } a; 191 } a;
193} node; 192} node;
194 193
195/* Block of temporary variables */
196typedef struct nvblock_s {
197 int size;
198 var *pos;
199 struct nvblock_s *prev;
200 struct nvblock_s *next;
201 var nv[];
202} nvblock;
203
204typedef struct tsplitter_s { 194typedef struct tsplitter_s {
205 node n; 195 node n;
206 regex_t re[2]; 196 regex_t re[2];
@@ -537,7 +527,6 @@ struct globals {
537 int nfields; 527 int nfields;
538 int maxfields; /* used in fsrealloc() only */ 528 int maxfields; /* used in fsrealloc() only */
539 var *Fields; 529 var *Fields;
540 nvblock *g_cb;
541 char *g_pos; 530 char *g_pos;
542 char g_saved_ch; 531 char g_saved_ch;
543 smallint icase; 532 smallint icase;
@@ -605,7 +594,6 @@ struct globals2 {
605#define nfields (G1.nfields ) 594#define nfields (G1.nfields )
606#define maxfields (G1.maxfields ) 595#define maxfields (G1.maxfields )
607#define Fields (G1.Fields ) 596#define Fields (G1.Fields )
608#define g_cb (G1.g_cb )
609#define g_pos (G1.g_pos ) 597#define g_pos (G1.g_pos )
610#define g_saved_ch (G1.g_saved_ch ) 598#define g_saved_ch (G1.g_saved_ch )
611#define icase (G1.icase ) 599#define icase (G1.icase )
@@ -640,7 +628,6 @@ static int awk_exit(int) NORETURN;
640 628
641/* ---- error handling ---- */ 629/* ---- error handling ---- */
642 630
643static const char EMSG_INTERNAL_ERROR[] ALIGN1 = "Internal error";
644static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string"; 631static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string";
645static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token"; 632static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token";
646static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero"; 633static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero";
@@ -1050,77 +1037,6 @@ static int istrue(var *v)
1050 return (v->string && v->string[0]); 1037 return (v->string && v->string[0]);
1051} 1038}
1052 1039
1053/* temporary variables allocator. Last allocated should be first freed */
1054static var *nvalloc(int n)
1055{
1056 nvblock *pb = NULL;
1057 var *v, *r;
1058 int size;
1059
1060 while (g_cb) {
1061 pb = g_cb;
1062 if ((g_cb->pos - g_cb->nv) + n <= g_cb->size)
1063 break;
1064 g_cb = g_cb->next;
1065 }
1066
1067 if (!g_cb) {
1068 size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
1069 g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var));
1070 g_cb->size = size;
1071 g_cb->pos = g_cb->nv;
1072 g_cb->prev = pb;
1073 /*g_cb->next = NULL; - xzalloc did it */
1074 if (pb)
1075 pb->next = g_cb;
1076 }
1077
1078 v = r = g_cb->pos;
1079 g_cb->pos += n;
1080
1081 while (v < g_cb->pos) {
1082 v->type = 0;
1083 v->string = NULL;
1084 v++;
1085 }
1086
1087 return r;
1088}
1089
1090static void nvfree(var *v)
1091{
1092 var *p;
1093
1094 if (v < g_cb->nv || v >= g_cb->pos)
1095 syntax_error(EMSG_INTERNAL_ERROR);
1096
1097 for (p = v; p < g_cb->pos; p++) {
1098 if ((p->type & (VF_ARRAY | VF_CHILD)) == VF_ARRAY) {
1099 clear_array(iamarray(p));
1100 free(p->x.array->items);
1101 free(p->x.array);
1102 }
1103 if (p->type & VF_WALK) {
1104 walker_list *n;
1105 walker_list *w = p->x.walker;
1106 debug_printf_walker("nvfree: freeing walker @%p\n", &p->x.walker);
1107 p->x.walker = NULL;
1108 while (w) {
1109 n = w->prev;
1110 debug_printf_walker(" free(%p)\n", w);
1111 free(w);
1112 w = n;
1113 }
1114 }
1115 clrvar(p);
1116 }
1117
1118 g_cb->pos = v;
1119 while (g_cb->prev && g_cb->pos == g_cb->nv) {
1120 g_cb = g_cb->prev;
1121 }
1122}
1123
1124/* ------- awk program text parsing ------- */ 1040/* ------- awk program text parsing ------- */
1125 1041
1126/* Parse next token pointed by global pos, place results into global t_XYZ variables. 1042/* Parse next token pointed by global pos, place results into global t_XYZ variables.
@@ -1793,6 +1709,41 @@ static void parse_program(char *p)
1793 1709
1794/* -------- program execution part -------- */ 1710/* -------- program execution part -------- */
1795 1711
1712/* temporary variables allocator */
1713static var *nvalloc(int sz)
1714{
1715 return xzalloc(sz * sizeof(var));
1716}
1717
1718static void nvfree(var *v, int sz)
1719{
1720 var *p = v;
1721
1722 while (--sz >= 0) {
1723 if ((p->type & (VF_ARRAY | VF_CHILD)) == VF_ARRAY) {
1724 clear_array(iamarray(p));
1725 free(p->x.array->items);
1726 free(p->x.array);
1727 }
1728 if (p->type & VF_WALK) {
1729 walker_list *n;
1730 walker_list *w = p->x.walker;
1731 debug_printf_walker("nvfree: freeing walker @%p\n", &p->x.walker);
1732 p->x.walker = NULL;
1733 while (w) {
1734 n = w->prev;
1735 debug_printf_walker(" free(%p)\n", w);
1736 free(w);
1737 w = n;
1738 }
1739 }
1740 clrvar(p);
1741 p++;
1742 }
1743
1744 free(v);
1745}
1746
1796static node *mk_splitter(const char *s, tsplitter *spl) 1747static node *mk_splitter(const char *s, tsplitter *spl)
1797{ 1748{
1798 regex_t *re, *ire; 1749 regex_t *re, *ire;
@@ -1814,9 +1765,9 @@ static node *mk_splitter(const char *s, tsplitter *spl)
1814 return n; 1765 return n;
1815} 1766}
1816 1767
1817/* use node as a regular expression. Supplied with node ptr and regex_t 1768/* Use node as a regular expression. Supplied with node ptr and regex_t
1818 * storage space. Return ptr to regex (if result points to preg, it should 1769 * storage space. Return ptr to regex (if result points to preg, it should
1819 * be later regfree'd manually 1770 * be later regfree'd manually).
1820 */ 1771 */
1821static regex_t *as_regex(node *op, regex_t *preg) 1772static regex_t *as_regex(node *op, regex_t *preg)
1822{ 1773{
@@ -1840,7 +1791,7 @@ static regex_t *as_regex(node *op, regex_t *preg)
1840 cflags &= ~REG_EXTENDED; 1791 cflags &= ~REG_EXTENDED;
1841 xregcomp(preg, s, cflags); 1792 xregcomp(preg, s, cflags);
1842 } 1793 }
1843 nvfree(v); 1794 nvfree(v, 1);
1844 return preg; 1795 return preg;
1845} 1796}
1846 1797
@@ -2292,6 +2243,8 @@ static char *awk_printf(node *n, int *len)
2292 var *v, *arg; 2243 var *v, *arg;
2293 2244
2294 v = nvalloc(1); 2245 v = nvalloc(1);
2246//TODO: above, to avoid allocating a single temporary var, take a pointer
2247//to a temporary that our caller (evaluate()) already has?
2295 fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v))); 2248 fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v)));
2296 2249
2297 i = 0; 2250 i = 0;
@@ -2333,7 +2286,7 @@ static char *awk_printf(node *n, int *len)
2333 } 2286 }
2334 2287
2335 free(fmt); 2288 free(fmt);
2336 nvfree(v); 2289 nvfree(v, 1);
2337 b = xrealloc(b, i + 1); 2290 b = xrealloc(b, i + 1);
2338 b[i] = '\0'; 2291 b[i] = '\0';
2339#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS 2292#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
@@ -2661,14 +2614,14 @@ static NOINLINE var *exec_builtin(node *op, var *res)
2661 break; 2614 break;
2662 } 2615 }
2663 2616
2664 nvfree(tv); 2617 nvfree(tv, 4);
2665 return res; 2618 return res;
2666#undef tspl 2619#undef tspl
2667} 2620}
2668 2621
2669/* 2622/*
2670 * Evaluate node - the heart of the program. Supplied with subtree 2623 * Evaluate node - the heart of the program. Supplied with subtree
2671 * and place where to store result. returns ptr to result. 2624 * and place where to store result. Returns ptr to result.
2672 */ 2625 */
2673#define XC(n) ((n) >> 8) 2626#define XC(n) ((n) >> 8)
2674 2627
@@ -2953,33 +2906,38 @@ static var *evaluate(node *op, var *res)
2953 break; 2906 break;
2954 2907
2955 case XC( OC_FUNC ): { 2908 case XC( OC_FUNC ): {
2956 var *vbeg, *v; 2909 var *tv, *sv_fnargs;
2957 const char *sv_progname; 2910 const char *sv_progname;
2911 int nargs1, i;
2912
2958 debug_printf_eval("FUNC\n"); 2913 debug_printf_eval("FUNC\n");
2959 2914
2960 /* The body might be empty, still has to eval the args */
2961 if (!op->r.n->info && !op->r.f->body.first) 2915 if (!op->r.n->info && !op->r.f->body.first)
2962 syntax_error(EMSG_UNDEF_FUNC); 2916 syntax_error(EMSG_UNDEF_FUNC);
2963 2917
2964 vbeg = v = nvalloc(op->r.f->nargs + 1); 2918 /* The body might be empty, still has to eval the args */
2919 nargs1 = op->r.f->nargs + 1;
2920 tv = nvalloc(nargs1);
2921 i = 0;
2965 while (op1) { 2922 while (op1) {
2923//TODO: explain why one iteration is done even for the case p->r.f->nargs == 0
2966 var *arg = evaluate(nextarg(&op1), v1); 2924 var *arg = evaluate(nextarg(&op1), v1);
2967 copyvar(v, arg); 2925 copyvar(&tv[i], arg);
2968 v->type |= VF_CHILD; 2926 tv[i].type |= VF_CHILD;
2969 v->x.parent = arg; 2927 tv[i].x.parent = arg;
2970 if (++v - vbeg >= op->r.f->nargs) 2928 if (++i >= op->r.f->nargs)
2971 break; 2929 break;
2972 } 2930 }
2973 2931
2974 v = fnargs; 2932 sv_fnargs = fnargs;
2975 fnargs = vbeg;
2976 sv_progname = g_progname; 2933 sv_progname = g_progname;
2977 2934
2935 fnargs = tv;
2978 res = evaluate(op->r.f->body.first, res); 2936 res = evaluate(op->r.f->body.first, res);
2937 nvfree(fnargs, nargs1);
2979 2938
2980 g_progname = sv_progname; 2939 g_progname = sv_progname;
2981 nvfree(fnargs); 2940 fnargs = sv_fnargs;
2982 fnargs = v;
2983 2941
2984 break; 2942 break;
2985 } 2943 }
@@ -3301,7 +3259,7 @@ static var *evaluate(node *op, var *res)
3301 break; 3259 break;
3302 } /* while (op) */ 3260 } /* while (op) */
3303 3261
3304 nvfree(v1); 3262 nvfree(v1, 2);
3305 debug_printf_eval("returning from %s(): %p\n", __func__, res); 3263 debug_printf_eval("returning from %s(): %p\n", __func__, res);
3306 return res; 3264 return res;
3307#undef fnargs 3265#undef fnargs