# HG changeset patch # User Bram Moolenaar # Date 1664379004 -7200 # Node ID b3de17181c19163cd3c3d3fea5cf13afdc2b8afe # Parent ce8e4c9cc95aea7faed10e91372193efa3ef1df9 patch 9.0.0618: calling function for reduce() has too much overhead Commit: https://github.com/vim/vim/commit/82418263fa91792e851cb0de879d1595327d5531 Author: Bram Moolenaar Date: Wed Sep 28 16:16:15 2022 +0100 patch 9.0.0618: calling function for reduce() has too much overhead Problem: Calling function for reduce() has too much overhead. Solution: Do not create a funccall_T every time. diff --git a/src/blob.c b/src/blob.c --- a/src/blob.c +++ b/src/blob.c @@ -559,6 +559,8 @@ blob_filter_map( blob_T *b_ret; int idx = 0; int rem; + typval_T newtv; + funccall_T *fc; if (filtermap == FILTERMAP_MAPNEW) { @@ -579,15 +581,16 @@ blob_filter_map( // set_vim_var_nr() doesn't set the type set_vim_var_type(VV_KEY, VAR_NUMBER); + // Create one funccal_T for all eval_expr_typval() calls. + fc = eval_expr_get_funccal(expr, &newtv); + for (i = 0; i < b->bv_ga.ga_len; i++) { - typval_T newtv; - tv.v_type = VAR_NUMBER; val = blob_get(b, i); tv.vval.v_number = val; set_vim_var_nr(VV_KEY, idx); - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL || did_emsg) break; if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) @@ -612,6 +615,9 @@ blob_filter_map( } ++idx; } + + if (fc != NULL) + remove_funccal(); } /* @@ -714,7 +720,7 @@ blob_reduce( argv[1].v_type = VAR_NUMBER; argv[1].vval.v_number = blob_get(b, i); - r = eval_expr_typval(expr, argv, 2, rettv); + r = eval_expr_typval(expr, argv, 2, NULL, rettv); clear_tv(&argv[0]); if (r == FAIL || called_emsg != called_emsg_start) diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -1315,6 +1315,8 @@ dict_filter_map( dictitem_T *di; int todo; int rem; + typval_T newtv; + funccall_T *fc; if (filtermap == FILTERMAP_MAPNEW) { @@ -1335,6 +1337,9 @@ dict_filter_map( d_ret = rettv->vval.v_dict; } + // Create one funccal_T for all eval_expr_typval() calls. + fc = eval_expr_get_funccal(expr, &newtv); + if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0) d->dv_lock = VAR_LOCKED; ht = &d->dv_hashtab; @@ -1345,7 +1350,6 @@ dict_filter_map( if (!HASHITEM_EMPTY(hi)) { int r; - typval_T newtv; --todo; di = HI2DI(hi); @@ -1357,8 +1361,7 @@ dict_filter_map( break; set_vim_var_string(VV_KEY, di->di_key, -1); newtv.v_type = VAR_UNKNOWN; - r = filter_map_one(&di->di_tv, expr, filtermap, - &newtv, &rem); + r = filter_map_one(&di->di_tv, expr, filtermap, fc, &newtv, &rem); clear_tv(get_vim_var_tv(VV_KEY)); if (r == FAIL || did_emsg) { @@ -1398,6 +1401,8 @@ dict_filter_map( } hash_unlock(ht); d->dv_lock = prev_lock; + if (fc != NULL) + remove_funccal(); } /* diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -216,12 +216,39 @@ eval_expr_valid_arg(typval_T *tv) } /* + * When calling eval_expr_typval() many times we only need one funccall_T. + * Returns NULL when no funccall_T is to be used. + * When returning non-NULL remove_funccal() must be called later. + */ + funccall_T * +eval_expr_get_funccal(typval_T *expr, typval_T *rettv) +{ + if (expr->v_type != VAR_PARTIAL) + return NULL; + + partial_T *partial = expr->vval.v_partial; + if (partial == NULL) + return NULL; + if (partial->pt_func == NULL + || partial->pt_func->uf_def_status == UF_NOT_COMPILED) + return NULL; + + return create_funccal(partial->pt_func, rettv); +} + +/* * Evaluate an expression, which can be a function, partial or string. * Pass arguments "argv[argc]". + * "fc_arg" is from eval_expr_get_funccal() or NULL; * Return the result in "rettv" and OK or FAIL. */ int -eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv) +eval_expr_typval( + typval_T *expr, + typval_T *argv, + int argc, + funccall_T *fc_arg, + typval_T *rettv) { char_u *s; char_u buf[NUMBUFLEN]; @@ -247,7 +274,8 @@ eval_expr_typval(typval_T *expr, typval_ if (partial->pt_func != NULL && partial->pt_func->uf_def_status != UF_NOT_COMPILED) { - funccall_T *fc = create_funccal(partial->pt_func, rettv); + funccall_T *fc = fc_arg != NULL ? fc_arg + : create_funccal(partial->pt_func, rettv); int r; if (fc == NULL) @@ -256,7 +284,8 @@ eval_expr_typval(typval_T *expr, typval_ // Shortcut to call a compiled function with minimal overhead. r = call_def_function(partial->pt_func, argc, argv, DEF_USE_PT_ARGV, partial, fc, rettv); - remove_funccal(); + if (fc_arg == NULL) + remove_funccal(); if (r == FAIL) return FAIL; } @@ -304,7 +333,7 @@ eval_expr_to_bool(typval_T *expr, int *e typval_T rettv; int res; - if (eval_expr_typval(expr, NULL, 0, &rettv) == FAIL) + if (eval_expr_typval(expr, NULL, 0, NULL, &rettv) == FAIL) { *error = TRUE; return FALSE; diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -6740,7 +6740,7 @@ indexof_eval_expr(typval_T *expr) argv[1] = *get_vim_var_tv(VV_VAL); newtv.v_type = VAR_UNKNOWN; - if (eval_expr_typval(expr, argv, 2, &newtv) == FAIL) + if (eval_expr_typval(expr, argv, 2, NULL, &newtv) == FAIL) return FALSE; found = tv_get_bool_chk(&newtv, &error); diff --git a/src/filepath.c b/src/filepath.c --- a/src/filepath.c +++ b/src/filepath.c @@ -1609,7 +1609,7 @@ checkitem_common(void *context, char_u * argv[0].vval.v_dict = dict; } - if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) + if (eval_expr_typval(expr, argv, 1, NULL, &rettv) == FAIL) goto theend; // We want to use -1, but also true/false should be allowed. diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -2320,6 +2320,7 @@ filter_map_one( typval_T *tv, // original value typval_T *expr, // callback filtermap_T filtermap, + funccall_T *fc, // from eval_expr_get_funccal() typval_T *newtv, // for map() and mapnew(): new value int *remp) // for filter(): remove flag { @@ -2329,7 +2330,7 @@ filter_map_one( copy_tv(tv, get_vim_var_tv(VV_VAL)); argv[0] = *get_vim_var_tv(VV_KEY); argv[1] = *get_vim_var_tv(VV_VAL); - if (eval_expr_typval(expr, argv, 2, newtv) == FAIL) + if (eval_expr_typval(expr, argv, 2, fc, newtv) == FAIL) goto theend; if (filtermap == FILTERMAP_FILTER) { @@ -2371,6 +2372,8 @@ list_filter_map( int idx = 0; int rem; listitem_T *li, *nli; + typval_T newtv; + funccall_T *fc; if (filtermap == FILTERMAP_MAPNEW) { @@ -2395,6 +2398,9 @@ list_filter_map( if (filtermap != FILTERMAP_FILTER && l->lv_lock == 0) l->lv_lock = VAR_LOCKED; + // Create one funccal_T for all eval_expr_typval() calls. + fc = eval_expr_get_funccal(expr, &newtv); + if (l->lv_first == &range_list_item) { varnumber_T val = l->lv_u.nonmat.lv_start; @@ -2413,13 +2419,12 @@ list_filter_map( for (idx = 0; idx < len; ++idx) { typval_T tv; - typval_T newtv; tv.v_type = VAR_NUMBER; tv.v_lock = 0; tv.vval.v_number = val; set_vim_var_nr(VV_KEY, idx); - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL) + if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL) break; if (did_emsg) { @@ -2457,15 +2462,13 @@ list_filter_map( // Materialized list: loop over the items for (li = l->lv_first; li != NULL; li = nli) { - typval_T newtv; - if (filtermap == FILTERMAP_MAP && value_check_lock( li->li_tv.v_lock, arg_errmsg, TRUE)) break; nli = li->li_next; set_vim_var_nr(VV_KEY, idx); - if (filter_map_one(&li->li_tv, expr, filtermap, - &newtv, &rem) == FAIL) + if (filter_map_one(&li->li_tv, expr, filtermap, fc, + &newtv, &rem) == FAIL) break; if (did_emsg) { @@ -2498,6 +2501,8 @@ list_filter_map( } l->lv_lock = prev_lock; + if (fc != NULL) + remove_funccal(); } /* @@ -3018,6 +3023,7 @@ list_reduce( int r; int called_emsg_start = called_emsg; int prev_locked; + funccall_T *fc; // Using reduce on a range() uses "range_idx" and "range_val". range_list = l != NULL && l->lv_first == &range_list_item; @@ -3055,6 +3061,9 @@ list_reduce( if (l == NULL) return; + // Create one funccal_T for all eval_expr_typval() calls. + fc = eval_expr_get_funccal(expr, rettv); + prev_locked = l->lv_lock; l->lv_lock = VAR_FIXED; // disallow the list changing here @@ -3071,7 +3080,7 @@ list_reduce( else argv[1] = li->li_tv; - r = eval_expr_typval(expr, argv, 2, rettv); + r = eval_expr_typval(expr, argv, 2, fc, rettv); if (argv[0].v_type != VAR_NUMBER && argv[0].v_type != VAR_UNKNOWN) clear_tv(&argv[0]); @@ -3088,6 +3097,9 @@ list_reduce( li = li->li_next; } + if (fc != NULL) + remove_funccal(); + l->lv_lock = prev_locked; } diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -6,7 +6,8 @@ void eval_clear(void); void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip); int eval_to_bool(char_u *arg, int *error, exarg_T *eap, int skip); int eval_expr_valid_arg(typval_T *tv); -int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv); +funccall_T *eval_expr_get_funccal(typval_T *expr, typval_T *rettv); +int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, funccall_T *fc_arg, typval_T *rettv); int eval_expr_to_bool(typval_T *expr, int *error); char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip); void init_evalarg(evalarg_T *evalarg); diff --git a/src/proto/list.pro b/src/proto/list.pro --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -52,7 +52,7 @@ void init_static_list(staticList10_T *sl void f_list2str(typval_T *argvars, typval_T *rettv); void f_sort(typval_T *argvars, typval_T *rettv); void f_uniq(typval_T *argvars, typval_T *rettv); -int filter_map_one(typval_T *tv, typval_T *expr, filtermap_T filtermap, typval_T *newtv, int *remp); +int filter_map_one(typval_T *tv, typval_T *expr, filtermap_T filtermap, funccall_T *fc, typval_T *newtv, int *remp); void f_filter(typval_T *argvars, typval_T *rettv); void f_map(typval_T *argvars, typval_T *rettv); void f_mapnew(typval_T *argvars, typval_T *rettv); diff --git a/src/strings.c b/src/strings.c --- a/src/strings.c +++ b/src/strings.c @@ -882,6 +882,8 @@ string_filter_map( int len = 0; int idx = 0; int rem; + typval_T newtv; + funccall_T *fc; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -889,18 +891,19 @@ string_filter_map( // set_vim_var_nr() doesn't set the type set_vim_var_type(VV_KEY, VAR_NUMBER); + // Create one funccal_T for all eval_expr_typval() calls. + fc = eval_expr_get_funccal(expr, &newtv); + ga_init2(&ga, sizeof(char), 80); for (p = str; *p != NUL; p += len) { - typval_T newtv; - if (copy_first_char_to_tv(p, &tv) == FAIL) break; len = (int)STRLEN(tv.vval.v_string); newtv.v_type = VAR_UNKNOWN; set_vim_var_nr(VV_KEY, idx); - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + if (filter_map_one(&tv, expr, filtermap, fc, &newtv, &rem) == FAIL || did_emsg) { clear_tv(&newtv); @@ -929,6 +932,8 @@ string_filter_map( } ga_append(&ga, NUL); rettv->vval.v_string = ga.ga_data; + if (fc != NULL) + remove_funccal(); } /* @@ -947,6 +952,7 @@ string_reduce( typval_T argv[3]; int r; int called_emsg_start = called_emsg; + funccall_T *fc; if (argvars[2].v_type == VAR_UNKNOWN) { @@ -964,6 +970,9 @@ string_reduce( else copy_tv(&argvars[2], rettv); + // Create one funccal_T for all eval_expr_typval() calls. + fc = eval_expr_get_funccal(expr, rettv); + for ( ; *p != NUL; p += len) { argv[0] = *rettv; @@ -971,13 +980,16 @@ string_reduce( break; len = (int)STRLEN(argv[1].vval.v_string); - r = eval_expr_typval(expr, argv, 2, rettv); + r = eval_expr_typval(expr, argv, 2, fc, rettv); clear_tv(&argv[0]); clear_tv(&argv[1]); if (r == FAIL || called_emsg != called_emsg_start) return; } + + if (fc != NULL) + remove_funccal(); } static void diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 618, +/**/ 617, /**/ 616,