Closed fpacanowski closed 10 months ago
Hey, I put together a set of changes matching what the other versions are doing, you can find them here https://github.com/fpacanowski/ruby-extensions-benchmark/compare/master...matsadler:ruby-extensions-benchmark:master
The first change was to update the version of Magnus from 0.4 to 0.6. 0.6 has a number of optimisations.
The main reason your Magnus version was running slower because it wasn't doing quite the same thing as the other versions. Symbol::new
allocates a full Garbage Collectable object version of a symbol. It's the equivalent to "foo".to_sym
. I swapped it for Ruby::sym_new
, which creates a StaticSymbol
, which is a lighter weight non-GC-able symbol, the equivalent of a symbol literal in Ruby (e.g. :foo
), and the equivalent to what you were doing with the C and rb-sys versions. I also made the change to only create the StaticSymbol
once, then reuse it.
With this (at least on my machine) the Magnus version ends up faster than the C one. I think this is because rb_str_new_cstr
function used in the C version has to count the length of the string with every call, where as Rust knows the length of the string already, so Magnus can use the rb_str_new
function under the hood and pass the length in.
Background
I'm benchmarking different ways of creating a large nested hash in Ruby. In my benchmark I compared an implementation using raw
rb-sys
withmagnus
-based implementation. To my surprise the latter seems to be over 2x slower. Is this expected? Perhaps I'm doing something wrong?Benchmark results:
Code
The code is also available in this repo:
Ruby:
magnus implementation:
rb-sys implementation:
Notes
I thought this might have something to do with building Ruby symbols, but using string keys in the hash doesn't affect the result much.
Related discussion: https://github.com/oxidize-rb/rb-sys/issues/314