Some example results in the docs no longer match what Ruby prints:
* Array#flatten and Array#flatten!: Set#inspect now shows Set[6, 7]
* Module: Mod.constants also includes Math::DomainError
* Module#const_get and Module#const_set: Floats are shown with full
precision
* Complex#imag: sin(PI/4) is 0.7071067811865475
* String#<<: missing # in #<Encoding:UTF-8>
* Enumerable#inject: typo #=>>
Only imemo_count_up and imemo_count_value treat the count as unsigned, so keep cnt as rb_len_t for the other users of MEMO and give them their own member.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
These still held String and Array lengths and indices in long, or cast them to long. rb_len_t is still long everywhere, so the generated code does not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MEMO counter becomes rb_len_t, which is wider than `unsigned long` once rb_len_t widens, so the Enumerable#count overflow builds the next value from RB_ULEN_MAX instead of assuming `unsigned long` words.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LONG2FIX does not check the range, so `[1].cycle(2**30).size` returned -1073741824 on mswin. 64-bit platforms hit the same with 2**62.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep the state for block-based `Enumerable#uniq` alive after it returns,
as `each` can retain and later invoke the internal block, making the
stack-allocated memo introduced in 29c669bb56 invalid.
This commit changes Enumerable#uniq to use a temporary Set instead of Hash
for performance and memory savings. We can see in the following benchmark:
a = ((0...10_000).to_a + (0...10_000).to_a).each
1_000.times { a.uniq }
Results:
Benchmark 1: master
Time (mean ± σ): 222.4 ms ± 3.7 ms [User: 212.4 ms, System: 7.6 ms]
Range (min … max): 216.9 ms … 227.6 ms 13 runs
Benchmark 2: branch
Time (mean ± σ): 189.7 ms ± 4.0 ms [User: 177.7 ms, System: 9.2 ms]
Range (min … max): 182.8 ms … 198.6 ms 15 runs
Summary
branch ran
1.17 ± 0.03 times faster than master
We always know when this contains a VALUE and even fire a write barrier
for it. We should use this when marking, support compaction, and no
longer need conservative marking.
After returning from the callback in st_update is the point that the
hash table may be resized, which could trigger a GC and mark the table
being used for the tally.
RUBY_GC_LIBRARY=wbcheck WBCHECK_VERIFY_AFTER_WB=1 ./miniruby -e '(0...100).map(&:to_s).tally'
* Remove 1 allocation in Enumerable#each_with_index
Previously, each call to Enumerable#each_with_index allocates 2
objects, one for the counting index, the other an imemo_ifunc passed
to `self.each` as a block.
Use `struct vm_ifunc::data` to hold the counting index directly to
remove 1 allocation.
* [DOC] Brief summary for usages of `struct vm_ifunc`
Enumerable#all?, #any?, #one?, and #none? do not use yielded arguments
as an Array. So they can use rb_block_call2 to omit array allocatoin.
Enumerable#find does not have to immediately accept yielded arguments as
an Array. It can delay array allocation until the predicate block
returns truthy.
(TODO: Enumerable#count and #find_all seem to be optimizable as well.)
assert does not print the bug report, only the file and line number of
the assertion that failed. RUBY_ASSERT prints the full bug report, which
makes it much easier to debug.
In most of case `sort_by` works on primitive type.
Using `qsort_r` with function pointer is much slower than compare data directly.
I implement an intro sort which compare primitive data directly for `sort_by`.
We can even afford an O(n) type check before primitive data sort.
It still go faster.
Prior to this commit the `OPTIMIZED_CMP` macro relied on a method lookup
to determine whether `<=>` was overridden. The result of the lookup was
cached, but only for the duration of the specific method that
initialized the cmp_opt_data cache structure.
With this method lookup, `[x,y].max` is slower than doing `x > y ?
x : y` even though there's an optimized instruction for "new array max".
(John noticed somebody a proposed micro-optimization based on this fact
in https://github.com/mastodon/mastodon/pull/19903.)
```rb
a, b = 1, 2
Benchmark.ips do |bm|
bm.report('conditional') { a > b ? a : b }
bm.report('method') { [a, b].max }
bm.compare!
end
```
Before:
```
Comparison:
conditional: 22603733.2 i/s
method: 19820412.7 i/s - 1.14x (± 0.00) slower
```
This commit replaces the method lookup with a new CMP basic op, which
gives the examples above equivalent performance.
After:
```
Comparison:
method: 24022466.5 i/s
conditional: 23851094.2 i/s - same-ish: difference falls within
error
```
Relevant benchmarks show an improvement to Array#max and Array#min when
not using the optimized newarray_max instruction as well. They are
noticeably faster for small arrays with the relevant types, and the same
or maybe a touch faster on larger arrays.
```
$ make benchmark COMPARE_RUBY=<master@5958c305> ITEM=array_min
$ make benchmark COMPARE_RUBY=<master@5958c305> ITEM=array_max
```
The benchmarks added in this commit also look generally improved.
Co-authored-by: John Hawthorn <jhawthorn@github.com>
rb_ary_tmp_new suggests that the array is temporary in some way, but
that's not true, it just creates an array that's hidden and not on the
transient heap. This commit renames it to rb_ary_hidden_new.
Previously, this would work as expected if the enumerable contained
0 or 1 element, and would raise LocalJumpError otherwise. That
inconsistent behavior is likely to lead to bugs.
Fixes [Bug #18635]