mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Use Set instead of Hash for Enumerable#uniq
This commit changes Enumerable#uniq to use a temporary Set instead of Hash
for performance and memory savings. We can see in the following benchmark:
a = ((0...10_000).to_a + (0...10_000).to_a).each
1_000.times { a.uniq }
Results:
Benchmark 1: master
Time (mean ± σ): 222.4 ms ± 3.7 ms [User: 212.4 ms, System: 7.6 ms]
Range (min … max): 216.9 ms … 227.6 ms 13 runs
Benchmark 2: branch
Time (mean ± σ): 189.7 ms ± 4.0 ms [User: 177.7 ms, System: 9.2 ms]
Range (min … max): 182.8 ms … 198.6 ms 15 runs
Summary
branch ran
1.17 ± 0.03 times faster than master
This commit is contained in:
1 parent
7ebf7492a9
commit
29c669bb56
1 file changed
+25
-13
Notes:
git
2026-09-13 05:35:02 +00:00
@@ -20,6 +20,7 @@
|
||||
#include "internal/proc.h"
|
||||
#include "internal/rational.h"
|
||||
#include "internal/re.h"
|
||||
#include "internal/set.h"
|
||||
#include "ruby/util.h"
|
||||
#include "ruby_assert.h"
|
||||
#include "symbol.h"
|
||||
@@ -4870,18 +4871,26 @@ enum_sum(int argc, VALUE* argv, VALUE obj)
|
||||
}
|
||||
|
||||
static VALUE
|
||||
uniq_func(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||
uniq_func(RB_BLOCK_CALL_FUNC_ARGLIST(i, set))
|
||||
{
|
||||
ENUM_WANT_SVALUE();
|
||||
rb_hash_add_new_element(hash, i, i);
|
||||
rb_set_add_no_check(set, i);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
struct uniq_iter_memo {
|
||||
VALUE set;
|
||||
VALUE ary;
|
||||
};
|
||||
|
||||
static VALUE
|
||||
uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||
uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo_))
|
||||
{
|
||||
struct uniq_iter_memo *memo = (struct uniq_iter_memo *)memo_;
|
||||
ENUM_WANT_SVALUE();
|
||||
rb_hash_add_new_element(hash, rb_yield_values2(argc, argv), i);
|
||||
if (rb_set_add_no_check(memo->set, rb_yield_values2(argc, argv))) {
|
||||
rb_ary_push(memo->ary, i);
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
@@ -4909,15 +4918,18 @@ uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||
static VALUE
|
||||
enum_uniq(VALUE obj)
|
||||
{
|
||||
VALUE hash, ret;
|
||||
rb_block_call_func *const func =
|
||||
rb_block_given_p() ? uniq_iter : uniq_func;
|
||||
|
||||
hash = rb_obj_hide(rb_hash_new());
|
||||
rb_block_call(obj, id_each, 0, 0, func, hash);
|
||||
ret = rb_hash_values(hash);
|
||||
rb_hash_clear(hash);
|
||||
return ret;
|
||||
if (rb_block_given_p()) {
|
||||
struct uniq_iter_memo memo;
|
||||
memo.set = rb_obj_hide(rb_set_new());
|
||||
memo.ary = rb_ary_new();
|
||||
rb_block_call(obj, id_each, 0, 0, uniq_iter, (VALUE)&memo);
|
||||
return memo.ary;
|
||||
}
|
||||
else {
|
||||
VALUE set = rb_obj_hide(rb_set_new());
|
||||
rb_block_call(obj, id_each, 0, 0, uniq_func, set);
|
||||
return rb_set_to_a(set);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
||||
Reference in new issue
Block a user