I tried to use Rust's HashMap from Ruby ... is a continuation.
Last time, I wrote as follows.
To get the hash value, a Ruby method is dynamically called and Rust's hash function is also executed. Furthermore, Ruby methods are dynamically called during Eq. When calling a method from Ruby, the method is usually cached so that the next call will be faster, but when it is called from C or Rust, it will have to be searched from the method table every time, which is very slow.
This time, I was able to confirm that find, insert, and delete are faster by caching the method.
before:
$ ruby benchmark.rb --rust --seed 1
Hash: Rust
Seed: 1
user system total real
values 1.896433 0.152249 2.048682 ( 2.049503)
keys 1.589012 0.118299 1.707311 ( 1.708012)
find 11.612382 0.002426 11.614808 ( 11.617854)
insert 15.397090 0.012138 15.409228 ( 15.413532)
delete 7.581418 0.001750 7.583168 ( 7.585473)
after:
$ ruby benchmark.rb --rust --seed 1
Hash: Rust
Seed: 1
user system total real
values 1.924479 0.166750 2.091229 ( 2.092445)
keys 1.514441 0.112787 1.627228 ( 1.628187)
find 8.677083 0.001990 8.679073 ( 8.681669)
insert 12.661791 0.010463 12.672254 ( 12.678383)
delete 5.908580 0.001670 5.910250 ( 5.912530)
insert is 37% faster than the standard Hash class. I did it
So far, the hash function has been implemented as follows.
fn hash<H: Hasher>(&self, state: &mut H) {
let val = ruby::fun_call(self.0, "hash", &[]);
val.to_raw().hash(state);
}
Every time I call it from Rust, I have to pull the table, but when I call it from Ruby, it is cached just like that. In other words, you can call it via Ruby. so
pub static mut M_HASH: Value = NIL;
// ...
let sym_hash = module_eval(klass, "def self.value_hash(val); val.hash; end");
M_HASH = obj_method_by_symbol(klass, sym_hash);
Create eval
to define a Ruby function.
fn hash<H: Hasher>(&self, state: &mut H) {
let method = unsafe { crate::hashmap::M_HASH };
let val = ruby::method_call(method, &[self.0]);
val.to_raw().hash(state);
}
The hash function makes this cached Method
call instead of calling the instance method.
extern "C" fn mark(ptr: *const ffi::c_void) {
gc_mark(unsafe { M_HASH });
Be careful not to retrieve this Method
instance by GC (this sometimes caused SEGV and was hard to debug)
Recommended Posts