aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias EngdegÄrd2024-03-21 19:35:15 +0100
committerMattias EngdegÄrd2024-03-29 11:39:38 +0100
commitdeae311281522864ebabaf56adafbe37032cc8a9 (patch)
tree9672a97c14084162887cd6cc54d3565c195ce9e3 /src
parentae5f2c02bd2fc269e2cc32c8039d95fbf4225e69 (diff)
downloademacs-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.c5
-rw-r--r--src/sort.c79
2 files changed, 40 insertions, 44 deletions
diff --git a/src/fns.c b/src/fns.c
index 7eacf99cbba..bf7c0920750 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -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
155typedef struct 155typedef 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. */ 198static bool
199order_pred_lisp (merge_state *ms, Lisp_Object a, Lisp_Object b)
200{
201 return !NILP (call2 (ms->predicate, a, b));
202}
197 203
198static inline bool 204static bool
199inorder (const Lisp_Object predicate, const Lisp_Object a, const Lisp_Object b) 205order_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. */
211static inline bool
212inorder (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
212binarysort (merge_state *ms, sortslice lo, const Lisp_Object *hi, 224binarysort (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
263count_run (merge_state *ms, Lisp_Object *lo, const Lisp_Object *hi, 273count_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
319gallop_left (merge_state *ms, const Lisp_Object key, Lisp_Object *a, 327gallop_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
403gallop_right (merge_state *ms, const Lisp_Object key, Lisp_Object *a, 409gallop_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
637merge_lo (merge_state *ms, sortslice ssa, ptrdiff_t na, 642merge_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
762merge_hi (merge_state *ms, sortslice ssa, ptrdiff_t na, 765merge_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
1078tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, 1079tim_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