What you call "the root cause" is literally why I use Ruby.
What you call "the root cause" is literally why I use Ruby.
Call/cc, OTOH, should be dropped from Ruby. It's caused a great deal of pain for the devs, and it's not nearly as important as it is in something like Scheme. They could implement the similar (though not as cool) shift/reset, and have much of the power of call/cc, an easier implementation, and perf that's practical.
Do you have a concrete idea for how to support monkey patching without deoptimisation and have it be fast? If you do, congratulations it's a major breakthrough in language research and you should write a paper.
But I'm just thinking this is utterly nuts: Not that I'm degrading your accomplishments. If it's really that hard a problem, than the fact that you have solved it at all is impressive. But the fact that we have to go to those lengths at all is crazy.
Part of why it seems so nuts to me is probably because I don't know enough about Ruby's internals. As I understand it, A class's methods are kept in a datstructure, associating them with their identity (I think it was a hashtable at some point, although this may have changed) - A vtable, in C++ parlance. When you monkeypatch a method, you're either adding a function to the vtable, or changing which function a vtable entry points to. I don't know why you have to deoptimize to do that. past-me should have probably understood the problem better before he made claims he couldn't back about it, but there's nothing I can about it now, save apologize for being stupid, and ask for an explanation. Which is what I'm doing now.
The only theoretical way I think it would work would be if you knew all the method names ahead of time, gave them all an index, and gave each class a v-table large enough to contain them all. In a Rails application I estimate this might be about 5,000 entries - so 40KB per class. And with singleton classes lots of objects have their own class, so 40KB per object! And as I said that doesn't work anyway as the method names are not known ahead of time - they can be dynamically created and the set of method names is infinite.
But that was method lookup, and that isn't really our problem. I like to use this example from a real Ruby gem to illustrate the bigger problem.
def clamp(min, max, value)
[min, max, value].sort[1]
end
That clamps a value between a min and a max. Using v-tables we could look up the calls to #sort and #[] relatively quickly (well we can't because of the problems with v-tables described above, but I'll keep going) but even with a fast lookup this is still terribly slow code. It creates an array, it sorts it, creating another new array, and then indexes it.To make that code fast we don't just need fast lookup, we need to take the logic from the sort routine and inline it into this method, and we need to remove the loop in sort and specialise it for just three entries, then inline the methods that calls, such as #<=> to compare the values, then specialise the sort code for the fact that we only wanted the middle entry and remove the code for sorting the other entries, and then we need to remove the allocation of the two arrays.
We need to be able to automatically turn that Ruby code in this.
def clamp(min, max, value)
# deoptimise if things aren't as expected
if value < min
min
elsif value > max
max
else
value
end
end
(Pretend that the calls to #> and #< are simple operators like in C). There's not even any lookup there any longer - so lookup was never really part of the problem.And crucially, we need to be able to reverse all that inlining if someone monkey patches any of those methods. V-tables don't help us do any of that, but deoptimisation allows us to be in the middle of executing code like that, and reverse the inlining and the specialisation, allocate the objects we removed the allocation of, and keep going after a monkey patch.
The implementation of Ruby I work on, JRuby+Truffle, does that kind of optimisation for real.
I wrote a thesis about all of this http://chrisseaton.com/phd/
The point I was discussing is actually irrelevant to vtables, it would work just as well with any other method dispatch mechanism, but you probably already knew that, and it has nothing to do with the actual problem.
Out of curiosity, how does Squeak handle this? Or does it just not inline?
Yeah, now that I actually get what deoptimization is, it doesn't seem as crazy.
(Is the syntax worth using call/cc? Eh, good question. But Sinatra's killer feature is, literally, its syntax, so I don't think I get to tell them "don't do it that way." Rails has an ugly workaround for not using call/cc that bites many, many app programmers who forget to return after calling render().)