Skip to content

[pull] master from ruby:master - #1256

Merged
pull[bot] merged 4 commits into
turkdevops:masterfrom
ruby:master
Jul 28, 2026
Merged

[pull] master from ruby:master#1256
pull[bot] merged 4 commits into
turkdevops:masterfrom
ruby:master

Conversation

@pull

@pull pull Bot commented Jul 28, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

peterzhu2118 and others added 4 commits July 28, 2026 10:57
I benchmarked various values of STR_DUPLICATE_MAX_EMBED_LEN and the
performance impacts. For String#dup, creating a view is faster until it
is below 256 bytes. Unsurprisingly, if we add modification to the string,
then it is faster to eagerly create a copy than use a view. Surprisingly,
if we append to the string, it's still faster to go from embedded -> extended
than shared -> extended. However, both of these cases will incur a memory
penalty as the remainder of the slot is wasted memory.

Benchmark:

    require "benchmark"

    TIMES = 10_000_000

    Benchmark.bm do |x|
      [64, 128, 256, 512, 1024].each do |len|
        len -= 25 # Subtract header length

        x.report("String#dup for #{len.to_s}") do
          pid = fork do
            str = ("a" * len).freeze

            i = 0
            while i < TIMES
              str.dup
              i += 1
            end
          end
          Process.wait(pid)
        end

        x.report("String#dup + String#[]= for #{len.to_s}") do
          pid = fork do
            str = ("a" * len).freeze

            i = 0
            while i < TIMES
              dup = str.dup
              dup[0] = "1"
              i += 1
            end
          end
          Process.wait(pid)
        end

        x.report("String#dup + String#<< for #{len.to_s}") do
          pid = fork do
            str = ("a" * len).freeze

            i = 0
            while i < TIMES
              dup = str.dup
              dup << "1"
              i += 1
            end
          end
          Process.wait(pid)
        end
      end
    end

Result:

    1024

                                            user     system      total        real
        String#dup for 39                0.000085   0.000646   0.207714 (  0.208041)
        String#dup + String#[]= for 39   0.000067   0.000379   0.499957 (  0.500514)
        String#dup + String#<< for 39    0.000069   0.000413   0.600205 (  0.600984)
        String#dup for 103               0.000068   0.000374   0.236938 (  0.237424)
        String#dup + String#[]= for 103  0.000063   0.000401   0.501791 (  0.502506)
        String#dup + String#<< for 103   0.000066   0.000395   0.600041 (  0.600870)
        String#dup for 231               0.000066   0.000370   0.281924 (  0.282409)
        String#dup + String#[]= for 231  0.000064   0.000355   0.574779 (  0.575599)
        String#dup + String#<< for 231   0.000070   0.000395   0.691011 (  0.691972)
        String#dup for 487               0.000070   0.000366   0.355631 (  0.356230)
        String#dup + String#[]= for 487  0.000067   0.000391   0.653774 (  0.654509)
        String#dup + String#<< for 487   0.000066   0.000359   0.891413 (  0.892607)
        String#dup for 999               0.000068   0.000394   0.527350 (  0.528033)
        String#dup + String#[]= for 999  0.000067   0.000378   0.786794 (  0.787682)
        String#dup + String#<< for 999   0.000063   0.000363   1.070604 (  1.071885)

    512

                                            user     system      total        real
        String#dup for 39                0.000083   0.000639   0.207305 (  0.207687)
        String#dup + String#[]= for 39   0.000067   0.000398   0.501968 (  0.502645)
        String#dup + String#<< for 39    0.000071   0.000414   0.586533 (  0.587365)
        String#dup for 103               0.000064   0.000397   0.222104 (  0.222600)
        String#dup + String#[]= for 103  0.000069   0.000389   0.483612 (  0.484240)
        String#dup + String#<< for 103   0.000069   0.000380   0.606659 (  0.607510)
        String#dup for 231               0.000067   0.000388   0.292563 (  0.293070)
        String#dup + String#[]= for 231  0.000064   0.000389   0.573774 (  0.574479)
        String#dup + String#<< for 231   0.000069   0.000413   0.739569 (  0.740614)
        String#dup for 487               0.000067   0.000419   0.370516 (  0.371072)
        String#dup + String#[]= for 487  0.000060   0.000337   0.676507 (  0.677282)
        String#dup + String#<< for 487   0.000074   0.000410   0.911670 (  0.916480)
        String#dup for 999               0.000069   0.000397   0.239633 (  0.240049)
        String#dup + String#[]= for 999  0.000066   0.000370   1.196397 (  1.198272)
        String#dup + String#<< for 999   0.000072   0.000396   1.683562 (  1.686244)

    256

                                            user     system      total        real
        String#dup for 39                0.000073   0.000588   0.198103 (  0.198485)
        String#dup + String#[]= for 39   0.000067   0.000397   0.475345 (  0.476011)
        String#dup + String#<< for 39    0.000074   0.000408   0.589001 (  0.589778)
        String#dup for 103               0.000073   0.000420   0.244381 (  0.244818)
        String#dup + String#[]= for 103  0.000065   0.000421   0.480720 (  0.481569)
        String#dup + String#<< for 103   0.000070   0.000414   0.589164 (  0.590123)
        String#dup for 231               0.000077   0.000447   0.272169 (  0.272640)
        String#dup + String#[]= for 231  0.000067   0.000425   0.517280 (  0.518037)
        String#dup + String#<< for 231   0.000064   0.000409   0.681593 (  0.682572)
        String#dup for 487               0.000065   0.000390   0.229708 (  0.230153)
        String#dup + String#[]= for 487  0.000067   0.000395   0.872966 (  0.874233)
        String#dup + String#<< for 487   0.000071   0.000411   1.317758 (  1.320085)
        String#dup for 999               0.000070   0.000397   0.255197 (  0.255609)
        String#dup + String#[]= for 999  0.000068   0.000380   1.174781 (  1.176700)
        String#dup + String#<< for 999   0.000067   0.000389   1.729608 (  1.738797)

    128

                                            user     system      total        real
        String#dup for 39                0.000105   0.000950   0.205384 (  0.206565)
        String#dup + String#[]= for 39   0.000066   0.000406   0.483924 (  0.485308)
        String#dup + String#<< for 39    0.000075   0.000433   0.590150 (  0.591641)
        String#dup for 103               0.000076   0.000414   0.225900 (  0.227975)
        String#dup + String#[]= for 103  0.000098   0.000604   0.499234 (  0.503926)
        String#dup + String#<< for 103   0.000070   0.000400   0.619562 (  0.627411)
        String#dup for 231               0.000073   0.000445   0.241433 (  0.243606)
        String#dup + String#[]= for 231  0.000067   0.000402   0.843665 (  0.853293)
        String#dup + String#<< for 231   0.000071   0.000392   0.990903 (  0.994555)
        String#dup for 487               0.000073   0.000403   0.234920 (  0.236132)
        String#dup + String#[]= for 487  0.000077   0.000445   0.944582 (  0.956001)
        String#dup + String#<< for 487   0.000069   0.000410   1.337099 (  1.341250)
        String#dup for 999               0.000072   0.000420   0.233520 (  0.234182)
        String#dup + String#[]= for 999  0.000072   0.000419   1.186288 (  1.188972)
        String#dup + String#<< for 999   0.000069   0.000436   1.685112 (  1.689536)
Ractor::Port has a public allocator, so Ractor::Port.allocate returns a port whose owner Ractor is still NULL; only ractor_port_init() fills it in. The methods dereferenced it unchecked:

Ractor::Port.allocate.closed?  # [BUG] Segmentation fault at 0x60

Route the paths reachable from Ruby through ractor_port_ptr_check(), which raises TypeError as other uninitialized typed data objects do. The self of #initialize_copy keeps the unchecked accessor because it is legitimately uninitialized there; only orig is checked.

Ractor.select did not crash, but such a port has id 0, the same as the current Ractor's default port, so it took messages from it.

Port#closed? also loses Primitive.attr! :leaf. It can raise now, and it was not a leaf before either, since the foreign Ractor path takes RACTOR_LOCK().

[Bug #22214]
* ZJIT: Fix trivial inlining for optional argument entries

Use SendDirect's jit_entry_idx when reading the callee bytecode for
trivial inlining, so optional-argument calls inspect the same entry
point that SendDirect selected.

This avoids inlining default-argument initialization code when the
caller actually provided the optional positional argument.

* ZJIT: Require trivial inline target to end at leave

Limit the trivial inliner to whole-ISEQ shapes that start at bytecode
index 0 and end with the second instruction's leave. Optional argument
entries can start in the middle of the ISEQ, so keep those SendDirect
calls for the general inliner, which handles jit_entry_idx.

This avoids folding default-argument initialization code when the caller
actually supplied the optional positional argument.

Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
@pull pull Bot locked and limited conversation to collaborators Jul 28, 2026
@pull pull Bot added the ⤵️ pull label Jul 28, 2026
@pull
pull Bot merged commit 3ab7644 into turkdevops:master Jul 28, 2026
1 of 3 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants