Commit cd8f350
Dynamic ar_table size
Instead of always allocating `ar_table` backed hashes in 160B
slots for them to have capacity of `RHASH_AR_TABLE_MAX_SIZE` (`8`),
they can now be allocated in any slot size between `64B` and `160B`.
`64B` is the minimum slot size that still allows transitioning to
an `st_table` if necessary.
The assumption is that there is a large number of literal hashes
and keyword argument hashes being built, and the overwhelming majority
of them aren't appended to.
When this assumption hold, this patch saves memory and reduce GC
pressure by allocating in a smaller slot than before.
However when the assumption doesn't hold, it has the oppositve effect
as the Hash will transition to an `st_table` when before it may have
stayed an `ar_table`.
Currently the main blocked for this change is that the compiler
has a tendency to generate code that make the final Hash size unknown.
For instance: `{a: 4, b: 4 + 1}` is compiled as:
```
0000 duphash {a: 4} ( 1)[Li]
0002 putspecialobject 1
0004 swap
0005 putobject :b
0007 putobject 4
0009 putobject_INT2FIX_1_
0010 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
0012 opt_send_without_block <calldata!mid:core#hash_merge_ptr, argc:3, ARGS_SIMPLE>
```
Here `duphash` has no information about how large of a hash it needs to allocate.
For this patch to be more efficient the compiler should generate something like:
```
0000 putobject {a: 4} ( 1)[Li]
0002 putspecialobject 1
0004 swap
0005 putobject :b
0007 putobject 4
0009 putobject_INT2FIX_1_
0010 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
0012 opt_send_without_block <calldata!mid:core#hash_merge_ptr, argc:3, ARGS_SIMPLE>
```
So that it's `hash_merge_ptr` that is responsible for allocating the final hash,
given it does have a much better clue of how large it would need to be.
Demo:
```ruby
require 'objspace'
def size(hash)
puts "size: #{hash.size}\tmemsize: #{ObjectSpace.memsize_of(hash)}"
end
size({})
size(a:1)
size(a:1, b:2)
size(a:1, b:2, c:3)
size(a:1, b:2, c:3, d:4)
size(a:1, b:2, c:3, d:4, e:5)
size(a:1, b:2, c:3, d:4, e:5, f:6)
size(a:1, b:2, c:3, d:4, e:5, f:6, g:7)
size(a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8)
size(a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9)
```
Before:
```
size: 0 memsize: 160
size: 1 memsize: 160
size: 2 memsize: 160
size: 3 memsize: 160
size: 4 memsize: 160
size: 5 memsize: 160
size: 6 memsize: 160
size: 7 memsize: 160
size: 8 memsize: 160
size: 9 memsize: 464
```
After:
```
size: 0 memsize: 64
size: 1 memsize: 64
size: 2 memsize: 64
size: 3 memsize: 80
size: 4 memsize: 96
size: 5 memsize: 128
size: 6 memsize: 128
size: 7 memsize: 160
size: 8 memsize: 160
size: 9 memsize: 448
```1 parent 4b30340 commit cd8f350
7 files changed
Lines changed: 262 additions & 55 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3351 | 3351 | | |
3352 | 3352 | | |
3353 | 3353 | | |
3354 | | - | |
3355 | | - | |
| 3354 | + | |
| 3355 | + | |
| 3356 | + | |
3356 | 3357 | | |
3357 | | - | |
| 3358 | + | |
| 3359 | + | |
| 3360 | + | |
| 3361 | + | |
| 3362 | + | |
| 3363 | + | |
| 3364 | + | |
3358 | 3365 | | |
3359 | 3366 | | |
3360 | 3367 | | |
| |||
0 commit comments