mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
array.c: add integer-specialized fast path for Array#sort!
when all elements are integers and no block is given, use specialized heapify/insertion_sort that compare mrb_int values directly, bypassing sort_cmp entirely. this eliminates per-comparison overhead of GC arena save/restore, type checking, and array modification checks. the pre-scan to detect all-integer arrays is O(n), negligible compared to O(n log n) sort. non-integer and block sorts are unaffected. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+93
-1
@@ -2081,6 +2081,79 @@ mrb_ary_delete(mrb_state *mrb, mrb_value self)
|
||||
|
||||
#define SMALL_ARRAY_SORT_THRESHOLD 16
|
||||
|
||||
/* Check if all elements in the array are integers (fast path candidate) */
|
||||
static mrb_bool
|
||||
ary_all_fixnum_p(const mrb_value *a, mrb_int n)
|
||||
{
|
||||
for (mrb_int i = 0; i < n; i++) {
|
||||
if (!mrb_integer_p(a[i])) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Integer-specialized heapify: no sort_cmp overhead, direct comparison */
|
||||
static void
|
||||
heapify_fixnum(mrb_value *a, mrb_int index, mrb_int size)
|
||||
{
|
||||
mrb_int val = mrb_integer(a[index]);
|
||||
|
||||
while (1) {
|
||||
mrb_int child = 2 * index + 1;
|
||||
if (child >= size) break;
|
||||
if (child + 1 < size && mrb_integer(a[child + 1]) > mrb_integer(a[child])) {
|
||||
child++;
|
||||
}
|
||||
if (mrb_integer(a[child]) <= val) break;
|
||||
a[index] = a[child];
|
||||
index = child;
|
||||
}
|
||||
SET_FIXNUM_VALUE(a[index], val);
|
||||
}
|
||||
|
||||
/* Integer-specialized Floyd's bottom-up heap deletion */
|
||||
static void
|
||||
heap_delete_root_fixnum(mrb_value *a, mrb_int size)
|
||||
{
|
||||
mrb_int last = mrb_integer(a[0]);
|
||||
|
||||
mrb_int hole = 0;
|
||||
mrb_int child = 1;
|
||||
while (child + 1 < size) {
|
||||
if (mrb_integer(a[child + 1]) > mrb_integer(a[child])) {
|
||||
child++;
|
||||
}
|
||||
a[hole] = a[child];
|
||||
hole = child;
|
||||
child = 2 * hole + 1;
|
||||
}
|
||||
if (child < size) {
|
||||
a[hole] = a[child];
|
||||
hole = child;
|
||||
}
|
||||
|
||||
while (hole > 0) {
|
||||
mrb_int parent = (hole - 1) / 2;
|
||||
if (mrb_integer(a[parent]) >= last) break;
|
||||
a[hole] = a[parent];
|
||||
hole = parent;
|
||||
}
|
||||
SET_FIXNUM_VALUE(a[hole], last);
|
||||
}
|
||||
|
||||
/* Integer-specialized insertion sort */
|
||||
static void
|
||||
insertion_sort_fixnum(mrb_value *a, mrb_int size)
|
||||
{
|
||||
for (mrb_int i = 1; i < size; i++) {
|
||||
mrb_int key = mrb_integer(a[i]);
|
||||
mrb_int j = i - 1;
|
||||
while (j >= 0 && mrb_integer(a[j]) > key) {
|
||||
a[j + 1] = a[j];
|
||||
j--;
|
||||
}
|
||||
SET_FIXNUM_VALUE(a[j + 1], key);
|
||||
}
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
sort_cmp(mrb_state *mrb, mrb_value ary, mrb_value a_val, mrb_value b_val, mrb_value blk)
|
||||
@@ -2243,7 +2316,26 @@ mrb_ary_sort_bang(mrb_state *mrb, mrb_value ary)
|
||||
|
||||
mrb_value *a = RARRAY_PTR(ary);
|
||||
|
||||
/* Algorithm selection based on array size */
|
||||
/* Integer fast path: no block and all elements are integers */
|
||||
if (mrb_nil_p(blk) && ary_all_fixnum_p(a, n)) {
|
||||
if (n <= SMALL_ARRAY_SORT_THRESHOLD) {
|
||||
insertion_sort_fixnum(a, n);
|
||||
}
|
||||
else {
|
||||
for (mrb_int i = n / 2 - 1; i >= 0; i--) {
|
||||
heapify_fixnum(a, i, n);
|
||||
}
|
||||
for (mrb_int i = n - 1; i > 0; i--) {
|
||||
mrb_value tmp = a[0];
|
||||
a[0] = a[i];
|
||||
a[i] = tmp;
|
||||
heap_delete_root_fixnum(a, i);
|
||||
}
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
/* General path */
|
||||
if (n <= SMALL_ARRAY_SORT_THRESHOLD) {
|
||||
/* Use insertion sort for small arrays */
|
||||
insertion_sort(mrb, ary, a, n, blk);
|
||||
|
||||
Reference in New Issue
Block a user