[ruby-core:120923] [Ruby master Bug#19288] Ractor JSON parsing significantly slower than linear parsing
From:
"byroot (Jean Boussier) via ruby-core" <ruby-core@...>
Date:
2025-02-09 09:15:14 UTC
List:
ruby-core #120923
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 -- [email protected]
To unsubscribe send an email to [email protected]
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/