Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite Kernel#tap with Ruby #3281

Merged
merged 2 commits into from Jul 3, 2020
Merged

Rewrite Kernel#tap with Ruby #3281

merged 2 commits into from Jul 3, 2020

Conversation

@k0kubun
Copy link
Member

@k0kubun k0kubun commented Jul 3, 2020

because Ruby is faster than C.

$ benchmark-driver -v --rbenv 'before;after' benchmark/kernel_tap.yml benchmark/vm_symbol_block_pass.rb --repeat-count=4
before: ruby 2.8.0dev (2020-07-03T06:16:55Z master 01776ca1c0) [x86_64-linux]
after: ruby 2.8.0dev (2020-07-03T07:14:40Z builtin-tap be549c7763) [x86_64-linux]
Calculating -------------------------------------
                         before       after
          kernel_tap    22.130M     29.958M i/s -     20.000M times in 0.903731s 0.667597s
vm_symbol_block_pass      1.439       1.485 i/s -       1.000 times in 0.694709s 0.673524s

Comparison:
                       kernel_tap
               after:  29958213.1 i/s
              before:  22130488.0 i/s - 1.35x  slower

             vm_symbol_block_pass
               after:         1.5 i/s
              before:         1.4 i/s - 1.03x  slower
@shyouhei
Copy link
Member

@shyouhei shyouhei commented Jul 3, 2020

Second thought you might also want to do this for Kernel#then.

Loading

@benoittgt
Copy link
Contributor

@benoittgt benoittgt commented Jul 3, 2020

Related explanation submitted by @k0kubun on twitter:

I thought a block must be inlinable over methods which are currently written in C with future JIT, but it's actually hard to update (for inlining block) a part of C code from JIT. My current plan is to mix C and Ruby for methods like #each, but then I noticed #tap was too easy :)

😊

Loading

@k0kubun
Copy link
Member Author

@k0kubun k0kubun commented Jul 3, 2020

Second thought you might also want to do this for Kernel#then.

Absolutely 🙂 I'll prepare another PR to see CI results for it separately though.

Loading

@k0kubun k0kubun merged commit f3a0d7a into ruby:master Jul 3, 2020
111 checks passed
Loading
@k0kubun k0kubun deleted the builtin-tap branch Jul 3, 2020
@ruurd
Copy link

@ruurd ruurd commented Jul 10, 2020

because Ruby is faster than C.

Lies. Damned lies. Statistics. Did you find out WHY it is faster?

Loading

@simi
Copy link
Contributor

@simi simi commented Jul 10, 2020

Lies. Damned lies. Statistics. Did you find out WHY it is faster?

It is not meant it is faster in general, but in this case of Kernel#tap implementation. Stats are in original post.

Loading

@ruurd
Copy link

@ruurd ruurd commented Jul 10, 2020

WHY is it faster. That is the question. Concluding that Ruby is faster here is uninteresting. Do a root cause analysis. Then we learn something perhaps.

Loading

@simi
Copy link
Contributor

@simi simi commented Jul 10, 2020

Everything is written in this thread actually.

Loading

@schneems
Copy link
Contributor

@schneems schneems commented Nov 19, 2020

Everything is written in this thread actually.

I don't totally follow from this thread alone. However via the linked twitter conversation: https://twitter.com/k0kubun/status/1279128061355495425

I haven't confirmed the details, but my current guess is that creating a new VM frame (vm_exec_core) is expensive and reusing the same VM frame by yield is faster than creating one every time like vm_exec_core -> rb_obj_tap -> vm_exec_core.

So to be explicit. While this unlocks JIT improvements, it is ALSO faster even without JIT. Possibly due to the reduction in calling vm_exec_core

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
6 participants