summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <[email protected]>2024-07-03 09:45:29 -0700
committerAaron Patterson <[email protected]>2024-07-03 11:32:40 -0700
commit4c9134d2b2ab3830dd4cca3b9595c97fc7361392 (patch)
tree80b49421107ee8df9031056f61e9e37c511168d0
parent7fe5f0a1d0e628e9e330169a5c2dedae0d40dedd (diff)
Move Array#select to Ruby
This speeds up the mail benchmark by about 7% on the interpreter: ``` before: ruby 3.4.0dev (2024-07-03T17:01:41Z master f4b313f733) [arm64-darwin23] after: ruby 3.4.0dev (2024-07-03T17:45:50Z ruby-select de282cacd5) [arm64-darwin23] ----- ----------- ---------- ---------- ---------- ------------- ------------ bench before (ms) stddev (%) after (ms) stddev (%) after 1st itr before/after mail 72.9 0.8 68.2 1.0 1.02 1.07 ----- ----------- ---------- ---------- ---------- ------------- ------------ Legend: - after 1st itr: ratio of before/after time for the first benchmarking iteration. - before/after: ratio of before/after time. Higher is better for after. Above 1 represents a speedup. ``` YJIT is about 13% faster: ``` before: ruby 3.4.0dev (2024-07-03T17:01:41Z master f4b313f733) +YJIT [arm64-darwin23] after: ruby 3.4.0dev (2024-07-03T17:45:50Z ruby-select de282cacd5) +YJIT [arm64-darwin23] ----- ----------- ---------- ---------- ---------- ------------- ------------ bench before (ms) stddev (%) after (ms) stddev (%) after 1st itr before/after mail 51.0 0.8 45.2 0.6 1.00 1.13 ----- ----------- ---------- ---------- ---------- ------------- ------------ Legend: - after 1st itr: ratio of before/after time for the first benchmarking iteration. - before/after: ratio of before/after time. Higher is better for after. Above 1 represents a speedup. ```
-rw-r--r--array.c44
-rw-r--r--array.rb35
2 files changed, 41 insertions, 38 deletions
diff --git a/array.c b/array.c
index 1b2903a1f4..6c4ea5fd2a 100644
--- a/array.c
+++ b/array.c
@@ -3807,42 +3807,6 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary)
}
-/*
- * call-seq:
- * array.select {|element| ... } -> new_array
- * array.select -> new_enumerator
- *
- * Calls the block, if given, with each element of +self+;
- * returns a new +Array+ containing those elements of +self+
- * for which the block returns a truthy value:
- *
- * a = [:foo, 'bar', 2, :bam]
- * a1 = a.select {|element| element.to_s.start_with?('b') }
- * a1 # => ["bar", :bam]
- *
- * Returns a new Enumerator if no block given:
- *
- * a = [:foo, 'bar', 2, :bam]
- * a.select # => #<Enumerator: [:foo, "bar", 2, :bam]:select>
- *
- */
-
-static VALUE
-rb_ary_select(VALUE ary)
-{
- VALUE result;
- long i;
-
- RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
- result = rb_ary_new2(RARRAY_LEN(ary));
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
- rb_ary_push(result, rb_ary_elt(ary, i));
- }
- }
- return result;
-}
-
struct select_bang_arg {
VALUE ary;
long len[2];
@@ -6697,6 +6661,12 @@ ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE
}
static VALUE
+ary_sized_alloc(rb_execution_context_t *ec, VALUE self)
+{
+ return rb_ary_new2(RARRAY_LEN(self));
+}
+
+static VALUE
ary_sample0(rb_execution_context_t *ec, VALUE ary)
{
return ary_sample(ec, ary, rb_cRandom, Qfalse, Qfalse);
@@ -8702,9 +8672,7 @@ Init_Array(void)
rb_define_method(rb_cArray, "collect!", rb_ary_collect_bang, 0);
rb_define_method(rb_cArray, "map", rb_ary_collect, 0);
rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0);
- rb_define_method(rb_cArray, "select", rb_ary_select, 0);
rb_define_method(rb_cArray, "select!", rb_ary_select_bang, 0);
- rb_define_method(rb_cArray, "filter", rb_ary_select, 0);
rb_define_method(rb_cArray, "filter!", rb_ary_select_bang, 0);
rb_define_method(rb_cArray, "keep_if", rb_ary_keep_if, 0);
rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1);
diff --git a/array.rb b/array.rb
index f63ff00056..5ec95a1097 100644
--- a/array.rb
+++ b/array.rb
@@ -57,6 +57,41 @@ class Array
end
# call-seq:
+ # array.select {|element| ... } -> new_array
+ # array.select -> new_enumerator
+ #
+ # Calls the block, if given, with each element of +self+;
+ # returns a new +Array+ containing those elements of +self+
+ # for which the block returns a truthy value:
+ #
+ # a = [:foo, 'bar', 2, :bam]
+ # a1 = a.select {|element| element.to_s.start_with?('b') }
+ # a1 # => ["bar", :bam]
+ #
+ # Returns a new Enumerator if no block given:
+ #
+ # a = [:foo, 'bar', 2, :bam]
+ # a.select # => #<Enumerator: [:foo, "bar", 2, :bam]:select>
+ def select
+ Primitive.attr! :inline_block
+ Primitive.attr! :use_block
+
+ unless defined?(yield)
+ return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)'
+ end
+
+ _i = 0
+ value = nil
+ result = Primitive.ary_sized_alloc
+ while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) })
+ result << value if yield value
+ end
+ result
+ end
+
+ alias filter select
+
+ # call-seq:
# array.shuffle!(random: Random) -> array
#
# Shuffles the elements of +self+ in place.