From: "Eregon (Benoit Daloze) via ruby-core" Date: 2023-02-11T14:00:17+00:00 Subject: [ruby-core:112366] [Ruby master Bug#19424] Significant performance decreases in `OpenStruct#marshal_load` in Ruby 3.0 and 3.1 Issue #19424 has been updated by Eregon (Benoit Daloze). Agreed, OpenStruct shouldn't be used for any performance-sensitive code, as already documented. IMHO it'd be nice if OpenStruct doesn't define extra methods, and only uses `method_missing` + a Hash internally and just inherits from BasicObject, for simplicity and to avoids unexpected performance trade-offs. There might be some compatibility issues with that, hard to know until it's tried. Some more thoughts about that in https://github.com/oracle/truffleruby/pull/2702#issuecomment-1209267087 ---------------------------------------- Bug #19424: Significant performance decreases in `OpenStruct#marshal_load` in Ruby 3.0 and 3.1 https://bugs.ruby-lang.org/issues/19424#change-101810 * Author: sumitdey035 (Sumit Dey) * Status: Closed * Priority: Normal * ruby -v: 3.1.2 * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I can see degradation in **Marshal load** only in Ruby 3.1.2 compared to 2.7.4 Processing time increased by 200%(2.4 sec to 4.3 sec) Memory allocation increased by 600%(6500001 to 39000004) ``` require 'benchmark' require 'ostruct' N_OJ = 500_000 ex_HASH = { 'one' => 1, 'array' => [ true, false ] } ex_JSON = '{ "one": 1, "array": [ true, false ] }' ex_STRUCT = OpenStruct.new( one: 1, hash: ex_HASH, array: [ true, false ] ) ex_MARSHAL = "\x04\bU:\x0FOpenStruct{\b:\bonei\x06:\thash{\aI\"\bone\x06:\x06ETi\x06I\"\narray\x06;\bT[\aTF:\narray[\aTF" "-----------------Ruby #{system("rbenv version")}----------------" Benchmark.bm(20) do |x| x.report('native marshal dump') do N_OJ.times do y = Marshal.dump(ex_STRUCT) end end x.report('native marshal load') do N_OJ.times do y = Marshal.load(ex_MARSHAL) end end start_memory = GC.stat[:total_allocated_objects] N_OJ.times do y = Marshal.dump(ex_STRUCT) end end_memory = GC.stat[:total_allocated_objects] print "Marshal dump memory allocation- #{end_memory - start_memory}\n" start_memory = GC.stat[:total_allocated_objects] N_OJ.times do y = Marshal.load(ex_MARSHAL) end end_memory = GC.stat[:total_allocated_objects] print "Marshal load memory allocation- #{end_memory - start_memory}\n" end``` **Benchmark and Memory consumption result** ![](Screenshot 2023-02-07 at 1.04.49 PM.png) ---Files-------------------------------- Screenshot 2023-02-07 at 1.04.49 PM.png (184 KB) -- 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/postorius/lists/ruby-core.ml.ruby-lang.org/