ruby-x.github.io/app/views/posts/_2017-4-7-how-not-to-interpret.haml

80 lines
3.9 KiB
Plaintext
Raw Normal View History

2018-04-10 18:50:07 +02:00
%p Method caching can be done at language level. Wow. But first some boring news:
%h2#vool-is-ready-mom-is-coming Vool is ready, Mom is coming
%p
The
= succeed "irtual" do
%strong V
= succeed "bject" do
%strong O
= succeed "riented" do
%strong O
= succeed "anguage" do
%strong L
%p
Vool will not reflect some of rubys more advanced features, like splats or implicit blocks,
and hopes to make the conditional logic more consistent.
%p
The
= succeed "inimal" do
%strong M
= succeed "bject" do
%strong O
= succeed "achine" do
%strong M
%h2#inline-method-caching Inline Method caching
%p
In ruby almost all work is actually done by method calling and an interpreter spends much of its
time looking up methods to call. The obvious thing to do is to cache the result, and this has
been the plan for a while.
%p
Off course for caching to work, one needs a cache key and invalidation strategy, both of which
are handled by the static types, which ill review below.
%h3#small-cache Small cache
%p
Aaron Patterson has done
%a{:href => "https://www.youtube.com/watch?v=b77V0rkr5rk"} research into method caching
in mri and found that most call sites (>99%) only need one cache entry.
%p
This means a single small object can carry the information needed, probably type, function address
and counter, times two.
%p
In rubyx this can literally be an object that we attach to the CallSite, either prefill if possible
or leave to be used at runtime.
%h3#method-lookup-is-a-static-function Method lookup is a static function
%p
The other important idea here is that the actual lookup of a method is a know function. Known at
compile time that is.
%p
Thus dynamic dispatch can be substituted by a cache lookup, and a static call. The result of the call
can/should update the cache and then we can start with the lookup again.
%p
This makes it possible to remove dynamic dispatch from the code, actually at code level.
I had previously though of implementing the send at a lower level, but see now that it would
be quite possible to do it at the language level with an if and a call, possible another call
for the miss. That would drop the language down from dynamic (4th level) to static (3rd level).
%p I am still somewhat at odds whether to actually do this or leave it for the machine level (mom).
%h2#static-type-review Static Type review
%p
To make the caching possible, the cache key - value association has to be constant.
Off course in oo systems the class of an object is constant and so we could just use that.
But in ruby you can change the class, add instance variables or add/remove/change methods,
and so the class as a key and the method as value is not correct over time.
%p
In rubyx, an object has a type, and its type can change. But a type can never change. A type refers
to the class that it represented at the time of creation. Conversely a class carries an instance
type, which is the type of new instances that get created. But when variables or methods are added
or removed from the class, a new type is created. Type instances never change. Method implementations
are attached to types, and once compiled, never changed either.
%p
Thus using the objects type as cache key and the method as its value will stay correct over time.
And the double bonus of this is that it takes care of both objects of different classes (as those will have different type for sure), but also objects of the same class, at different times, when
eg a method with the same name has been added. Those objects will have different type too, and
thus experience a cache miss and have their correct method found.
%h2#up-next Up next
%p
More grunt-work. Now that Vool replaces the ast the code from rubyx/passes has to be “ported” to use it. That means:
\- class extraction and class object creation
\- method extraction and creation
\- type creation by ivar analysis
\- frame creation by local variable analysis