| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: b9c718d1-5d57-4e7f-88d9-2e099ee86ee9 📥 CommitsReviewing files that changed from the base of the PR and between 1247f93 and 33f8f4c. 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 8 included reviews per hour; 2 remain after this review. 📝 Walkthrough WalkthroughAdded C implementations for Array#include? and Array#member?. The methods use mrb_equal() during traversal and refresh array state after each comparison. Tests cover equality, identity, special values, matching, and mutation. ChangesArray membership methods
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to 33f8f This PR adds a localized C-level fast path for Array# include? and member? without introducing an actionable merge-blocking risk; it is merge-ready after normal checks and review. Suggested reviewers: matz, dearblue 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. ❤️ ShareComment @coderabbitai help to get the list of available commands. |
Sorry, something went wrong.
`Enumerable#include?` reaches an element through a call to `each`, a block call and a `__svalue` send, then compares it with `==`. An Array can be walked in place, and the pair compared with `mrb_equal()`, as `Array#index` and `#delete` already compare one; `mrb_equal()` is also the test `OP_EQ` makes before it dispatches `==`, so the answers are the ones the array gave before. ```ruby a = (1..200).to_a i = 0 while i < 20000 do a.include?(200); i += 1 end ``` reads 6,524,727,384 instructions against master and 1,865,200,104 here. There is no block form to leave behind, so the two names reach the C walk directly rather than through `super`, which is where `Array#max` and `#min` leave theirs.
| Back | FazBrowse Home | New Git URL |
Summary
Enumerable#include? reaches an element through a call to each, a block call and a __svalue send, then compares it with ==. An Array can be walked in place, and the pair compared with mrb_equal(), as Array#index and #delete already compare one. This is the same move Array#max and #min made in 6722d35, applied to the other search Enumerable lends an Array.
Changes
Where the walk goes
mrb_equal() takes two values for equal where they are the same object and asks == only after, which is the test OP_EQ makes for a == b before it dispatches anything. Enumerable#include? reaches == through OP_EQ too, so the answers are the ones the array gave before, arrived at without a block call per element.
There is no block form to leave behind, so the two names reach the C walk directly rather than through super, which is where Array#max and #min leave theirs.
The walk reads the length and the pointer afresh each turn, because == may run Ruby that grows or shrinks the array under it.
There is no GC arena restore in the loop, and none is needed: what a call leaves in the arena is its return value, and this walk returns at the first one that is true. The answers it walks past are false, which is immediate. Array#index searches without one for the same reason. Array#count is the walk that does need one, since it goes past a true answer, and it has one.
Behaviour
Nothing an array answers moves, apart from a NaN. a and b are two NaNs built at run time as z / z from z = [0.0][0], so that nothing folds them into a single literal. Bold marks a row that differs from CRuby.
A NaN is equal to no value, its own operand included, so == can never find one: Enumerable#include? found nothing, while #index looked for the object and found the NaN the array holds. The two give one answer now, and it is #index's. That answer is right about the NaN an array holds and wrong about a second one, because a boxing that keeps a Float in the value normalizes every NaN to one representation, which is what the last row shows #index saying on master and saying still. Making a NaN an object of its own is a change to what a Float is rather than to how an array searches, and it is not in this PR.
Speed
Best of 11 interleaved runs against master, with a master-vs-master control to read the noise by, and the instruction counts callgrind reads beside them:
Size
bin/mruby .text for every build in build_config/ci/gcc-clang.rb:
The whole of it is mrbgems/mruby-array-ext/src/array.o, which grows by exactly 144 for the one function; no other object moves.
Testing
rake test passes for the default build, for build_config/host-nofloat.rb, for every build in build_config/ci/gcc-clang.rb including the C++ ABI one, and for a MRB_NO_BOXING build of the full-core gembox, with no compiler warnings beyond the one -Wmaybe-uninitialized about eq in hash.c that master already emits in a MRB_NO_BOXING build. The last of those is there because the rows about a NaN in the table above are ones a boxing decides.
One thing this does not test, and could not before either: the ISO assertions for Enumerable#include? and #member? (15.3.2.2.10 and 15.3.2.2.15) in test/t/enumerable.rb are written with an Array receiver, so in a build carrying this gem they now reach the C walk rather than the Enumerable one. Enumerable#max and #min are in the same position after 6722d35. Giving those assertions a receiver that is not an Array is worth doing and is not in this PR.
Environment
Machine, toolchain and the compile lines these numbers were taken withThe wall clock and instruction counts were taken with the default build:
gcc -std=gnu99 -g -O3 -Wall -Wundef -Werror-implicit-function-declaration -Wwrite-strings -DHAVE_MRUBY_REGEXP_GEM -DMRB_USE_SET -DHAVE_MRUBY_IO_GEM -DMRB_USE_RATIONAL -DMRB_USE_COMPLEX -DMRB_USE_BIGINT -DMRB_USE_DEBUG_HOOKThe ## Size table was taken with build_config/ci/gcc-clang.rb:
cxx_abi compiles mruby as C++ with gcc -x c++ -std=gnu++03; g++ only links. full-debug is the one build at -O0, enable_debug putting -g3 -O0 after the toolchain default of -g -O3.
Summary by CodeRabbit
New Features
Tests