soon I want to look at a simple x+y. But those x+y can have various
types and I don't think that for the case of having int for x and y it
is really good to have a method like this:
static int plus(Integer x, Integer y){return x+y;}
and I wonder further if I should really provide methods for all
combinations of byte, char, int, short even though they are more or less
handled as int in bytecode. Counting all versions I would have some 49
methods.
What do you guys advise to do here?
bye Jochen
--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org
I don't think the smaller types would gain you much unless you have
behavioral differences you need to express for them. In JRuby, there's
only one integer type and one floating-point type (not counting
arbitrary-precision types).
JRuby doesn't have any static typed numerics for LHS, so we only have
about 20 special paths for math, boolean, and comparison operators
with Object LHS and long or double RHS, which currently are only used
for literals (e.g. a + 1, b < 10.0, etc).
- Charlie
> --
> You received this message because you are subscribed to the Google Groups
> "JVM Languages" group.
> To post to this group, send email to jvm-la...@googlegroups.com.
> To unsubscribe from this group, send email to
> jvm-language...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/jvm-languages?hl=en.
>
> I have been creating specific versions for long and double RHS because
> it does save some overhead to not construct the box or get a cached
> box. I think it's worth it for common cases (int, long, float, double)
> at least.
Hotspot has some optimizations for collapsing box/unbox pairs, as in (int)(Integer)(int)n. I forget when they kick in, sorry.
Meanwhile, you can sometimes arrange for exactly one generic routine to automatically specialize in different caller contexts, based on the types at the call site. That was the outcome of this experiment:
http://blogs.oracle.com/jrose/entry/an_experiment_with_generic_arithmetic
The key is to make the type tests in the generic function be transparent enough to the JIT.
Example:
Object plus(Object x, Object y) { // we expect this function to inline and customize
if (x instanceof Integer && y instanceof Integer) return (int)x + (int)y;
return slowPlus(x, y); // slow path is out-of-line
}
The function must be kept simple to encourage the JIT to inline your fast path, but not get scared away by a complex slow path.
Then, calls like the following will constant-fold the type tests:
x = plus(1, 2); // no type tests left
x = plus(x, 3); // one type test left
x = plus((int)x, 4); // no type tests left
static final MethodHandle plusCustom = (::plus).asType(methodType(Object, int, int)); // or invokedynamic
x = plusCustom.invokeExact((int)x, 5); // no type tests left
> I don't think the smaller types would gain you much unless you have
> behavioral differences you need to express for them. In JRuby, there's
> only one integer type and one floating-point type (not counting
> arbitrary-precision types).
I would try to rely on JIT customization of a generic.
If there were measurable bottlenecks, I would add just a few hand-customized versions.
> JRuby doesn't have any static typed numerics for LHS, so we only have
> about 20 special paths for math, boolean, and comparison operators
> with Object LHS and long or double RHS, which currently are only used
> for literals (e.g. a + 1, b < 10.0, etc).
Likewise, I would think that the (Object,int)Object version of +, etc., would be useful to Jochen.
But only few like this are needed, to hit the sweet spot without overkill.
The motivation for these cases comes mainly from loop control and array indexes. (...In languages where those are your iteration tools; sigh.)
-- John
but will this work with a guarded methodHandle in the chain?
> Meanwhile, you can sometimes arrange for exactly one generic routine to automatically specialize in different caller contexts, based on the types at the call site. That was the outcome of this experiment:
> http://blogs.oracle.com/jrose/entry/an_experiment_with_generic_arithmetic
>
> The key is to make the type tests in the generic function be transparent enough to the JIT.
>
> Example:
>
> Object plus(Object x, Object y) { // we expect this function to inline and customize
> if (x instanceof Integer&& y instanceof Integer) return (int)x + (int)y;
> return slowPlus(x, y); // slow path is out-of-line
> }
Will a method like this:
> public Number addImpl(Number left, Number right) {
> return Integer.valueOf(left.intValue() + right.intValue());
> }
be ok as well? I mean will the JIT still be able to remove the boxing?
> The function must be kept simple to encourage the JIT to inline your fast path,
> but not get scared away by a complex slow path.
in my case that is a different selection. Or are you telling me that it
is better to have it with a fast and a slow path instead of a special
function I replace? I mean I can see that this avoids replacing the
callsite and as such is surely better in those terms, but in Groovy you
can replace int+int and the usage of for example double might be also
used very much.
[...]
> I would try to rely on JIT customization of a generic.
yeah... if those JIT optimization wouldn't be so difficult to see through ;)
[...]
> The motivation for these cases comes mainly from loop control and array indexes.
> (...In languages where those are your iteration tools; sigh.)
Well currently I am working on my fibonacci benchmark with indy and I
try to somehow get the indy implementation near (performance wise) my
primitive optimizations work I have done for 1.8. But since those are
not much slower for this case, there are a lot of strings to be pulled.
from my first test I think I can tell that this method is not ok. While
not being very bad, it does not benefit from removed boxing or such.
Your Version does btw, not compiler, you cannot cast to int, it has to
be Integer. Small mistake I guess. Anyway, your approach seems to work,
but I wonder if that is the right way for me as well.
One odd thing I noticed is that if I make a plus(Integer,Integer):Object
and compare it with a plus(Integer,Integer):Integer method, then this
later version seems to perform quite a bit slower. Sure, yes, the call
site expects Object, so most probably a cast is needed here. But
still... should that really cost my almost 20% of my performance?
My problem with the math operations is not really solved btw. If I use
the fast/slow path approach I get into problems with other types. So
imho that makes sense if you look at int driven programs a lot. But what
if there is also long and double? Soon there will be the point in which
the JIT cannot inline it any more and when this point is reached depends
on more than this function alone I would assume. Inlining is done not
always, even if the method is small. There has to be also "space" for it
in the target. And the bigger and more functions I have like that, the
less often inlining happens.
I hope someone will correct the statement if it is totally off.
But going by this I seem to be better of with specialized functions for
int, double and long... only that in mixed operations I still have the
problem of conversion. My test kind of show my that if
explicitCastArguments is doing the conversion it is not going to be nice.