Skip to content

Conversation

chrisseaton
Copy link
Contributor

Using exceptions for control flow is slower than using conventional control flow in all implementations of Ruby I tried, and has particularly bad performance on optimising implementations of Ruby. This PR makes each test on a failing method overload 3x faster in MRI, and allows optimising implementations of Ruby like JRuby and TruffleRuby run it up to 10x faster than that.

truffleruby/truffleruby#697

@nixpulvis
Copy link
Contributor

Oh noes you've offended RoboCop!

@chrisseaton
Copy link
Contributor Author

Thanks I fixed it.

@nixpulvis
Copy link
Contributor

Ran the bench.rb with bundle exec ruby benchmarks/bench.rb and got the following results on master and this branch respectively.

master:

testing add                      0.260000   0.000000   0.260000 (  0.260768)
testing contracts add            3.180000   0.000000   3.180000 (  3.180327)
MethodProfiler results for: Contract
+------------------------+----------+----------+--------------+------------+-------------+
| Method                 | Min Time | Max Time | Average Time | Total Time | Total Calls |
+------------------------+----------+----------+--------------+------------+-------------+
| #failure_exception     | 0.015 ms | 0.121 ms | 0.020 ms     | 198.116 ms | 10000       |
| #args_contracts        | 0.001 ms | 0.127 ms | 0.001 ms     | 26.941 ms  | 20000       |
| #maybe_append_block!   | 0.001 ms | 0.143 ms | 0.001 ms     | 10.478 ms  | 10000       |
| #pattern_match?        | 0.001 ms | 0.031 ms | 0.001 ms     | 10.359 ms  | 10000       |
| #ret_contract          | 0.001 ms | 0.016 ms | 0.001 ms     | 10.346 ms  | 10000       |
| #method                | 0.001 ms | 0.025 ms | 0.001 ms     | 20.588 ms  | 20000       |
| #maybe_append_options! | 0.001 ms | 0.017 ms | 0.001 ms     | 10.241 ms  | 10000       |
+------------------------+----------+----------+--------------+------------+-------------+
MethodProfiler results for: Object
+-----------------------------------------------------+----------+----------+--------------+-------------+-------------+
| Method                                              | Min Time | Max Time | Average Time | Total Time  | Total Calls |
+-----------------------------------------------------+----------+----------+--------------+-------------+-------------+
| #contracts_add                                      | 0.155 ms | 0.368 ms | 0.182 ms     | 1822.543 ms | 10000       |
| .__contracts_engine                                 | 0.001 ms | 0.006 ms | 0.002 ms     | 0.173 ms    | 103         |
| #__contracts_ruby_original_contracts_add_j9u7oe5... | 0.001 ms | 0.100 ms | 0.001 ms     | 11.585 ms   | 10000       |
+-----------------------------------------------------+----------+----------+--------------+-------------+-------------+
MethodProfiler results for: Contracts::MethodDecorators
+-------------------------+----------+----------+--------------+------------+-------------+
| Method                  | Min Time | Max Time | Average Time | Total Time | Total Calls |
+-------------------------+----------+----------+--------------+------------+-------------+
| #method_added           | 0.024 ms | 0.234 ms | 0.049 ms     | 2.213 ms   | 45          |
| #singleton_method_added | 0.024 ms | 0.048 ms | 0.030 ms     | 0.273 ms   | 9           |
+-------------------------+----------+----------+--------------+------------+-------------+
MethodProfiler results for: Contracts::Decorator
MethodProfiler results for: Contracts::Support
+----------------+----------+----------+--------------+------------+-------------+
| Method         | Min Time | Max Time | Average Time | Total Time | Total Calls |
+----------------+----------+----------+--------------+------------+-------------+
| .eigenclass_of | 0.001 ms | 0.008 ms | 0.002 ms     | 0.079 ms   | 42          |
+----------------+----------+----------+--------------+------------+-------------+
MethodProfiler results for: UnboundMethod

no-exception-control-flow:

                                     user     system      total        real
testing add                      0.210000   0.000000   0.210000 (  0.205330)
testing contracts add            2.830000   0.000000   2.830000 (  2.831659)
MethodProfiler results for: Contract
+------------------------+----------+----------+--------------+------------+-------------+
| Method                 | Min Time | Max Time | Average Time | Total Time | Total Calls |
+------------------------+----------+----------+--------------+------------+-------------+
| #failure_exception     | 0.015 ms | 0.226 ms | 0.020 ms     | 201.966 ms | 10000       |
| #maybe_append_block!   | 0.001 ms | 0.110 ms | 0.001 ms     | 12.598 ms  | 10000       |
| #maybe_append_options! | 0.001 ms | 0.090 ms | 0.001 ms     | 12.472 ms  | 10000       |
| #ret_contract          | 0.001 ms | 0.159 ms | 0.001 ms     | 12.423 ms  | 10000       |
| #pattern_match?        | 0.001 ms | 0.103 ms | 0.001 ms     | 12.099 ms  | 10000       |
| #method                | 0.001 ms | 0.110 ms | 0.001 ms     | 24.122 ms  | 20000       |
| #args_contracts        | 0.001 ms | 0.037 ms | 0.001 ms     | 23.647 ms  | 20000       |
+------------------------+----------+----------+--------------+------------+-------------+
MethodProfiler results for: Object
+-----------------------------------------------------+----------+----------+--------------+-------------+-------------+
| Method                                              | Min Time | Max Time | Average Time | Total Time  | Total Calls |
+-----------------------------------------------------+----------+----------+--------------+-------------+-------------+
| #contracts_add                                      | 0.158 ms | 0.733 ms | 0.194 ms     | 1943.463 ms | 10000       |
| .__contracts_engine                                 | 0.001 ms | 0.013 ms | 0.002 ms     | 0.218 ms    | 103         |
| #__contracts_ruby_original_contracts_add_j9u7r18... | 0.001 ms | 0.111 ms | 0.002 ms     | 18.388 ms   | 10000       |
+-----------------------------------------------------+----------+----------+--------------+-------------+-------------+
MethodProfiler results for: Contracts::MethodDecorators
+-------------------------+----------+----------+--------------+------------+-------------+
| Method                  | Min Time | Max Time | Average Time | Total Time | Total Calls |
+-------------------------+----------+----------+--------------+------------+-------------+
| #method_added           | 0.025 ms | 0.065 ms | 0.048 ms     | 2.139 ms   | 45          |
| #singleton_method_added | 0.025 ms | 0.044 ms | 0.029 ms     | 0.258 ms   | 9           |
+-------------------------+----------+----------+--------------+------------+-------------+
MethodProfiler results for: Contracts::Decorator
MethodProfiler results for: Contracts::Support
+----------------+----------+----------+--------------+------------+-------------+
| Method         | Min Time | Max Time | Average Time | Total Time | Total Calls |
+----------------+----------+----------+--------------+------------+-------------+
| .eigenclass_of | 0.001 ms | 0.010 ms | 0.002 ms     | 0.095 ms   | 42          |
+----------------+----------+----------+--------------+------------+-------------+
MethodProfiler results for: UnboundMethod

My setup FWIW:
deepinscreenshot_select-area_20171110110924

@chrisseaton
Copy link
Contributor Author

There's a benchmark in the linked issue as well (but with a slightly different patch).

@nixpulvis
Copy link
Contributor

Yea, I just skimmed over that. The bench.rb in this library could use some work. Maybe that's another PR down the pipeline :P

@egonSchiele egonSchiele merged commit f8f99bd into egonSchiele:master Nov 13, 2017
@egonSchiele
Copy link
Owner

Super interesting, thanks for the PR!

@chrisseaton chrisseaton deleted the no-exception-control-flow branch November 13, 2017 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants