On Tue, Aug 4, 2009 at 11:02 AM, Mark Addleman<mark_addle...@bigfoot.com> wrote: > > I think there may be a somewhat straightforward solution to improving > Clojure's performance when passing primitives between functions. > Here's my understanding of the problem: The IFn interface is a series > of invoke method signatures that take a number of java.lang.Objects as > parameters and returns a java.lang.Object. Primitives can't be passed > this way and it would be a silly explosion of code to create the > methods signatures in IFn that included primitives. The performance > characteristcs of "boxing" primitives relies on the JVM's performance > of allocating and deallocating objects. > > My analysis: The JVM is awfully good at handling transient objects > like the ones used for boxing, but no matter how good it is, it's > always better if the work needn't be done in the first place. > > Here's my notion (bordering on brain fart): Clojure does its own > primitive boxing using one element arrays. Each thread has a > dynamically growable threadlocal cache of one-element arrays for each > primitive that needs to pass through the stack. At the call site, the > compiler generates code to "allocate" the one element array > threadlocal of the appropriate type and stuffs the primitive into the > array. Since the array itself is a java.lang.Object, it can pass > through the IFn interface without a problem. At the target site, it > gets a bit more tricky. If the object is type-coerced to a primitive, > it's simply a matter of pulling the first (and only) element out of > the array. If the object is treated as a java.lang.Number, Clojure > must create the correct java object on the fly. I don't think this > logic is very hard, but I can see how it might get tricky and have > lots of cases depending on how the compiler is designed. > > Once the threadlocal cache is of sufficient size (I *think* the > largest it ever needs to grow is the largest number of primitives in > any method signature), the boxing costs fall into two categories: In > the case where the object is coerced to a primitive, no tax is placed > on the garbage collector. The cost is obtaining an available > primitive array, stuffing the primtive into the array, reading the > primitive out of the array and putting the array back into the cache. > This should be substantially faster than standard Java boxing. In the > case where the object is treated as a java.lang.Number, the cost is > obtaining an available primitive array, stuffing the primtive into the > array, constructing the appropriate object and returning the the array > to the cache.
This approach is doomed on so many fronts. First, there is zero reason to believe your arrays will be any more efficient than Integer etc. Second, the caching is a barrier to escape analysis. Since rather than seeing a freshly-boxed integer 42, which can be optimized away, the compiler sees a branch into cache lookup code that returns something the compiler cannot know, thus it can't get rid of the lookup. Cliff Click sees this problem today, with Integer/valueOf(int), which does caching. etc FYI, I'm going with the "silly explosion of code to create the methods signatures in IFn that included primitives", since the only 2 primitives one needs to support are long and double. Stay tuned... Rich --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---