mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
array.c: replace sort! method implementation
- use heap sort (O(1)) instead of merge sort (O(n)) for better space complexity. - method implemented in C for better performance As a result, simple sorting now consumes far less memory and is faster. Since it's implemented in C, fiber context switching is not allowed from comparison, but we consider the risk is minimal (no one switches context in the comparison, right?)
This commit is contained in:
@@ -198,78 +198,6 @@ class Array
|
||||
ret
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# array.sort! -> self
|
||||
# array.sort! {|a, b| ... } -> self
|
||||
#
|
||||
# Sort all elements and replace +self+ with these
|
||||
# elements.
|
||||
def sort!(&block)
|
||||
stack = [ [ 0, self.size - 1 ] ]
|
||||
until stack.empty?
|
||||
left, mid, right = stack.pop
|
||||
if right == nil
|
||||
right = mid
|
||||
# sort self[left..right]
|
||||
if left < right
|
||||
if left + 1 == right
|
||||
lval = self[left]
|
||||
rval = self[right]
|
||||
cmp = if block then block.call(lval,rval) else lval <=> rval end
|
||||
if cmp.nil?
|
||||
raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
|
||||
end
|
||||
if cmp > 0
|
||||
self[left] = rval
|
||||
self[right] = lval
|
||||
end
|
||||
else
|
||||
mid = ((left + right + 1) / 2).floor
|
||||
stack.push [ left, mid, right ]
|
||||
stack.push [ mid, right ]
|
||||
stack.push [ left, (mid - 1) ] if left < mid - 1
|
||||
end
|
||||
end
|
||||
else
|
||||
lary = self[left, mid - left]
|
||||
lsize = lary.size
|
||||
|
||||
# The entity sharing between lary and self may cause a large memory
|
||||
# copy operation in the merge loop below. This harmless operation
|
||||
# cancels the sharing and provides a huge performance gain.
|
||||
lary[0] = lary[0]
|
||||
|
||||
# merge
|
||||
lidx = 0
|
||||
ridx = mid
|
||||
(left..right).each { |i|
|
||||
if lidx >= lsize
|
||||
break
|
||||
elsif ridx > right
|
||||
self[i, lsize - lidx] = lary[lidx, lsize - lidx]
|
||||
break
|
||||
else
|
||||
lval = lary[lidx]
|
||||
rval = self[ridx]
|
||||
cmp = if block then block.call(lval,rval) else lval <=> rval end
|
||||
if cmp.nil?
|
||||
raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
|
||||
end
|
||||
if cmp <= 0
|
||||
self[i] = lval
|
||||
lidx += 1
|
||||
else
|
||||
self[i] = rval
|
||||
ridx += 1
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# array.sort -> new_array
|
||||
|
||||
+71
@@ -1409,6 +1409,76 @@ mrb_ary_delete(mrb_state *mrb, mrb_value self)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
sort_cmp(mrb_state *mrb, mrb_value *p, mrb_int a, mrb_int b, mrb_value blk)
|
||||
{
|
||||
mrb_int cmp;
|
||||
|
||||
if (mrb_nil_p(blk)) {
|
||||
cmp = mrb_cmp(mrb, p[a], p[b]);
|
||||
}
|
||||
else {
|
||||
mrb_value c = mrb_funcall_id(mrb, blk, MRB_SYM(call), 2, p[a], p[b]);
|
||||
if (mrb_nil_p(c) || !mrb_fixnum_p(c)) {
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %!v and %!v failed", p[a], p[b]);
|
||||
}
|
||||
cmp = mrb_fixnum(c);
|
||||
}
|
||||
return cmp > 0;
|
||||
}
|
||||
|
||||
static void
|
||||
heapify(mrb_state *mrb, mrb_value *a, mrb_int index, mrb_int size, mrb_value blk)
|
||||
{
|
||||
mrb_int max = index;
|
||||
mrb_int left_index = 2 * index + 1;
|
||||
mrb_int right_index = left_index + 1;
|
||||
if (left_index < size && sort_cmp(mrb, a, left_index, max, blk)) {
|
||||
max = left_index;
|
||||
}
|
||||
if (right_index < size && sort_cmp(mrb, a, right_index, max, blk)) {
|
||||
max = right_index;
|
||||
}
|
||||
if (max != index) {
|
||||
mrb_value tmp = a[max];
|
||||
a[max] = a[index];
|
||||
a[index] = tmp;
|
||||
heapify(mrb, a, max, size, blk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* array.sort! -> self
|
||||
* array.sort! {|a, b| ... } -> self
|
||||
*
|
||||
* Sort all elements and replace +self+ with these
|
||||
* elements.
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_ary_sort_bang(mrb_state *mrb, mrb_value ary)
|
||||
{
|
||||
mrb_value blk;
|
||||
|
||||
mrb_int n = RARRAY_LEN(ary);
|
||||
if (n < 2) return ary;
|
||||
|
||||
ary_modify(mrb, mrb_ary_ptr(ary));
|
||||
mrb_get_args(mrb, "&", &blk);
|
||||
|
||||
mrb_value *a = RARRAY_PTR(ary);
|
||||
for (mrb_int i = n / 2 - 1; i > -1; i--) {
|
||||
heapify(mrb, a, i, n, blk);
|
||||
}
|
||||
for (mrb_int i = n - 1; i > 0; i--) {
|
||||
mrb_value tmp = a[0];
|
||||
a[0] = a[i];
|
||||
a[i] = tmp;
|
||||
heapify(mrb, a, 0, i, blk);
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_array(mrb_state *mrb)
|
||||
{
|
||||
@@ -1446,6 +1516,7 @@ mrb_init_array(mrb_state *mrb)
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(unshift), mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(to_s), mrb_ary_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(inspect), mrb_ary_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method_id(mrb, a, MRB_SYM_B(sort), mrb_ary_sort_bang, MRB_ARGS_NONE());
|
||||
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(__ary_eq), mrb_ary_eq, MRB_ARGS_REQ(1));
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(__ary_cmp), mrb_ary_cmp, MRB_ARGS_REQ(1));
|
||||
|
||||
Reference in New Issue
Block a user