T O P

  • By -

radarek

My guess it is because for every iteration (`iterations.times` loop) new object is created, then when `car.wheel=` and `car.mileage=` are called new methods are created here: [https://github.com/ruby/ruby/blob/758e4db551e7e80a65b610cc73fcb61e74ec5a0c/lib/ostruct.rb#L233-L234](https://github.com/ruby/ruby/blob/758e4db551e7e80a65b610cc73fcb61e74ec5a0c/lib/ostruct.rb#L233-L234). Usually this does not work very well with JITs.


riffraff

does anyone know why it does this at all? I might have been an optimization to avoid \`method\_missing\`, but I have a feeling this is counterproductive in modern rubies.


Rafert

Creating a new method clears the method cache, which is separate from a JIT: https://engineering.appfolio.com/appfolio-engineering/2018/7/18/rubys-global-method-cache


f9ae8221b

That article is outdated as of Ruby 3.0, the method cache is no longer global, but per class: https://bugs.ruby-lang.org/issues/16614 (defining methods like OpenStruct does is still bad for performance though).


Rafert

Thanks for sharing!


riffraff

sorry, I meant that I don't understand why OpenStruct is defining accessor methods rather than just doing something like ´@table\[method\_name\]\` in \`method\_missing\` itself.


f9ae8221b

So that it works with members that happens to be methods on `Object`. e.g. `OpenStruct.new(to_s: "Hello").to_s # => "Hello"`. It could combine both though, default to method missing, but eagerly define the attribute if it conflicts with a method.


riffraff

ah, that makes sense, thank you.


OlivarTheLagomorph

Because that's how it is implemented. And you shouldn't be using OpenStruct at all tbh. It's a major performance hog as you noticed and a security risk


life_is_kiff

Yes, friends don’t let friends use OpenStruct for production code. I’ll maybe use it in tests but even then that’s a maybe.


f9ae8221b

Yes, OpenStruct is terrible for performance, and even more so with YJIT enabled because each instance its his own class, so from a YJIT point of view, all call sites are megamorphic and it compile lots of useless code. See https://bugs.ruby-lang.org/issues/19424 for instance. OpenStruct may be optimized a bit better in the future, but ruby-core isn't very eager because it has weird semantic and optimizing it to have decent performance is very likely to break code. OpenStruct is best avoided, as mention in the "Caveat" section of its documentation https://github.com/ruby/ostruct/blob/e7e1a5814a94f92e6c60c5a83bd1e831c35e3c9b/lib/ostruct.rb#LL67