diff options
| author | Mattias EngdegÄrd | 2024-03-21 19:35:15 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-03-29 11:39:38 +0100 |
| commit | deae311281522864ebabaf56adafbe37032cc8a9 (patch) | |
| tree | 9672a97c14084162887cd6cc54d3565c195ce9e3 /src | |
| parent | ae5f2c02bd2fc269e2cc32c8039d95fbf4225e69 (diff) | |
| download | emacs-deae311281522864ebabaf56adafbe37032cc8a9.tar.gz emacs-deae311281522864ebabaf56adafbe37032cc8a9.zip | |
Speed up `sort` by special-casing the `value<` ordering
This gives a 1.5x-2x speed-up when using the default :lessp value,
by eliminating the Ffuncall overhead.
* src/sort.c (order_pred_lisp, order_pred_valuelt): New.
(merge_state, inorder, binarysort, count_run, gallop_left, gallop_right)
(merge_init, merge_lo, merge_hi, tim_sort):
* src/fns.c (Fsort):
When using value<, call it directly.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 5 | ||||
| -rw-r--r-- | src/sort.c | 79 |
2 files changed, 40 insertions, 44 deletions
| @@ -2455,11 +2455,6 @@ usage: (sort SEQ &key KEY LESSP REVERSE IN-PLACE) */) | |||
| 2455 | signal_error ("Invalid keyword argument", args[i]); | 2455 | signal_error ("Invalid keyword argument", args[i]); |
| 2456 | } | 2456 | } |
| 2457 | 2457 | ||
| 2458 | if (NILP (lessp)) | ||
| 2459 | /* FIXME: normalise it as Qnil instead, and special-case it in tim_sort? | ||
| 2460 | That would remove the funcall overhead for the common case. */ | ||
| 2461 | lessp = Qvaluelt; | ||
| 2462 | |||
| 2463 | /* FIXME: for lists it may be slightly faster to make the copy after | 2458 | /* FIXME: for lists it may be slightly faster to make the copy after |
| 2464 | sorting? Measure. */ | 2459 | sorting? Measure. */ |
| 2465 | if (!inplace) | 2460 | if (!inplace) |
diff --git a/src/sort.c b/src/sort.c index a0f127c35b3..527d5550342 100644 --- a/src/sort.c +++ b/src/sort.c | |||
| @@ -152,7 +152,7 @@ struct reloc | |||
| 152 | }; | 152 | }; |
| 153 | 153 | ||
| 154 | 154 | ||
| 155 | typedef struct | 155 | typedef struct merge_state |
| 156 | { | 156 | { |
| 157 | Lisp_Object *basekeys; | 157 | Lisp_Object *basekeys; |
| 158 | Lisp_Object *allocated_keys; /* heap-alloc'ed key array or NULL */ | 158 | Lisp_Object *allocated_keys; /* heap-alloc'ed key array or NULL */ |
| @@ -187,20 +187,32 @@ typedef struct | |||
| 187 | 187 | ||
| 188 | struct reloc reloc; | 188 | struct reloc reloc; |
| 189 | 189 | ||
| 190 | /* PREDICATE is the lisp comparison predicate for the sort. */ | 190 | /* The C ordering (less-than) predicate. */ |
| 191 | bool (*pred_fun) (struct merge_state *ms, Lisp_Object a, Lisp_Object b); | ||
| 191 | 192 | ||
| 193 | /* The Lisp ordering predicate; Qnil means value<. */ | ||
| 192 | Lisp_Object predicate; | 194 | Lisp_Object predicate; |
| 193 | } merge_state; | 195 | } merge_state; |
| 194 | 196 | ||
| 195 | 197 | ||
| 196 | /* Return true iff (PREDICATE A B) is non-nil. */ | 198 | static bool |
| 199 | order_pred_lisp (merge_state *ms, Lisp_Object a, Lisp_Object b) | ||
| 200 | { | ||
| 201 | return !NILP (call2 (ms->predicate, a, b)); | ||
| 202 | } | ||
| 197 | 203 | ||
| 198 | static inline bool | 204 | static bool |
| 199 | inorder (const Lisp_Object predicate, const Lisp_Object a, const Lisp_Object b) | 205 | order_pred_valuelt (merge_state *ms, Lisp_Object a, Lisp_Object b) |
| 200 | { | 206 | { |
| 201 | return !NILP (call2 (predicate, a, b)); | 207 | return !NILP (Fvaluelt (a, b)); |
| 202 | } | 208 | } |
| 203 | 209 | ||
| 210 | /* Return true iff A < B according to the order predicate. */ | ||
| 211 | static inline bool | ||
| 212 | inorder (merge_state *ms, Lisp_Object a, Lisp_Object b) | ||
| 213 | { | ||
| 214 | return ms->pred_fun (ms, a, b); | ||
| 215 | } | ||
| 204 | 216 | ||
| 205 | /* Sort the list starting at LO and ending at HI using a stable binary | 217 | /* Sort the list starting at LO and ending at HI using a stable binary |
| 206 | insertion sort algorithm. On entry the sublist [LO, START) (with | 218 | insertion sort algorithm. On entry the sublist [LO, START) (with |
| @@ -212,8 +224,6 @@ static void | |||
| 212 | binarysort (merge_state *ms, sortslice lo, const Lisp_Object *hi, | 224 | binarysort (merge_state *ms, sortslice lo, const Lisp_Object *hi, |
| 213 | Lisp_Object *start) | 225 | Lisp_Object *start) |
| 214 | { | 226 | { |
| 215 | Lisp_Object pred = ms->predicate; | ||
| 216 | |||
| 217 | eassume (lo.keys <= start && start <= hi); | 227 | eassume (lo.keys <= start && start <= hi); |
| 218 | if (lo.keys == start) | 228 | if (lo.keys == start) |
| 219 | ++start; | 229 | ++start; |
| @@ -226,7 +236,7 @@ binarysort (merge_state *ms, sortslice lo, const Lisp_Object *hi, | |||
| 226 | eassume (l < r); | 236 | eassume (l < r); |
| 227 | do { | 237 | do { |
| 228 | Lisp_Object *p = l + ((r - l) >> 1); | 238 | Lisp_Object *p = l + ((r - l) >> 1); |
| 229 | if (inorder (pred, pivot, *p)) | 239 | if (inorder (ms, pivot, *p)) |
| 230 | r = p; | 240 | r = p; |
| 231 | else | 241 | else |
| 232 | l = p + 1; | 242 | l = p + 1; |
| @@ -263,8 +273,6 @@ static ptrdiff_t | |||
| 263 | count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, | 273 | count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, |
| 264 | bool *descending) | 274 | bool *descending) |
| 265 | { | 275 | { |
| 266 | Lisp_Object pred = ms->predicate; | ||
| 267 | |||
| 268 | eassume (lo < hi); | 276 | eassume (lo < hi); |
| 269 | *descending = 0; | 277 | *descending = 0; |
| 270 | ++lo; | 278 | ++lo; |
| @@ -273,12 +281,12 @@ count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, | |||
| 273 | return n; | 281 | return n; |
| 274 | 282 | ||
| 275 | n = 2; | 283 | n = 2; |
| 276 | if (inorder (pred, lo[0], lo[-1])) | 284 | if (inorder (ms, lo[0], lo[-1])) |
| 277 | { | 285 | { |
| 278 | *descending = 1; | 286 | *descending = 1; |
| 279 | for (lo = lo + 1; lo < hi; ++lo, ++n) | 287 | for (lo = lo + 1; lo < hi; ++lo, ++n) |
| 280 | { | 288 | { |
| 281 | if (!inorder (pred, lo[0], lo[-1])) | 289 | if (!inorder (ms, lo[0], lo[-1])) |
| 282 | break; | 290 | break; |
| 283 | } | 291 | } |
| 284 | } | 292 | } |
| @@ -286,7 +294,7 @@ count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, | |||
| 286 | { | 294 | { |
| 287 | for (lo = lo + 1; lo < hi; ++lo, ++n) | 295 | for (lo = lo + 1; lo < hi; ++lo, ++n) |
| 288 | { | 296 | { |
| 289 | if (inorder (pred, lo[0], lo[-1])) | 297 | if (inorder (ms, lo[0], lo[-1])) |
| 290 | break; | 298 | break; |
| 291 | } | 299 | } |
| 292 | } | 300 | } |
| @@ -319,21 +327,19 @@ static ptrdiff_t | |||
| 319 | gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, | 327 | gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, |
| 320 | const ptrdiff_t n, const ptrdiff_t hint) | 328 | const ptrdiff_t n, const ptrdiff_t hint) |
| 321 | { | 329 | { |
| 322 | Lisp_Object pred = ms->predicate; | ||
| 323 | |||
| 324 | eassume (a && n > 0 && hint >= 0 && hint < n); | 330 | eassume (a && n > 0 && hint >= 0 && hint < n); |
| 325 | 331 | ||
| 326 | a += hint; | 332 | a += hint; |
| 327 | ptrdiff_t lastofs = 0; | 333 | ptrdiff_t lastofs = 0; |
| 328 | ptrdiff_t ofs = 1; | 334 | ptrdiff_t ofs = 1; |
| 329 | if (inorder (pred, *a, key)) | 335 | if (inorder (ms, *a, key)) |
| 330 | { | 336 | { |
| 331 | /* When a[hint] < key, gallop right until | 337 | /* When a[hint] < key, gallop right until |
| 332 | a[hint + lastofs] < key <= a[hint + ofs]. */ | 338 | a[hint + lastofs] < key <= a[hint + ofs]. */ |
| 333 | const ptrdiff_t maxofs = n - hint; /* This is one after the end of a. */ | 339 | const ptrdiff_t maxofs = n - hint; /* This is one after the end of a. */ |
| 334 | while (ofs < maxofs) | 340 | while (ofs < maxofs) |
| 335 | { | 341 | { |
| 336 | if (inorder (pred, a[ofs], key)) | 342 | if (inorder (ms, a[ofs], key)) |
| 337 | { | 343 | { |
| 338 | lastofs = ofs; | 344 | lastofs = ofs; |
| 339 | eassume (ofs <= (PTRDIFF_MAX - 1) / 2); | 345 | eassume (ofs <= (PTRDIFF_MAX - 1) / 2); |
| @@ -355,7 +361,7 @@ gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, | |||
| 355 | const ptrdiff_t maxofs = hint + 1; /* Here &a[0] is lowest. */ | 361 | const ptrdiff_t maxofs = hint + 1; /* Here &a[0] is lowest. */ |
| 356 | while (ofs < maxofs) | 362 | while (ofs < maxofs) |
| 357 | { | 363 | { |
| 358 | if (inorder (pred, a[-ofs], key)) | 364 | if (inorder (ms, a[-ofs], key)) |
| 359 | break; | 365 | break; |
| 360 | /* Here key <= a[hint - ofs]. */ | 366 | /* Here key <= a[hint - ofs]. */ |
| 361 | lastofs = ofs; | 367 | lastofs = ofs; |
| @@ -380,7 +386,7 @@ gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, | |||
| 380 | { | 386 | { |
| 381 | ptrdiff_t m = lastofs + ((ofs - lastofs) >> 1); | 387 | ptrdiff_t m = lastofs + ((ofs - lastofs) >> 1); |
| 382 | 388 | ||
| 383 | if (inorder (pred, a[m], key)) | 389 | if (inorder (ms, a[m], key)) |
| 384 | lastofs = m + 1; /* Here a[m] < key. */ | 390 | lastofs = m + 1; /* Here a[m] < key. */ |
| 385 | else | 391 | else |
| 386 | ofs = m; /* Here key <= a[m]. */ | 392 | ofs = m; /* Here key <= a[m]. */ |
| @@ -403,21 +409,19 @@ static ptrdiff_t | |||
| 403 | gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, | 409 | gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, |
| 404 | const ptrdiff_t n, const ptrdiff_t hint) | 410 | const ptrdiff_t n, const ptrdiff_t hint) |
| 405 | { | 411 | { |
| 406 | Lisp_Object pred = ms->predicate; | ||
| 407 | |||
| 408 | eassume (a && n > 0 && hint >= 0 && hint < n); | 412 | eassume (a && n > 0 && hint >= 0 && hint < n); |
| 409 | 413 | ||
| 410 | a += hint; | 414 | a += hint; |
| 411 | ptrdiff_t lastofs = 0; | 415 | ptrdiff_t lastofs = 0; |
| 412 | ptrdiff_t ofs = 1; | 416 | ptrdiff_t ofs = 1; |
| 413 | if (inorder (pred, key, *a)) | 417 | if (inorder (ms, key, *a)) |
| 414 | { | 418 | { |
| 415 | /* When key < a[hint], gallop left until | 419 | /* When key < a[hint], gallop left until |
| 416 | a[hint - ofs] <= key < a[hint - lastofs]. */ | 420 | a[hint - ofs] <= key < a[hint - lastofs]. */ |
| 417 | const ptrdiff_t maxofs = hint + 1; /* Here &a[0] is lowest. */ | 421 | const ptrdiff_t maxofs = hint + 1; /* Here &a[0] is lowest. */ |
| 418 | while (ofs < maxofs) | 422 | while (ofs < maxofs) |
| 419 | { | 423 | { |
| 420 | if (inorder (pred, key, a[-ofs])) | 424 | if (inorder (ms, key, a[-ofs])) |
| 421 | { | 425 | { |
| 422 | lastofs = ofs; | 426 | lastofs = ofs; |
| 423 | eassume (ofs <= (PTRDIFF_MAX - 1) / 2); | 427 | eassume (ofs <= (PTRDIFF_MAX - 1) / 2); |
| @@ -440,7 +444,7 @@ gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, | |||
| 440 | const ptrdiff_t maxofs = n - hint; /* Here &a[n-1] is highest. */ | 444 | const ptrdiff_t maxofs = n - hint; /* Here &a[n-1] is highest. */ |
| 441 | while (ofs < maxofs) | 445 | while (ofs < maxofs) |
| 442 | { | 446 | { |
| 443 | if (inorder (pred, key, a[ofs])) | 447 | if (inorder (ms, key, a[ofs])) |
| 444 | break; | 448 | break; |
| 445 | /* Here a[hint + ofs] <= key. */ | 449 | /* Here a[hint + ofs] <= key. */ |
| 446 | lastofs = ofs; | 450 | lastofs = ofs; |
| @@ -464,7 +468,7 @@ gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, | |||
| 464 | { | 468 | { |
| 465 | ptrdiff_t m = lastofs + ((ofs - lastofs) >> 1); | 469 | ptrdiff_t m = lastofs + ((ofs - lastofs) >> 1); |
| 466 | 470 | ||
| 467 | if (inorder (pred, key, a[m])) | 471 | if (inorder (ms, key, a[m])) |
| 468 | ofs = m; /* Here key < a[m]. */ | 472 | ofs = m; /* Here key < a[m]. */ |
| 469 | else | 473 | else |
| 470 | lastofs = m + 1; /* Here a[m] <= key. */ | 474 | lastofs = m + 1; /* Here a[m] <= key. */ |
| @@ -509,6 +513,7 @@ merge_init (merge_state *ms, const ptrdiff_t list_size, | |||
| 509 | ms->listlen = list_size; | 513 | ms->listlen = list_size; |
| 510 | ms->basekeys = lo->keys; | 514 | ms->basekeys = lo->keys; |
| 511 | ms->allocated_keys = allocated_keys; | 515 | ms->allocated_keys = allocated_keys; |
| 516 | ms->pred_fun = NILP (predicate) ? order_pred_valuelt : order_pred_lisp; | ||
| 512 | ms->predicate = predicate; | 517 | ms->predicate = predicate; |
| 513 | ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; | 518 | ms->reloc = (struct reloc){NULL, NULL, NULL, 0}; |
| 514 | ms->count = make_invalid_specpdl_ref (); | 519 | ms->count = make_invalid_specpdl_ref (); |
| @@ -637,8 +642,6 @@ static void | |||
| 637 | merge_lo (merge_state *ms, sortslice ssa, ptrdiff_t na, | 642 | merge_lo (merge_state *ms, sortslice ssa, ptrdiff_t na, |
| 638 | sortslice ssb, ptrdiff_t nb) | 643 | sortslice ssb, ptrdiff_t nb) |
| 639 | { | 644 | { |
| 640 | Lisp_Object pred = ms->predicate; | ||
| 641 | |||
| 642 | eassume (ms && ssa.keys && ssb.keys && na > 0 && nb > 0); | 645 | eassume (ms && ssa.keys && ssb.keys && na > 0 && nb > 0); |
| 643 | eassume (ssa.keys + na == ssb.keys); | 646 | eassume (ssa.keys + na == ssb.keys); |
| 644 | needmem (ms, na); | 647 | needmem (ms, na); |
| @@ -665,7 +668,7 @@ merge_lo (merge_state *ms, sortslice ssa, ptrdiff_t na, | |||
| 665 | for (;;) | 668 | for (;;) |
| 666 | { | 669 | { |
| 667 | eassume (na > 1 && nb > 0); | 670 | eassume (na > 1 && nb > 0); |
| 668 | if (inorder (pred, ssb.keys[0], ssa.keys[0])) | 671 | if (inorder (ms, ssb.keys[0], ssa.keys[0])) |
| 669 | { | 672 | { |
| 670 | sortslice_copy_incr (&dest, &ssb); | 673 | sortslice_copy_incr (&dest, &ssb); |
| 671 | ++bcount; | 674 | ++bcount; |
| @@ -762,8 +765,6 @@ static void | |||
| 762 | merge_hi (merge_state *ms, sortslice ssa, ptrdiff_t na, | 765 | merge_hi (merge_state *ms, sortslice ssa, ptrdiff_t na, |
| 763 | sortslice ssb, ptrdiff_t nb) | 766 | sortslice ssb, ptrdiff_t nb) |
| 764 | { | 767 | { |
| 765 | Lisp_Object pred = ms->predicate; | ||
| 766 | |||
| 767 | eassume (ms && ssa.keys && ssb.keys && na > 0 && nb > 0); | 768 | eassume (ms && ssa.keys && ssb.keys && na > 0 && nb > 0); |
| 768 | eassume (ssa.keys + na == ssb.keys); | 769 | eassume (ssa.keys + na == ssb.keys); |
| 769 | needmem (ms, nb); | 770 | needmem (ms, nb); |
| @@ -793,7 +794,7 @@ merge_hi (merge_state *ms, sortslice ssa, ptrdiff_t na, | |||
| 793 | 794 | ||
| 794 | for (;;) { | 795 | for (;;) { |
| 795 | eassume (na > 0 && nb > 1); | 796 | eassume (na > 0 && nb > 1); |
| 796 | if (inorder (pred, ssb.keys[0], ssa.keys[0])) | 797 | if (inorder (ms, ssb.keys[0], ssa.keys[0])) |
| 797 | { | 798 | { |
| 798 | sortslice_copy_decr (&dest, &ssa); | 799 | sortslice_copy_decr (&dest, &ssa); |
| 799 | ++acount; | 800 | ++acount; |
| @@ -1078,19 +1079,19 @@ void | |||
| 1078 | tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, | 1079 | tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, |
| 1079 | Lisp_Object *seq, const ptrdiff_t length, bool reverse) | 1080 | Lisp_Object *seq, const ptrdiff_t length, bool reverse) |
| 1080 | { | 1081 | { |
| 1081 | /* FIXME: optimise for the predicate being value<; at the very | 1082 | /* FIXME: hoist this to the caller? */ |
| 1082 | least we'd go without the Lisp funcall overhead. */ | 1083 | if (EQ (predicate, Qvaluelt)) |
| 1083 | predicate = resolve_fun (predicate); | 1084 | predicate = Qnil; |
| 1085 | if (!NILP (predicate)) | ||
| 1086 | predicate = resolve_fun (predicate); | ||
| 1087 | if (EQ (keyfunc, Qidentity)) | ||
| 1088 | keyfunc = Qnil; | ||
| 1084 | 1089 | ||
| 1085 | sortslice lo; | 1090 | sortslice lo; |
| 1086 | Lisp_Object *keys; | 1091 | Lisp_Object *keys; |
| 1087 | Lisp_Object *allocated_keys = NULL; | 1092 | Lisp_Object *allocated_keys = NULL; |
| 1088 | merge_state ms; | 1093 | merge_state ms; |
| 1089 | 1094 | ||
| 1090 | /* FIXME: hoist this to the caller? */ | ||
| 1091 | if (EQ (keyfunc, Qidentity)) | ||
| 1092 | keyfunc = Qnil; | ||
| 1093 | |||
| 1094 | if (reverse) | 1095 | if (reverse) |
| 1095 | reverse_slice (seq, seq + length); /* preserve stability */ | 1096 | reverse_slice (seq, seq + length); /* preserve stability */ |
| 1096 | 1097 | ||