For those of you following along, I was able to shave off another 50 microseconds per client. I learned a few things along the way.

1. Most Ruby runtimes cannot optimize a call to #super with implicit arguments very well.

e.g.
  def foo(a, b, c)
    super  # <- slow because it has to figure out to pass (a,b,c)
    a + b + c
  end

It is more efficient to pass the specific arguments that the super-method should receive. If it should not receive any, use #super(). This let's the runtime avoid the work of interrogating the current method for its arguments and remarshalling them for the super-method.


2. When using FFI and FFI::Struct, it is *not* efficient to call MyStruct.size on a regular basis. The FFI library has to recalculate the size of the structure each time and return that value. Stash the size in an ivar somewhere and use that instead of calling #size.


3. When storing a few values, it's sometimes cheap & easy to just use a Hash. But in hot code, the work to look the element up is sometimes not worth it. If you are only storing a few values, use an ivar.


4. Constants in Ruby are (unfortunately) not really constant. You can change the value of a constant though the runtime will kick out a warning. Since the Constants aren't constant, each access of a constant can incur a guarded lookup. The guard is there to see if the value referred to by the constant has changed since the last access. Again, in hot code this extra check can add up particularly if you are using lots of constants. Again, just stash the value in an ivar where lookup is fast.


5. As always, there is no faster code than "no code." I found a few spots where I was recalculating the same value 3 times in a hot path. The calculation was fairly cheap, but when that hot path is called millions of times even the cheap calcs can be expensive. 

@value ||= cheap_when_called_once_but_expensive_when_called_millions_of_times

That does the trick. Wow, got to love those ivars.


6. In hot code when looking up a value in an array, it's a tad faster to use #at instead of #[]. The reason is that #[] can take different arguments other than a simple index (like a Range). Those extra argument checks can add up.


These are all micro-optimizations and may not even be valid on your runtime of choice. However, if you are running time critical code and your lingua franca is Ruby, then some of these tricks can eek out a few extra microseconds for you.

cr