On Sat, Mar 28, 2009 at 6:08 PM, Marvin Humphrey <[email protected]> wrote: > On Fri, Mar 27, 2009 at 08:21:54AM -0400, Michael McCandless wrote: > >> what if the Lucy obj never held onto the reference to the mirror host obj? >> (And the Lucy obj did its own reference counting, separately from the >> host's GC). > > What you've laid out is *exactly* the same scheme Peter Karman used in SWISH3, > the same scheme Dave Balmain used in Ferret, and the same scheme I used in > KinoSearch for objects which subclass FastObj.
OK, excellent. I'm playing catch up over here... > Since this algorithm seems to be an intuitive winner, and since I'd like to > just solve this problem and move on, I took a stab at converting KS over to > using it exclusively. Switching over to something that everybody else seems > to > grok would be great -- indeed, I was rooting for my cached-host-object algo to > lose. Sacrificing a little performance for the sake of an implementation that > other people might have less difficulty understanding, maintaining, and > modifying would be just fine with me. > > The results were disappointing. The cached-host-object algo has certain > advantages we should be reluctant to discard. Interesting. >> When we cross the bridge, Lucy to host, we would make a new host >> wrapper obj each time. These host mirror objs are very lightweight >> wrappers, right? > > FWIW, the cached-host-object algo wins the tight loop performance contest. Which makes sense, but how often are such loops [expected to be] used in practice? EG we already thought Collector inside host would be doable but expected poor performance. > Below my sig, you'll find a patch that applies against KinoSearch svn revision > 4361, and a benchmarking script. The patch gives TermQuery a member var named > "thing", plus Set_Thing() and Get_Thing() methods. The loop compares the > performance of calling $term_query->get_thing from Perl-space when "thing" is > a > VArray (which subclasses FastObj) vs. when "thing" is a Hash (which subclasses > Obj). > > The only difference in those loops is the behavior of FastObj_to_host (which > creates a new host object every time) vs. Obj_to_host (which exploits a > cached > host object). The results: > > mar...@smokey:~/projects/ks4361/perl $ perl -Mblib time_callbacks.pl > Rate no_cached_object cached_object > no_cached_object 1.93/s -- -66% > cached_object 5.71/s 196% -- > > On its own, this performance gap probably doesn't justify sacrificing clarity > of implementation. It only matters for host-language subclassing, where > performance is going to be heavily compromised anyway -- and if your method > does something non-trivial, this effect is going to get swamped. How much work is being done when you create a new host-wrapper object? Could you somehow re-use such objects? (Sort of like how when you go to a children's museum, to the water section, they provide a bunch of yellow frocks that the kids can wear when they play with the water, and return when they are done). I want to make sure we've fully explored performance improvements for the "simpler" non-cached-host-object approach before making the decision. > However... > >> But what do we lost by not retaining a permanent host obj wrapper? > > We lose convenient use of the "inside-out-object" pattern. :( Neat.... I've seen a similar approach (though likely someone has given / would give it a different named pattern) applied to fine-grained locks, too, where you want to make a lock for objects where you may have zillions of them, so instead of actually making a lock per object, you make a single shared array of locks and each object is hashed into one lock in that array. In your example, shouldn't/couldn't the hash key somehow be the id of the *Lucy* object, not of its fleeting host wrapper obj? I'm not sure how easy this'd be ($$self likely is not something you could override to return the pointer to the Lucy object instead of the host wrapper?). And then when the Lucy obj is really destroyed, you'd have to cross the bridge and call DESTROY in the host. >> (Note that if it's a real performance problem, then in certain cases >> you could eg explicitly retain a reference at the top of the loop, do >> the loop, then drop the reference at the end). I view this as being similar to the "though shalt not create cycles" approach. We are expecting the user (not end-user) to understand some limitations of how Lucy works and to properly work around them. It's an intentionally leaky abstraction... Mike
