First of all, I know how extend and include work, and what they’re usually used for etc. Whether it is a good idea or not is not part of my question.
My question is: how expensive is extend? It’s a common Javascript technique to extend instances and singleton objects. One could do something similar in Ruby, but would it be slow if used on a lot of objects?
Let’s see what happens in Ruby 1.9.3-p0 if you call
extendon an object:So the module is mixed into the singleton class of the object. How costly is it to fetch the singleton class? Well,
rb_singleton_class_of(obj)in turn callssingleton_class_of(obj)(class.c:1253). That one returns immediately if the singleton class was accessed before (and thus already exists). If not, a new class is created bymake_singleton_classwhich is not too expensive as well:This is all
O(1).Afterwards,
rb_include_module(class.c:660) is called, which isO(n)regarding the number of modules already included by the singleton class because it needs to check whether the module is already there (there will usually not be many included modules in the singleton class, so this is okay).Conclusion:
extendis not a very expensive operation so you can use it often if you want to. The only thing I could imagine is that the resolution of method calls to the instance after theextendcould be a bit more complex, as one additional layer of modules needs to be checked. Both is less of a problem if you know that the singleton class already exists. In that case,extendintroduces almost no additional complexity.However, dynamically extending instances can lead to very unreadable code if applied too extensively, so take care.
This small benchmark demonstrates the situation regarding performance:
Results
Interestingly,
Module#includeon the metaclass is actually slower thanObject#extend, although it does the exact same thing (because we need special Ruby syntax to access the metaclass).Object#extendis more than twice as fast if the singleton class already exists.Object#define_singleton_methodis slowest (although it can be cleaner if you only want to dynamically add a single method only).The most interesting results are the bottom two, however: Creating an object and then extending it is nearly 4 times as slow as only creating the object! So if you create a lot of throwaway objects in a loop, for example, it might have a significant impact on performance if you extend every single one of those. It is much more efficient here to create a temporary class that includes the mixin explicitely.