# HG changeset patch # User Bram Moolenaar # Date 1589402704 -7200 # Node ID 4c317d8c105138757f99a3b71583c159f4a57786 # Parent 9ceee4daa596d083de8c7362de667dc92953f954 patch 8.2.0751: Vim9: performance can be improved Commit: https://github.com/vim/vim/commit/7e9f351b2e69b498c4ee5004d7459844e1ba191a Author: Bram Moolenaar Date: Wed May 13 22:44:22 2020 +0200 patch 8.2.0751: Vim9: performance can be improved Problem: Vim9: performance can be improved. Solution: Don't call break. Inline check for list materialize. Make an inline version of ga_grow(). diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -2399,7 +2399,7 @@ channel_get_json( list_T *l = item->jq_value->vval.v_list; typval_T *tv; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); tv = &l->lv_first->li_tv; if ((without_callback || !item->jq_no_callback) @@ -5302,7 +5302,7 @@ get_job_options(typval_T *tv, jobopt_T * return FAIL; } - range_list_materialize(item->vval.v_list); + CHECK_LIST_MATERIALIZE(item->vval.v_list); li = item->vval.v_list->lv_first; for (; li != NULL && n < 16; li = li->li_next, n++) { @@ -5729,7 +5729,7 @@ win32_build_cmd(list_T *l, garray_T *gap listitem_T *li; char_u *s; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); FOR_ALL_LIST_ITEMS(l, li) { s = tv_get_string_chk(&li->li_tv); diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -1449,7 +1449,7 @@ eval_for_line( else { // Need a real list here. - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); // No need to increment the refcount, it's already set for // the list being used in "tv". diff --git a/src/evalbuffer.c b/src/evalbuffer.c --- a/src/evalbuffer.c +++ b/src/evalbuffer.c @@ -183,7 +183,7 @@ set_buffer_lines( rettv->vval.v_number = 1; // FAIL goto done; } - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); li = l->lv_first; } else diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2275,7 +2275,7 @@ execute_common(typval_T *argvars, typval { listitem_T *item; - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); item = list->lv_first; do_cmdline(NULL, get_list_line, (void *)&item, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); @@ -2820,7 +2820,7 @@ common_function(typval_T *argvars, typva copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); if (lv_len > 0) { - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); FOR_ALL_LIST_ITEMS(list, li) copy_tv(&li->li_tv, &pt->pt_argv[i++]); } @@ -4912,7 +4912,7 @@ f_index(typval_T *argvars, typval_T *ret l = argvars[0].vval.v_list; if (l != NULL) { - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); item = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { @@ -5018,7 +5018,7 @@ f_inputlist(typval_T *argvars, typval_T msg_clr_eos(); l = argvars[0].vval.v_list; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); FOR_ALL_LIST_ITEMS(l, li) { msg_puts((char *)tv_get_string(&li->li_tv)); @@ -5494,7 +5494,7 @@ find_some_match(typval_T *argvars, typva { if ((l = argvars[0].vval.v_list) == NULL) goto theend; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); li = l->lv_first; } else @@ -6279,7 +6279,7 @@ f_range(typval_T *argvars, typval_T *ret list_T *list = rettv->vval.v_list; // Create a non-materialized list. This is much more efficient and - // works with ":for". If used otherwise range_list_materialize() must + // works with ":for". If used otherwise CHECK_LIST_MATERIALIZE() must // be called. list->lv_first = &range_list_item; list->lv_u.nonmat.lv_start = start; @@ -6290,26 +6290,24 @@ f_range(typval_T *argvars, typval_T *ret } /* - * If "list" is a non-materialized list then materialize it now. + * Materialize "list". + * Do not call directly, use CHECK_LIST_MATERIALIZE() */ void range_list_materialize(list_T *list) { - if (list->lv_first == &range_list_item) - { - varnumber_T start = list->lv_u.nonmat.lv_start; - varnumber_T end = list->lv_u.nonmat.lv_end; - int stride = list->lv_u.nonmat.lv_stride; - varnumber_T i; - - list->lv_first = NULL; - list->lv_u.mat.lv_last = NULL; - list->lv_len = 0; - list->lv_u.mat.lv_idx_item = NULL; - for (i = start; stride > 0 ? i <= end : i >= end; i += stride) - if (list_append_number(list, (varnumber_T)i) == FAIL) - break; - } + varnumber_T start = list->lv_u.nonmat.lv_start; + varnumber_T end = list->lv_u.nonmat.lv_end; + int stride = list->lv_u.nonmat.lv_stride; + varnumber_T i; + + list->lv_first = NULL; + list->lv_u.mat.lv_last = NULL; + list->lv_len = 0; + list->lv_u.mat.lv_idx_item = NULL; + for (i = start; stride > 0 ? i <= end : i >= end; i += stride) + if (list_append_number(list, (varnumber_T)i) == FAIL) + break; } static void @@ -7327,7 +7325,7 @@ f_setreg(typval_T *argvars, typval_T *re if (ll != NULL) { - range_list_materialize(ll); + CHECK_LIST_MATERIALIZE(ll); FOR_ALL_LIST_ITEMS(ll, li) { strval = tv_get_string_buf_chk(&li->li_tv, buf); diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -863,7 +863,7 @@ ex_let_vars( return FAIL; } - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); item = l->lv_first; while (*arg != ']') { diff --git a/src/filepath.c b/src/filepath.c --- a/src/filepath.c +++ b/src/filepath.c @@ -1917,7 +1917,7 @@ f_writefile(typval_T *argvars, typval_T list = argvars[0].vval.v_list; if (list == NULL) return; - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); FOR_ALL_LIST_ITEMS(list, li) if (tv_get_string_chk(&li->li_tv) == NULL) return; diff --git a/src/highlight.c b/src/highlight.c --- a/src/highlight.c +++ b/src/highlight.c @@ -3743,7 +3743,7 @@ match_add( listitem_T *li; int i; - range_list_materialize(pos_list); + CHECK_LIST_MATERIALIZE(pos_list); for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; i++, li = li->li_next) { diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -785,7 +785,7 @@ VimToPython(typval_T *our_tv, int depth, return NULL; } - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); FOR_ALL_LIST_ITEMS(list, curr) { if (!(newObj = VimToPython(&curr->li_tv, depth + 1, lookup_dict))) @@ -2256,7 +2256,7 @@ ListNew(PyTypeObject *subtype, list_T *l return NULL; self->list = list; ++list->lv_refcount; - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); pyll_add((PyObject *)(self), &self->ref, &lastlist); @@ -2824,7 +2824,7 @@ ListIter(ListObject *self) return NULL; } - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); list_add_watch(l, &lii->lw); lii->lw.lw_item = l->lv_first; lii->list = l; @@ -3021,7 +3021,7 @@ FunctionConstructor(PyTypeObject *subtyp return NULL; } argslist = argstv.vval.v_list; - range_list_materialize(argslist); + CHECK_LIST_MATERIALIZE(argslist); argc = argslist->lv_len; if (argc != 0) diff --git a/src/insexpand.c b/src/insexpand.c --- a/src/insexpand.c +++ b/src/insexpand.c @@ -2334,7 +2334,7 @@ ins_compl_add_list(list_T *list) int dir = compl_direction; // Go through the List with matches and add each of them. - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); FOR_ALL_LIST_ITEMS(list, li) { if (ins_compl_add_tv(&li->li_tv, dir) == OK) @@ -2519,7 +2519,7 @@ get_complete_info(list_T *what_list, dic else { what_flag = 0; - range_list_materialize(what_list); + CHECK_LIST_MATERIALIZE(what_list); FOR_ALL_LIST_ITEMS(what_list, item) { char_u *what = tv_get_string(&item->li_tv); diff --git a/src/json.c b/src/json.c --- a/src/json.c +++ b/src/json.c @@ -265,7 +265,7 @@ json_encode_item(garray_T *gap, typval_T l->lv_copyID = copyID; ga_append(gap, '['); - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); for (li = l->lv_first; li != NULL && !got_int; ) { if (json_encode_item(gap, &li->li_tv, copyID, diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -378,8 +378,8 @@ list_equal( if (l1 == NULL || l2 == NULL) return FALSE; - range_list_materialize(l1); - range_list_materialize(l2); + CHECK_LIST_MATERIALIZE(l1); + CHECK_LIST_MATERIALIZE(l2); for (item1 = l1->lv_first, item2 = l2->lv_first; item1 != NULL && item2 != NULL; @@ -411,7 +411,7 @@ list_find(list_T *l, long n) if (n < 0 || n >= l->lv_len) return NULL; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); // When there is a cached index may start search from there. if (l->lv_u.mat.lv_idx_item != NULL) @@ -541,7 +541,7 @@ list_idx_of_item(list_T *l, listitem_T * if (l == NULL) return -1; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); idx = 0; for (li = l->lv_first; li != NULL && li != item; li = li->li_next) ++idx; @@ -556,7 +556,7 @@ list_idx_of_item(list_T *l, listitem_T * void list_append(list_T *l, listitem_T *item) { - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); if (l->lv_u.mat.lv_last == NULL) { // empty list @@ -706,7 +706,7 @@ list_insert_tv(list_T *l, typval_T *tv, void list_insert(list_T *l, listitem_T *ni, listitem_T *item) { - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); if (item == NULL) // Append new item at end of list. list_append(l, ni); @@ -741,8 +741,8 @@ list_extend(list_T *l1, list_T *l2, list listitem_T *item; int todo = l2->lv_len; - range_list_materialize(l1); - range_list_materialize(l2); + CHECK_LIST_MATERIALIZE(l1); + CHECK_LIST_MATERIALIZE(l2); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. @@ -801,7 +801,7 @@ list_copy(list_T *orig, int deep, int co orig->lv_copyID = copyID; orig->lv_copylist = copy; } - range_list_materialize(orig); + CHECK_LIST_MATERIALIZE(orig); for (item = orig->lv_first; item != NULL && !got_int; item = item->li_next) { @@ -842,7 +842,7 @@ vimlist_remove(list_T *l, listitem_T *it { listitem_T *ip; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); // notify watchers for (ip = item; ip != NULL; ip = ip->li_next) @@ -877,7 +877,7 @@ list2string(typval_T *tv, int copyID, in return NULL; ga_init2(&ga, (int)sizeof(char), 80); ga_append(&ga, '['); - range_list_materialize(tv->vval.v_list); + CHECK_LIST_MATERIALIZE(tv->vval.v_list); if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE, restore_copyID, copyID) == FAIL) { @@ -915,7 +915,7 @@ list_join_inner( char_u *s; // Stringify each item in the list. - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID, @@ -1116,7 +1116,7 @@ write_list(FILE *fd, list_T *list, int b int ret = OK; char_u *s; - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); FOR_ALL_LIST_ITEMS(list, li) { for (s = tv_get_string(&li->li_tv); *s != NUL; ++s) @@ -1203,7 +1203,7 @@ f_list2str(typval_T *argvars, typval_T * if (argvars[1].v_type != VAR_UNKNOWN) utf8 = (int)tv_get_number_chk(&argvars[1], NULL); - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); ga_init2(&ga, 1, 80); if (has_mbyte || utf8) { @@ -1496,7 +1496,7 @@ do_sort_uniq(typval_T *argvars, typval_T TRUE)) goto theend; rettv_list_set(rettv, l); - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); len = list_len(l); if (len <= 1) @@ -1873,7 +1873,7 @@ filter_map(typval_T *argvars, typval_T * // set_vim_var_nr() doesn't set the type set_vim_var_type(VV_KEY, VAR_NUMBER); - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); if (map && l->lv_lock == 0) l->lv_lock = VAR_LOCKED; for (li = l->lv_first; li != NULL; li = nli) @@ -2011,7 +2011,7 @@ f_count(typval_T *argvars, typval_T *ret if ((l = argvars[0].vval.v_list) != NULL) { - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); li = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { diff --git a/src/macros.h b/src/macros.h --- a/src/macros.h +++ b/src/macros.h @@ -373,3 +373,9 @@ # define ESTACK_CHECK_NOW # define CHECK_CURBUF #endif + +// Inline the condition for performance. +#define CHECK_LIST_MATERIALIZE(l) if ((l)->lv_first == &range_list_item) range_list_materialize(l) + +// Inlined version of ga_grow(). Especially useful if "n" is a constant. +#define GA_GROW(gap, n) (((gap)->ga_maxlen - (gap)->ga_len < n) ? ga_grow_inner((gap), (n)) : OK) diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -2054,30 +2054,35 @@ ga_init2(garray_T *gap, int itemsize, in int ga_grow(garray_T *gap, int n) { + if (gap->ga_maxlen - gap->ga_len < n) + return ga_grow_inner(gap, n); + return OK; +} + + int +ga_grow_inner(garray_T *gap, int n) +{ size_t old_len; size_t new_len; char_u *pp; - if (gap->ga_maxlen - gap->ga_len < n) - { - if (n < gap->ga_growsize) - n = gap->ga_growsize; - - // A linear growth is very inefficient when the array grows big. This - // is a compromise between allocating memory that won't be used and too - // many copy operations. A factor of 1.5 seems reasonable. - if (n < gap->ga_len / 2) - n = gap->ga_len / 2; - - new_len = gap->ga_itemsize * (gap->ga_len + n); - pp = vim_realloc(gap->ga_data, new_len); - if (pp == NULL) - return FAIL; - old_len = gap->ga_itemsize * gap->ga_maxlen; - vim_memset(pp + old_len, 0, new_len - old_len); - gap->ga_maxlen = gap->ga_len + n; - gap->ga_data = pp; - } + if (n < gap->ga_growsize) + n = gap->ga_growsize; + + // A linear growth is very inefficient when the array grows big. This + // is a compromise between allocating memory that won't be used and too + // many copy operations. A factor of 1.5 seems reasonable. + if (n < gap->ga_len / 2) + n = gap->ga_len / 2; + + new_len = gap->ga_itemsize * (gap->ga_len + n); + pp = vim_realloc(gap->ga_data, new_len); + if (pp == NULL) + return FAIL; + old_len = gap->ga_itemsize * gap->ga_maxlen; + vim_memset(pp + old_len, 0, new_len - old_len); + gap->ga_maxlen = gap->ga_len + n; + gap->ga_data = pp; return OK; } diff --git a/src/popupmenu.c b/src/popupmenu.c --- a/src/popupmenu.c +++ b/src/popupmenu.c @@ -1318,7 +1318,7 @@ ui_post_balloon(char_u *mesg, list_T *li balloon_array = ALLOC_CLEAR_MULT(pumitem_T, list->lv_len); if (balloon_array == NULL) return; - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx) { char_u *text = tv_get_string_chk(&li->li_tv); diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -98,7 +98,7 @@ set_padding_border(dict_T *dict, int *ar array[i] = 1; if (list != NULL) { - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len; ++i, li = li->li_next) { @@ -516,7 +516,7 @@ handle_moved_argument(win_T *wp, dictite int mincol; int maxcol; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); li = l->lv_first; if (l->lv_len == 3) { @@ -756,7 +756,7 @@ apply_general_options(win_T *wp, dict_T listitem_T *li; int i; - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len; ++i, li = li->li_next) { @@ -790,7 +790,7 @@ apply_general_options(win_T *wp, dict_T if (list != NULL) { - range_list_materialize(list); + CHECK_LIST_MATERIALIZE(list); for (i = 0, li = list->lv_first; i < 8 && i < list->lv_len; ++i, li = li->li_next) { @@ -845,7 +845,7 @@ apply_general_options(win_T *wp, dict_T break; } else - range_list_materialize(li->li_tv.vval.v_list); + CHECK_LIST_MATERIALIZE(li->li_tv.vval.v_list); } } if (ok) diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -59,6 +59,7 @@ void ga_clear_strings(garray_T *gap); void ga_init(garray_T *gap); void ga_init2(garray_T *gap, int itemsize, int growsize); int ga_grow(garray_T *gap, int n); +int ga_grow_inner(garray_T *gap, int n); char_u *ga_concat_strings(garray_T *gap, char *sep); void ga_add_string(garray_T *gap, char_u *p); void ga_concat(garray_T *gap, char_u *s); diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1718,7 +1718,7 @@ func_call( int argc = 0; int r = 0; - range_list_materialize(l); + CHECK_LIST_MATERIALIZE(l); FOR_ALL_LIST_ITEMS(l, item) { if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc))