From: "himura467 (Akito Shitara) via ruby-core" Date: 2026-06-30T06:41:57+00:00 Subject: [ruby-core:125880] [Ruby Feature#19315] Lazy substrings in CRuby Issue #19315 has been updated by himura467 (Akito Shitara). I wrote some micro-benchmarks targeting the patterns most affected by `SHARABLE_MIDDLE_SUBSTRING`. Environment: arm64-darwin25. current: https://github.com/himura467/ruby/tree/rstring-raw-ptr-explicit-length-ops Ref: https://github.com/himura467/ruby/tree/19315-eval/19315-eval **Throughput scaling with slice size** On master, slicing from the middle of a large string degrades as the result grows (a full copy is made). On this branch, throughput is nearly constant because no copy occurs.
master (a2b9d6ff3b): parent=20MB, offset=1MB
middle slice [1MB, 1KB]     4,885,198 ops/s
middle slice [1MB, 10KB]      694,444 ops/s
middle slice [1MB, 100KB]     279,791 ops/s
middle slice [1MB, 1MB]        55,168 ops/s
middle slice [1MB, 10MB]        2,458 ops/s

current (c7eace4288): parent=20MB, offset=1MB
middle slice [1MB, 1KB]     9,066,183 ops/s
middle slice [1MB, 10KB]   10,672,359 ops/s
middle slice [1MB, 100KB]  11,441,648 ops/s
middle slice [1MB, 1MB]    11,614,402 ops/s
middle slice [1MB, 10MB]   11,574,074 ops/s
**Small string slices**
                                      master       current
slice [0, 1000] (ELTS_SHARED)    4,822,298     11,224,604 ops/s  (+2.3x)
slice [0, 10000] (ELTS_SHARED)   7,029,877     11,879,306 ops/s  (+1.7x)
**Memory sharing** (1000 slices x 100KB from a 10MB parent, naive copy total: 100MB)
                      master        current
max RSS              145 MB          30 MB  (-80%)
peak memory          137 MB          22 MB  (-84%)
wall time            0.04s          0.03s
**Memory retention risk** As mentioned in #note-7, `ELTS_SHARED` keeps the parent string alive as long as any slice references it. In the worst case, slicing a small portion from a large parent retains the entire parent buffer.
                                     master    current
live data needed                    100 KB     100 KB
total live string memory after GC   615 KB     100 MB
Strings small enough to be embedded (less than 999 bytes on 64-bit) are copied into the `RString` struct and do not reference the parent, so this risk does not apply to them. ---------------------------------------- Feature #19315: Lazy substrings in CRuby https://bugs.ruby-lang.org/issues/19315#change-117813 * Author: Eregon (Benoit Daloze) * Status: Open ---------------------------------------- CRuby should implement lazy substrings, i.e., "abcdef"[1..3] must not copy bytes. Currently CRuby only reuse the char* if the substring is until the end of the buffer. But it should also work wherever the substring starts and ends. Yes, it means RSTRING_PTR() might need to allocate to \0-terminate, so be it, it's worth it. There is already code for this (`SHARABLE_MIDDLE_SUBSTRING`), but it's disabled by default and `RSTRING_PTR()` needs to be changed to deal with this. It seems a good idea to introduce a variant of `RSTRING_PTR` which doesn't guarantee \0-termination, so such callers can then use the existing bytes always without copy. There are countless workarounds for this missing optimization, all not worth it with lazy substring and all less readable: * https://bugs.ruby-lang.org/issues/19314 * https://bugs.ruby-lang.org/issues/18598#note-3 * https://github.com/ruby/net-protocol/pull/14 * Manual lazy substrings which track string + index + length * More but I don't remember all now, feel free to comment or link more urls/tickets. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/