To: vim_dev@googlegroups.com Subject: Patch 9.0.0615 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0615 Problem: Using reduce() on a list from range() is a bit slow. Solution: Avoid materializing the list. Files: src/list.c, src/testdir/test_listdict.vim *** ../vim-9.0.0614/src/list.c 2022-09-22 17:06:56.295037465 +0100 --- src/list.c 2022-09-28 12:56:19.060434241 +0100 *************** *** 2377,2385 **** rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } ! if (l == NULL ! || (filtermap == FILTERMAP_FILTER ! && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) return; prev_lock = l->lv_lock; --- 2377,2384 ---- rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } ! if (l == NULL || (filtermap == FILTERMAP_FILTER ! && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) return; prev_lock = l->lv_lock; *************** *** 3011,3038 **** { list_T *l = argvars[0].vval.v_list; listitem_T *li = NULL; typval_T initial; typval_T argv[3]; int r; int called_emsg_start = called_emsg; int prev_locked; ! if (l != NULL) ! CHECK_LIST_MATERIALIZE(l); if (argvars[2].v_type == VAR_UNKNOWN) { ! if (l == NULL || l->lv_first == NULL) { semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List"); return; } ! initial = l->lv_first->li_tv; ! li = l->lv_first->li_next; } else { initial = argvars[2]; ! if (l != NULL) li = l->lv_first; } copy_tv(&initial, rettv); --- 3010,3053 ---- { list_T *l = argvars[0].vval.v_list; listitem_T *li = NULL; + int range_list; + int range_idx = 0; + varnumber_T range_val = 0; typval_T initial; typval_T argv[3]; int r; int called_emsg_start = called_emsg; int prev_locked; ! // Using reduce on a range() uses "range_idx" and "range_val". ! range_list = l != NULL && l->lv_first == &range_list_item; ! if (range_list) ! range_val = l->lv_u.nonmat.lv_start; ! if (argvars[2].v_type == VAR_UNKNOWN) { ! if (l == NULL || l->lv_len == 0) { semsg(_(e_reduce_of_an_empty_str_with_no_initial_value), "List"); return; } ! if (range_list) ! { ! initial.v_type = VAR_NUMBER; ! initial.vval.v_number = range_val; ! range_val += l->lv_u.nonmat.lv_stride; ! range_idx = 1; ! } ! else ! { ! initial = l->lv_first->li_tv; ! li = l->lv_first->li_next; ! } } else { initial = argvars[2]; ! if (l != NULL && !range_list) li = l->lv_first; } copy_tv(&initial, rettv); *************** *** 3041,3060 **** return; prev_locked = l->lv_lock; - l->lv_lock = VAR_FIXED; // disallow the list changing here ! for ( ; li != NULL; li = li->li_next) { argv[0] = *rettv; - argv[1] = li->li_tv; rettv->v_type = VAR_UNKNOWN; r = eval_expr_typval(expr, argv, 2, rettv); clear_tv(&argv[0]); if (r == FAIL || called_emsg != called_emsg_start) break; } l->lv_lock = prev_locked; } --- 3056,3091 ---- return; prev_locked = l->lv_lock; l->lv_lock = VAR_FIXED; // disallow the list changing here ! ! while (range_list ? range_idx < l->lv_len : li != NULL) { argv[0] = *rettv; rettv->v_type = VAR_UNKNOWN; + if (range_list) + { + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = range_val; + } + else + argv[1] = li->li_tv; + r = eval_expr_typval(expr, argv, 2, rettv); clear_tv(&argv[0]); if (r == FAIL || called_emsg != called_emsg_start) break; + + if (range_list) + { + range_val += l->lv_u.nonmat.lv_stride; + ++range_idx; + } + else + li = li->li_next; } + l->lv_lock = prev_locked; } *** ../vim-9.0.0614/src/testdir/test_listdict.vim 2022-09-22 17:06:56.299037474 +0100 --- src/testdir/test_listdict.vim 2022-09-28 12:58:36.771957027 +0100 *************** *** 1015,1020 **** --- 1015,1026 ---- call assert_equal('x y z', reduce(['x', 'y', 'z'], LSTART acc, val LMIDDLE acc .. ' ' .. val LEND)) call assert_equal(120, range(1, 5)->reduce(LSTART acc, val LMIDDLE acc * val LEND)) + call assert_equal(0, range(1)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal(1, range(2)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal(3, range(3)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal(6, range(4)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal(10, range(5)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal(1, reduce(0z, LSTART acc, val LMIDDLE acc + val LEND, 1)) call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE acc + val LEND, 1)) call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce(LSTART acc, val LMIDDLE 2 * acc + val LEND, 1)) *************** *** 1038,1043 **** --- 1044,1050 ---- vim9 assert_equal({'x': 1, 'y': 1, 'z': 1 }, ['x', 'y', 'z']->reduce((acc, val) => extend(acc, {[val]: 1 }), {})) call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') + call assert_fails("call reduce(range(0), { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') call assert_fails("call reduce(test_null_blob(), { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') call assert_fails("call reduce('', { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value') *** ../vim-9.0.0614/src/version.c 2022-09-28 11:48:26.677156955 +0100 --- src/version.c 2022-09-28 13:20:48.906575541 +0100 *************** *** 701,702 **** --- 701,704 ---- { /* Add new patch number below this line */ + /**/ + 615, /**/ -- hundred-and-one symptoms of being an internet addict: 208. Your goals for the future are obtaining a second Gbit connection and upgrade your NAS to all SSD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///