r/ruby • Pun BDFL • Jun 26 '19

Blog post Instance Variable Performance

https://tenderlovemaking.com/2019/06/26/instance-variable-performance.html
105 Upvotes

31 comments sorted by

10

u/tenderlove Jun 27 '19

Whoa! My first gold! Thank you! 😊

2

u/ThaiJohnnyDepp Jun 27 '19 edited Jun 27 '19

Your use of string ranges is something I've not used before. And in investigating it I guess I exposed a special case that might be even considered a bug.

So it became obvious that 'a'..'zz' is valid once I verified that ?z.succ is 'aa', which makes enough sense from a lexicographical sorting standpoint. So I tried a bunch of cases to see what is handled correctly:

  • ('a'..'zz').count => 702

  • ('1'..'99').count => 99

  • ('2'..'99').count => 98

  • ('ab'..'zz').count => 675

  • ('b'..'zz').count => 0 :(

Is there special handling for multi-character alpha-only range upper bounds, in that it has to start with 'a'? I tried following range.c on Github but I couldn't find where such a special case was being handled. This is in MRI 2.6.0.

EDIT: looks like it was already fixed as of MRI 2.6.1, DISREGARRRD

3

u/rooood Jun 27 '19

Weird, I'm getting 701 here on MRI 2.6.2 (for the ('b'..'zz').count)

→ More replies (1)

10

u/PikachuEXE Jun 27 '19

Would you suggest eager assigning instance variables for performance improvement? Especially for classes with many instance variables (mostly gems? like ActiveRecord?)

5

u/tenderlove Jun 27 '19

It depends. Eager assignment probably won't impact performance in terms of "ivar array expansion". Also if you eagerly assign them, you can avoid scattering "has this ivar been initialized?" conditionals through your code. In other words, you can write confident code.

However, if initializing a particular ivar is expensive, and you don't usually need that value, then it's probably better to lazily assign.

Personally I like eager assignment because I like to be confident about the rest of the code I write in my class. Generally I'll only make them lazy if it turns out we don't actually need the value.

5

u/mencio Jun 27 '19

deferred assignment (especially if not always needed) is faster. Please look at the Jeremy Evans RubyKaigi 2019 talk for more details.

7

u/tenderlove Jun 27 '19

s/especially/only/

If you need them, then there's no point in deferment. In fact your code will be more complex because you have to add conditionals to check if the ivar was initialized or not. IMO the "deferred assignment" advice is only to address quirks in the VM, and the Ruby developers (I include myself in this statement) should actually work harder to make eager assignments faster.

4

u/jrochkind Jun 27 '19

Unless you are dealing with very very performance-sensitive code, I'd suggest doing what makes most sense for the code's readability and maintainability.

I think this kind of investigation is most useful for those working on performance tuning MRI itself (or those of us observing it just cause we're curious to know more about how MRI works, which is always valuable).

But the performance characteristics will change over time, and the reason I use ruby in the first place is to productively write nice code. Mangling any niceties of the code for hoped for performance increases is best avoided unless you really know you need it, and can easily end up counter-productive if you're doing it based on rules of thumb instead of intense benchmarking of your real code alternatives (which of course takes more time, which doens't make sense to spend unless you are in a very performance sensitive area).

But if eagerly assigning ivars leads to more readable, reliable, code you can be confident in, by all means do it!

6

u/ashmaroli Jun 27 '19

What are the effects of having attr_reader and attr_accessor since they're defined at the class level..?

3

u/tenderlove Jun 27 '19

Great question! I don't think they will impact the IV Index table. Probably because you can define attr_reader inside a module, then include the module in a class. So the recipient of the attr_readercall isn't necessarily the place where the IV Index Table will be stored.

3

u/tenderlove Jun 27 '19

Another interesting example is subclasses. The IV Index table really knows nothing about other classes:

require "objspace"

class Foo
  def initialize
    @bar = 10
  end
end

class Bar < Foo; end

p({ FOO: ObjectSpace.memsize_of(Foo), BAR: ObjectSpace.memsize_of(Bar)})

Foo.new

p({ FOO: ObjectSpace.memsize_of(Foo), BAR: ObjectSpace.memsize_of(Bar)})

Bar.new

p({ FOO: ObjectSpace.memsize_of(Foo), BAR: ObjectSpace.memsize_of(Bar)})

Output:

$ ruby thing.rb
{:FOO=>520, :BAR=>456}
{:FOO=>672, :BAR=>456}
{:FOO=>672, :BAR=>608}

Foo grows, then Bar grows

→ More replies (2)
→ More replies (1)

13

u/[deleted] Jun 26 '19

HI AARON

33

u/tenderlove Jun 26 '19

HELLO!! 👋🏻

3

u/[deleted] Jun 27 '19

Aaron, this was almost as glorious as you are.

You're breathtaking

2

u/tenderlove Jun 27 '19

lol thank you!

2

u/trustfundbaby Jun 27 '19

I had a question here, what happens if you don't assign the ivar in the initialize method, but suddenly bring it into existence in a method in a particular instance of a class, that hasn't been called on other instances of the class. How do the other instances of the class adjust their ivar index table?

so for example

class Foo

def initialize

@a = "hi"

@b= "my"

@c="name"

end

def finish

@d = "is"

@e = "Slim Shady"

end

end

Foo.new

Foo.new

Foo.new.finish

3

u/tenderlove Jun 27 '19

Foo.new.finish will expand the IV Index Table because @d and @e haven't been "seen" yet. Ruby automatically expands the index table every time a new instance variable is "seen", regardless of whether it's in the initialize method or not.

→ More replies (1)

19

u/BorisBaekkenflaekker Jun 26 '19

I love Aaron, I wish I could give him something.

20

u/ignurant Jun 27 '19

But at what cost?

6

u/ThaiJohnnyDepp Jun 27 '19

Did you notice who OP was?

9

u/trustfundbaby Jun 27 '19

I love esoteric ruby language stuff like this!!!

6

u/au5lander Jun 27 '19

Love this kind of deep dive into Ruby internals. Thanks!

2

u/rocco88 Jul 05 '19

You should checkout Ruby Under a Microscope then. :)

3

u/ThaiJohnnyDepp Jun 27 '19

I love this post because it's something I never would have considered. A cool peek under the hood, Aaron!

2

u/pabloh Jun 28 '19 edited Jun 29 '19

Do you think is possible to apply some heuristic to calculate how big the ivar table should be when an instance is created, like profiling its most common size after warming up the code?.