From: "byroot (Jean Boussier) via ruby-core" Date: 2025-02-09T09:15:14+00:00 Subject: [ruby-core:120923] [Ruby master Bug#19288] Ractor JSON parsing significantly slower than linear parsing Issue #19288 has been updated by byroot (Jean Boussier). I profiled this repro out of curiosity, and ractors spend 32% of their time waiting for the VM lock (`vm_lock_enter` + the unlock) to be able to lookup in the `fstring` table: https://share.firefox.dev/4152X8a Currently this is done explicitly by the `json` gem, but even if `json` wasn't attempting to do it, Ruby would do the same thing once we're trying to insert string keys: https://share.firefox.dev/4hsVPbC I suppose there are various solutions to this with their own tradeoffs: - Don't intern hash keys when not on the main ractor. - Protect the `fstring` table with its own dedicated Read-write lock, so that concurrent Ractors can lookup string (but only one insert). And also reduce contention for other areas still protected by the remaining VM lock. - Somehow replace the fstring table by a truly lockfree hash table. ---------------------------------------- Bug #19288: Ractor JSON parsing significantly slower than linear parsing https://bugs.ruby-lang.org/issues/19288#change-111806 * Author: maciej.mensfeld (Maciej Mensfeld) * Status: Open * ruby -v: ruby 3.2.0 (2022-12-25 revision a528908271) [x86_64-linux] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- a simple benchmark: ```ruby require 'json' require 'benchmark' CONCURRENT = 5 RACTORS = true ELEMENTS = 100_000 data = CONCURRENT.times.map do ELEMENTS.times.map do { rand => rand, rand => rand, rand => rand, rand => rand }.to_json end end ractors = CONCURRENT.times.map do Ractor.new do Ractor.receive.each { JSON.parse(_1) } end end result = Benchmark.measure do if RACTORS CONCURRENT.times do |i| ractors[i].send(data[i], move: false) end ractors.each(&:take) else # Linear without any threads data.each do |piece| piece.each { JSON.parse(_1) } end end end puts result ``` Gives following results on my 8 core machine: ```shell # without ractors: 2.731748 0.003993 2.735741 ( 2.736349) # with ractors 12.580452 5.089802 17.670254 ( 5.209755) ``` I would expect Ractors not to be two times slower on the CPU intense work. -- 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/