diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-30 02:12:27 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-06-30 08:01:29 +0200 |
commit | 6cf6f1eaee1f6be2b936c2ff0e5852c00740edb4 (patch) | |
tree | 155241f4286faca0cd1c79633dfcb527d90b2fe0 | |
parent | f99800758e24ff159808ca0b44064f548ed77a26 (diff) | |
download | busybox-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.c | 164 |
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 */ |
121 | typedef struct var_s { | 120 | typedef 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 */ | ||
196 | typedef 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 | |||
204 | typedef struct tsplitter_s { | 194 | typedef 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 | ||
643 | static const char EMSG_INTERNAL_ERROR[] ALIGN1 = "Internal error"; | ||
644 | static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string"; | 631 | static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string"; |
645 | static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token"; | 632 | static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token"; |
646 | static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero"; | 633 | static 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 */ | ||
1054 | static 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 | |||
1090 | static 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 */ | ||
1713 | static var *nvalloc(int sz) | ||
1714 | { | ||
1715 | return xzalloc(sz * sizeof(var)); | ||
1716 | } | ||
1717 | |||
1718 | static 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 | |||
1796 | static node *mk_splitter(const char *s, tsplitter *spl) | 1747 | static 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 | */ |
1821 | static regex_t *as_regex(node *op, regex_t *preg) | 1772 | static 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 |