Issue #21856 has been updated by mar21 (Martin Halfar). byroot (Jean Boussier) wrote in #note-1:
I'm able to repro on my machine, even though the different isn't quite as bad (more like 30% slower).
Profile of ruby 3.4.7: https://share.firefox.dev/4rw3mv0 Profile of ruby 4.0.0: https://share.firefox.dev/4rtvrmt
The striking difference on the profile seem to be that 4.0 spends 28% of its time in `remove_class_from_subclasses` -> `rb_classext_free_subclasses` -> `rb_iclass_classext_free` -> `rb_classext_foreach` -> `rb_obj_free`.
A few notes:
- This codepath was changed a lot with the `Ruby::Box` introduction, it may have become significantly slower. - It's surprising that we're sweeping lots of Class object, perhaps `Loofah` or `Nokogiri` are inadvertently allocating singleton classes in a hot spot?
We have just encountered this issue ourselves and while it is indeed not quite bad on smaller samples, it quickly scales up with larger (to huge) samples (in our case 2.5MB html document). - On Ruby 3.4.9 and below it took <1s to sanitize. - On Ruby 4.0.X immediately after startup and/or GC cleanup ~2s. Second run varies 40s-100s. Third run couple of minutes. ---------------------------------------- Bug #21856: Massive performance degradation of `rb_obj_free` for `T_CLASS` since Ruby 4.0 https://bugs.ruby-lang.org/issues/21856#change-116755 * Author: ahorek (Pavel Rosický) * Status: Open * ruby -v: ruby 4.1.0dev (2026-01-31T09:41:30Z master 7ef8c470d2) +PRISM [x86_64-linux] * Backport: 3.2: DONTNEED, 3.3: DONTNEED, 3.4: DONTNEED, 4.0: REQUIRED ---------------------------------------- Loofah sanitization is noticeably slower ``` Ruby: 3.4.8 Loofah: 2.25.0 Nokogiri: 1.19.0 Iterations: 100000 user system total real Loofah.fragment + scrub!(:prune) 26.091872 0.000000 26.091872 ( 25.110925) Loofah.scrub_fragment(:prune) 25.913185 0.010392 25.923577 ( 24.948464) Nokogiri HTML parse only 3.852690 0.000000 3.852690 ( 3.705930) Ruby: 4.0.0 & 4.0.1 Loofah: 2.25.0 Nokogiri: 1.19.0 Iterations: 100000 user system total real Loofah.fragment + scrub!(:prune) 38.094207 0.041753 38.135960 ( 36.669463) Loofah.scrub_fragment(:prune) 40.168795 0.000045 40.168840 ( 38.561806) Nokogiri HTML parse only 4.012936 0.052024 4.064960 ( 3.913272) Ruby: 4.1.0 (ruby 4.1.0dev (2026-01-31T09:41:30Z master 7ef8c470d2) +PRISM [x86_64-linux]) Loofah: 2.25.0 Nokogiri: 1.19.0 Iterations: 100000 user system total real Loofah.fragment + scrub!(:prune) 39.004228 0.000000 39.004228 ( 37.694873) Loofah.scrub_fragment(:prune) 39.043199 0.031284 39.074483 ( 37.182785) Nokogiri HTML parse only 3.889100 0.010427 3.899527 ( 3.741622) ``` Originally reported https://www.redmine.org/issues/43737 ---Files-------------------------------- benchmark.rb (1002 Bytes) -- https://bugs.ruby-lang.org/