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 |
