summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/hash_key.yml5
-rw-r--r--hash.c18
2 files changed, 22 insertions, 1 deletions
diff --git a/benchmark/hash_key.yml b/benchmark/hash_key.yml
new file mode 100644
index 0000000000..cab4cf9ca4
--- /dev/null
+++ b/benchmark/hash_key.yml
@@ -0,0 +1,5 @@
+prelude: |
+ obj = Object.new
+ hash = { obj => true }
+benchmark: hash.key?(obj)
+loop_count: 30000000
diff --git a/hash.c b/hash.c
index fba394fc40..437fb83fc2 100644
--- a/hash.c
+++ b/hash.c
@@ -210,10 +210,26 @@ any_hash(VALUE a, st_index_t (*other_func)(VALUE))
return (long)hnum;
}
+VALUE rb_obj_hash(VALUE obj);
+VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *cme, int kw_splat);
+
static st_index_t
obj_any_hash(VALUE obj)
{
- VALUE hval = rb_check_funcall_basic_kw(obj, id_hash, rb_mKernel, 0, 0, 0);
+ VALUE hval = Qundef;
+ VALUE klass = CLASS_OF(obj);
+ if (klass) {
+ const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, id_hash);
+ if (cme && METHOD_ENTRY_BASIC(cme) && RBASIC_CLASS(cme->defined_class) == rb_mKernel) {
+ // Optimize away the frame push overhead if it's the default Kernel#hash
+ if (cme->def->type == VM_METHOD_TYPE_CFUNC && cme->def->body.cfunc.func == (VALUE (*)(ANYARGS))rb_obj_hash) {
+ hval = rb_obj_hash(obj);
+ }
+ else {
+ hval = rb_vm_call0(GET_EC(), obj, id_hash, 0, 0, cme, 0);
+ }
+ }
+ }
if (UNDEF_P(hval)) {
hval = rb_exec_recursive_outer_mid(hash_recursive, obj, 0, id_hash);