Git fork

Merge branch 'pc/range-diff-memory-limit'

"git range-diff" learned a way to limit the memory consumed by
O(N*N) cost matrix.

* pc/range-diff-memory-limit:
range-diff: add configurable memory limit for cost matrix

+44 -4
+1
builtin/log.c
··· 1404 struct range_diff_options range_diff_opts = { 1405 .creation_factor = rev->creation_factor, 1406 .dual_color = 1, 1407 .diffopt = &opts, 1408 .other_arg = &other_arg 1409 };
··· 1404 struct range_diff_options range_diff_opts = { 1405 .creation_factor = rev->creation_factor, 1406 .dual_color = 1, 1407 + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, 1408 .diffopt = &opts, 1409 .other_arg = &other_arg 1410 };
+21
builtin/range-diff.c
··· 6 #include "parse-options.h" 7 #include "range-diff.h" 8 #include "config.h" 9 10 11 static const char * const builtin_range_diff_usage[] = { ··· 15 NULL 16 }; 17 18 int cmd_range_diff(int argc, 19 const char **argv, 20 const char *prefix, ··· 25 struct strvec diff_merges_arg = STRVEC_INIT; 26 struct range_diff_options range_diff_opts = { 27 .creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT, 28 .diffopt = &diffopt, 29 .other_arg = &other_arg 30 }; ··· 40 PARSE_OPT_OPTARG), 41 OPT_PASSTHRU_ARGV(0, "diff-merges", &diff_merges_arg, 42 N_("style"), N_("passed to 'git log'"), 0), 43 OPT_PASSTHRU_ARGV(0, "remerge-diff", &diff_merges_arg, NULL, 44 N_("passed to 'git log'"), PARSE_OPT_NOARG), 45 OPT_BOOL(0, "left-only", &left_only,
··· 6 #include "parse-options.h" 7 #include "range-diff.h" 8 #include "config.h" 9 + #include "parse.h" 10 11 12 static const char * const builtin_range_diff_usage[] = { ··· 16 NULL 17 }; 18 19 + static int parse_max_memory(const struct option *opt, const char *arg, int unset) 20 + { 21 + size_t *max_memory = opt->value; 22 + uintmax_t val; 23 + 24 + if (unset) 25 + return 0; 26 + 27 + if (!git_parse_unsigned(arg, &val, SIZE_MAX)) 28 + return error(_("invalid max-memory value: %s"), arg); 29 + 30 + *max_memory = (size_t)val; 31 + return 0; 32 + } 33 + 34 int cmd_range_diff(int argc, 35 const char **argv, 36 const char *prefix, ··· 41 struct strvec diff_merges_arg = STRVEC_INIT; 42 struct range_diff_options range_diff_opts = { 43 .creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT, 44 + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, 45 .diffopt = &diffopt, 46 .other_arg = &other_arg 47 }; ··· 57 PARSE_OPT_OPTARG), 58 OPT_PASSTHRU_ARGV(0, "diff-merges", &diff_merges_arg, 59 N_("style"), N_("passed to 'git log'"), 0), 60 + OPT_CALLBACK(0, "max-memory", &range_diff_opts.max_memory, 61 + N_("size"), 62 + N_("maximum memory for cost matrix (default 4G)"), 63 + parse_max_memory), 64 OPT_PASSTHRU_ARGV(0, "remerge-diff", &diff_merges_arg, NULL, 65 N_("passed to 'git log'"), PARSE_OPT_NOARG), 66 OPT_BOOL(0, "left-only", &left_only,
+1
log-tree.c
··· 717 struct range_diff_options range_diff_opts = { 718 .creation_factor = opt->creation_factor, 719 .dual_color = 1, 720 .diffopt = &opts 721 }; 722
··· 717 struct range_diff_options range_diff_opts = { 718 .creation_factor = opt->creation_factor, 719 .dual_color = 1, 720 + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, 721 .diffopt = &opts 722 }; 723
+16 -4
range-diff.c
··· 325 } 326 327 static void get_correspondences(struct string_list *a, struct string_list *b, 328 - int creation_factor) 329 { 330 int n = a->nr + b->nr; 331 int *cost, c, *a2b, *b2a; 332 int i, j; 333 - 334 - ALLOC_ARRAY(cost, st_mult(n, n)); 335 ALLOC_ARRAY(a2b, n); 336 ALLOC_ARRAY(b2a, n); 337 ··· 591 if (!res) { 592 find_exact_matches(&branch1, &branch2); 593 get_correspondences(&branch1, &branch2, 594 - range_diff_opts->creation_factor); 595 output(&branch1, &branch2, range_diff_opts); 596 } 597
··· 325 } 326 327 static void get_correspondences(struct string_list *a, struct string_list *b, 328 + int creation_factor, size_t max_memory) 329 { 330 int n = a->nr + b->nr; 331 int *cost, c, *a2b, *b2a; 332 int i, j; 333 + size_t cost_size = st_mult(n, n); 334 + size_t cost_bytes = st_mult(sizeof(int), cost_size); 335 + if (cost_bytes >= max_memory) { 336 + struct strbuf cost_str = STRBUF_INIT; 337 + struct strbuf max_str = STRBUF_INIT; 338 + strbuf_humanise_bytes(&cost_str, cost_bytes); 339 + strbuf_humanise_bytes(&max_str, max_memory); 340 + die(_("range-diff: unable to compute the range-diff, since it " 341 + "exceeds the maximum memory for the cost matrix: %s " 342 + "(%"PRIuMAX" bytes) needed, limited to %s (%"PRIuMAX" bytes)"), 343 + cost_str.buf, (uintmax_t)cost_bytes, max_str.buf, (uintmax_t)max_memory); 344 + } 345 + ALLOC_ARRAY(cost, cost_size); 346 ALLOC_ARRAY(a2b, n); 347 ALLOC_ARRAY(b2a, n); 348 ··· 602 if (!res) { 603 find_exact_matches(&branch1, &branch2); 604 get_correspondences(&branch1, &branch2, 605 + range_diff_opts->creation_factor, 606 + range_diff_opts->max_memory); 607 output(&branch1, &branch2, range_diff_opts); 608 } 609
+5
range-diff.h
··· 5 #include "strvec.h" 6 7 #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60 8 9 /* 10 * A much higher value than the default, when we KNOW we are comparing ··· 17 unsigned dual_color:1; 18 unsigned left_only:1, right_only:1; 19 unsigned include_merges:1; 20 const struct diff_options *diffopt; /* may be NULL */ 21 const struct strvec *other_arg; /* may be NULL */ 22 };
··· 5 #include "strvec.h" 6 7 #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60 8 + #define RANGE_DIFF_MAX_MEMORY_DEFAULT \ 9 + (sizeof(void*) >= 8 ? \ 10 + ((size_t)(1024L * 1024L) * (size_t)(4L * 1024L)) : /* 4GB on 64-bit */ \ 11 + ((size_t)(1024L * 1024L) * (size_t)(2L * 1024L))) /* 2GB on 32-bit */ 12 13 /* 14 * A much higher value than the default, when we KNOW we are comparing ··· 21 unsigned dual_color:1; 22 unsigned left_only:1, right_only:1; 23 unsigned include_merges:1; 24 + size_t max_memory; 25 const struct diff_options *diffopt; /* may be NULL */ 26 const struct strvec *other_arg; /* may be NULL */ 27 };