Age | Commit message (Collapse) | Author |
---|
| Most of this code use the `type * name` style, while the overwhemling majority of the rest of ruby use the `type *name` style. This is a cosmetic change, but helps with readability. |
| |
| Use PR_SET_VMA_ANON_NAME to set human-readable names for anonymous virtual memory areas mapped by `mmap()` when compiled and run on Linux 5.17 or higher. This makes it convenient for developers to debug mmap. Notes: Merged: https://github.com/ruby/ruby/pull/12119 |
| Now that we've inlined the eden_heap into the size_pool, we should rename the size_pool to heap. So that Ruby contains multiple heaps, with different sized objects. The term heap as a collection of memory pages is more in memory management nomenclature, whereas size_pool was a name chosen out of necessity during the development of the Variable Width Allocation features of Ruby. The concept of size pools was introduced in order to facilitate different sized objects (other than the default 40 bytes). They wrapped the eden heap and the tomb heap, and some related state, and provided a reasonably simple way of duplicating all related concerns, to provide multiple pools that all shared the same structure but held different objects. Since then various changes have happend in Ruby's memory layout: * The concept of tomb heaps has been replaced by a global free pages list, with each page having it's slot size reconfigured at the point when it is resurrected * the eden heap has been inlined into the size pool itself, so that now the size pool directly controls the free_pages list, the sweeping page, the compaction cursor and the other state that was previously being managed by the eden heap. Now that there is no need for a heap wrapper, we should refer to the collection of pages containing Ruby objects as a heap again rather than a size pool Notes: Merged: https://github.com/ruby/ruby/pull/11771 |
| Notes: Merged: https://github.com/ruby/ruby/pull/11389 |
| Notes: Merged: https://github.com/ruby/ruby/pull/11389 |
| * Fix unused functions when no `mmap`. ``` shape.c:285:1: warning: unused function 'redblack_insert' [-Wunused-function] 285 | redblack_insert(redblack_node_t * tree, ID key, rb_shape_t * value) | ^~~~~~~~~~~~~~~ ``` * Fix unknown warning group '-Wmaybe-uninitialized' with clang. ``` thread_win32.c:596:1: warning: unknown warning group '-Wmaybe-uninitialized', ignored [-Wunknown-warning-option] 596 | COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized) | ^ ``` Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com> Notes: Merged: https://github.com/ruby/ruby/pull/11376 Merged-By: nobu <nobu@ruby-lang.org> |
| [Bug #20522] If `Warning.warn` is redefined in Ruby, emitting a warning would invoke Ruby code, which can't safely be done when YJIT is compiling. |
| |
| The GC is initialized by this point, so we can use xcalloc instead of ruby_mimcalloc. |
| Many places call ruby_mimmalloc then MEMZERO. This can be reduced by using ruby_mimcalloc instead. |
| If there is a shape cache, then we should believe the results instead of doing a linear search for non-existent items This fixes a case where checking the index of an undefined ivar would result in an O(n) search. Now we get O(log n). Benchmark is as follows: ```ruby N = ARGV[0].to_i class ManyIVs class_eval "def initialize;" + N.times.map { "@a#{_1} = #{_1}" }.join("\n") + "end" def check defined?(@not) end end class Subclass < ManyIVs def initialize super @foo = 123 end end def t s = Process.clock_gettime Process::CLOCK_MONOTONIC yield Process.clock_gettime(Process::CLOCK_MONOTONIC) - s end def test a = ManyIVs.new b = Subclass.new t { 200000.times { a.check; b.check } } end puts "#{N},#{test}" ``` On the master branch: ``` $ for i in (seq 1 3 32); ./miniruby test.rb $i; end 1,0.015619999991031364 4,0.013061000005109236 7,0.013365999999223277 10,0.015474999992875382 13,0.017674999980954453 16,0.020055999979376793 19,0.02260500000556931 22,0.0254080000158865 25,0.02806599999894388 28,0.031244999991031364 31,0.034568000002764165 ``` On this branch: ``` $ for i in (seq 1 3 32); ./miniruby test.rb $i; end 1,0.015848999988520518 4,0.013225000002421439 7,0.013049000001046807 10,0.010697999998228624 13,0.010902000009082258 16,0.011448000004747882 19,0.01151199999731034 22,0.011539999977685511 25,0.01173300002119504 28,0.011900000012246892 31,0.012278999987756833 ``` |
| |
| |
| |
| |
| This removes the assumption about SIZE_POOL_COUNT for shapes. |
| |
| The redblack tree cache is totally optional, so if we can't allocate room for the cache, then just pretend as if the cache is full if mmap fails |
| |
| This is more consistent with other messages. |
| |
| As suggested by Mame, we should try to help users fix the issues without having to lookup the meaning of the warning. |
| Objects with the same shape must always have the same "embeddedness" (either embedded or heap allocated) because YJIT assumes so. However, using remove_instance_variable, it's possible that some objects are embedded and some are heap allocated because it does not re-embed heap allocated objects. This commit changes remove_instance_variable to re-embed Object instance variables when it becomes small enough. |
| The bug in i686 was fixed in commit 71babe5536bdb2238509752d8706194ee57ff485. |
| |
| |
| These assertions check that binary search tree invariants are held for the new tree. |
| It's too difficult for me to keep track that y is the new node, x is the new left node, z is the new right node, a is the new left left node, b is the new left right node, c is the new right left node, and d is the new right right node. This commit refactors the variable names to be more descriptive. |
| |
| |
| |
| |
| This commit adds assertions to verify that the shape cache is correct compared to the shape tree. |
| This adds an assertion that the instance variable does not already exist in the shape tree when creating a new shape. |
| Previously, the method left one shape available (MAX_SHAPE_ID) when called without arguments. |
| Many tests start by exhausting all shapes, which is a slow process. By exposing a method to directly move the bump allocator forward we cut test runtime in half. Before: ``` Finished tests in 1.544756s ``` After: ``` Finished tests in 0.759733s, ``` |
| Reproduction script: ``` o = Object.new 10.times { |i| o.instance_variable_set(:"@a#{i}", i) } i = 0 a = Object.new while RubyVM::Shape.shapes_available > 2 a.instance_variable_set(:"@i#{i}", 1) i += 1 end o.remove_instance_variable(:@a0) puts o.instance_variable_get(:@a1) ``` Before this patch, it would incorrectly output `2` and now it correctly outputs `1`. |
| That function is a bit too low level to called from multiple places. It's always used in tandem with `rb_shape_set_too_complex` and both have to know how the object is laid out to update the `iv_ptr`. So instead we can provide two higher level function: - `rb_obj_copy_ivs_to_hash_table` to prepare a `st_table` from an arbitrary oject. - `rb_obj_convert_to_too_complex` to assign the new `st_table` to the old object, and safely free the old `iv_ptr`. Unfortunately both can't be combined into one, because `rb_obj_copy_ivar` need `rb_obj_copy_ivs_to_hash_table` to copy from one object to another. |
| Other objects may be using the shape, so we can't change the capacity otherwise the other objects may have a buffer overflow. |
| This reverts commit 5f3fb4f4e397735783743fe52a7899b614bece20. |
| This reverts commit f6910a61122931e4193bcc0fad18d839c319b720. We're seeing crashes in the test suite of Shopify's core monolith after this change. |
| We don't need to create a shape to transition capacity as we can transition the capacity when the capacity of the SHAPE_IVAR changes. |
| Right now the `rb_shape_get_next` shape caller need to first check if there is capacity left, and if not call `rb_shape_transition_shape_capa` before it can call `rb_shape_get_next`. And on each of these it needs to checks if we got a TOO_COMPLEX back. All this logic is duplicated in the interpreter, YJIT and RJIT. Instead we can have `rb_shape_get_next` do the capacity transition when needed. The caller can compare the old and new shapes capacity to know if resizing is needed. It also can check for TOO_COMPLEX only once. |
| When an inline cache misses, it is very likely that the stale shape_id and the current instance shape_id have a close common ancestor. For example if the instance variable is sometimes frozen sometimes not, one of the two shape will be the direct parent of the other. Another pattern that commonly cause IC misses is "memoization", in such case the object will have a "base common shape" and then a number of close descendants. In addition, when we find a common ancestor, we store it in the inline cache instead of the current shape. This help prevent the cache from flip-flopping, ensuring the next lookup will be marginally faster and more generally avoid writing in memory too much. However, now that shapes have an ancestors index, we only check for a few ancestors before falling back to use the index. So overall this change speeds up what is assumed to be the more common case, but makes what is assumed to be the less common case a bit slower. ``` compare-ruby: ruby 3.3.0dev (2023-10-26T05:30:17Z master 701ca070b4) [arm64-darwin22] built-ruby: ruby 3.3.0dev (2023-10-26T09:25:09Z shapes_double_sear.. a723a85235) [arm64-darwin22] warming up...... | |compare-ruby|built-ruby| |:------------------------------------|-----------:|---------:| |vm_ivar_stable_shape | 11.672M| 11.679M| | | -| 1.00x| |vm_ivar_memoize_unstable_shape | 7.551M| 10.506M| | | -| 1.39x| |vm_ivar_memoize_unstable_shape_miss | 11.591M| 11.624M| | | -| 1.00x| |vm_ivar_unstable_undef | 9.037M| 7.981M| | | 1.13x| -| |vm_ivar_divergent_shape | 8.034M| 6.657M| | | 1.21x| -| |vm_ivar_divergent_shape_imbalanced | 10.471M| 9.231M| | | 1.13x| -| ``` Co-Authored-By: John Hawthorn <john@hawthorn.email> |
| This commit makes every initial size pool shape a root shape and assigns it a capacity of 0. |
| |
| `remove_shape_recursive` wasn't considering that if we run out of shapes, it might have to transition to SHAPE_TOO_COMPLEX. When this happens, we now return with an error and the caller initiates the evacuation. |
| On 32-bit systems, we must store the shape ID in the gen_ivtbl to not lose the shape. If we directly store the ST table into the generic ivar table, then we lose the shape. This makes it impossible to determine the shape of the object and whether it is too complex or not. |
| There is a handful of call sites where we may transition to OBJ_TOO_COMPLEX_SHAPE if we just ran out of shapes, but that weren't handling it properly. |