Charles Oliver Nutter schrieb: [...] > JRuby has had an inline cache for over a year, and *actively* flushes > call sites.
We had in Groovy a per class cache for method lookups. But the cache was too slow (the generation of the key did cost too much) and if we have inline caches we don't need that kind of caches. Now I don't what kind of relation your cache uses. I guess you use a (name,argument number) type of key and then react based on the receiver in the general case and maybe a precalculated number in special cases. If that is right, then of course you have to check all caches if a class changes... combined with synchronization this looks like something that could be better if someone had a bright idea > We keep references to them in a JRuby-runtime-global map, > and when changes happen to the hierarchies from which they came we tell > all sites known to have cached that method to flush themselves. This > works very well so far, but there's a very small chance that under high > concurrent load, if a method is simultaneously being called by one > thread and redefined by another a cache could get stuck with a stale > entry and potentially never flushed. I have not been able to produce > this effect experimentally and it has never been reported, so we have > not made substantial efforts to replace the active flushing logic. hehe... we once had a bug report that had its cause in a method object (a custom object wrapping a reflection method object) existing but not being intialized yet... that was a pretty tough one to debug, because of course we where not able to reproduce the problem, because it depends on too many factors that are not equal on the developer machine. NBut maybe class changes do not appear rapidly enough. > Are you using a version number now? We have attempted to install a > version number but it's complicated by several factors: we use it in some cases, yes, but there are factors in Groovy that allow that compared to Ruby maybe. > Given class X from which we cached the method: > - A change anywhere in X or its superclasses must trigger X's version to > increment. > - A change anywhere in any modules included into X or any of its > superclasses must trigger X's version to increment. > > These two combined mean we have to have both downreferences from parent > to child (necessarily weak references) as well as backreferences from > included modules to the pseudoclasses used to mix them in. I see the difference.... in Groovy a super class change may not affect childs. We thought about changing that, but it is not done yet... not really at last. Anyway... I guess you need references from the childs to the parents and you need to store method and the receiver class... which goes well for a call site cache, but maybe not for your inline cache... but I guess that is nothing that can't be broken through. Anyway, instead of using the method's class, the receiver pseudo class needs to be used. And to check the version number the pseudo class needs to know also the version number of its parent and if a version check is done, then it needs to check the parent too... of course that means that each call a lot of version checks are done... which is probably bad and a bit complicated. > An alternative, which I implemented as an experiment, is to instead > calculate the version each time, compositing version information > recursively through the hierarchy. If the per-class cost is low enough, > this does improve performance slightly over a raw search for the method > in all superclasses' method maps. But it doesn't appear to help enough > to warrant the complexity. you mean the way I just suggested here? Hmm... >> For example in a call site cache, you can throw the method out, as soon >> as you revisit the cache. If the method is cached in a memory sensitive >> way, then there is no need to do that more early I think. If your cache >> happens to be in the class itself, then invalidating the cache as soon >> as the class is mutated might be an idea. If you have some kind of >> inline cache, then you usually have something based on method names and >> the cache has some kind of structure that reacts to what class is in >> use. Well... I would say that once you found your case you have to check >> the version again. > > The benefit of throwing it out early is reducing the guard cost at the > site itself. If we can actively clean out sites there's no need to > re-check anything other than incoming object type == previously cached > object type. true > We have been looking at various strategies, including per-class caches, > global caches, class serial/version numbers, and so far have been > thwarted trying to implement them due to the extreme mutability of > Ruby's class hierarchy. hmm... I just had a new idea, but it involves to create many objects and I am not sure this helps here much... anyway.. you wrote: >> Given class X from which we cached the method: >> - A change anywhere in X or its superclasses must trigger X's version to >> increment. >> - A change anywhere in any modules included into X or any of its >> superclasses must trigger X's version to increment. ok let me try to develop a new idea here.... let us say that each time you mutate a class in any way you create a new class an the old class will be flagged as dirty. Let us also say that there is some kind of ClassHandle that stores a reference to the current class. Now a super class may have a listener like structure for each child class. If the super class changes it notifies each child by marking them as dirty and then marks itself as dirty too... I guess this needs to be synchronized. Anyway, the handle is constant and points now to a dirty class. If you do any action involving this class, then you need to create the class first new to get a version that is not dirty. his new version will then again register itself at the parent and update the method handle. In the inline cache you store the tuple receiver class (the goal of the method handle) the method handle, as well as the handle. Then you would have to check if handle.reference==receiverClass && !handle.reference.isDirty(). If the update did already happen, then that part of the cache is no longer called. I don't know if you have timeouts for your caches or if you keep track which branches has been called to ensure the cache does not blow up... or you change the order to !handle.reference.isDirty() && handle.reference==receiverClass and in case of isDirty you change the cache. method objects do not use the real class, instead they use the handle, meaning that you don't need to recreate them when a class changes. Well I guess that is not really different from the version idea since I now use a reference instead of a number, but it should be interesting to know how this affects the performance. the advantage is that you never need to go through all caches and update them. You also do not need to update classes that are not used. The disadvantage is that you still have synchronization a lot. >> What is bad about the version idea? In a multi threaded environment you >> have the problem, that each time you ask for the class version you have >> to access synchronized data... There are possible ways around it if you >> have thread locals that get informed about class changes in a event like >> system. A bit lame probably ;) > > I think a volatile version number would be sufficient, and only updating > the version number would need to be synchronized. yes. I included that in synchronization. > There's no reason > accessing the version number would have to be synchronized since field > access is an atomic operation. field access yes, object creation not. So in case of a primitive number there is of course no problem ;) Still it is not a CPU cache friendly version, requiring memory synchronization for each method call. bye blackdrag -- Jochen "blackdrag" Theodorou The Groovy Project Tech Lead (http://groovy.codehaus.org) http://blackdragsview.blogspot.com/ http://www.g2one.com/ --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "JVM Languages" group. To post to this group, send email to jvm-languages@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en -~----------~----~----~----~------~----~------~--~---