Quantcast

Groovy performance: return types

classic Classic list List threaded Threaded
36 messages Options
1234
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Groovy performance: return types

Alex Tkachman
Hi All!

Yesterday I commited patch which significanlly improves performance of
math operations on primitives. The idea is following: in the method

def method (int a, double b) {
   a + b
}

we compile call special method NumberModificationInfo.plus (a,b),
which takes int and double as parameters and returns double. The
method looks like

static double plus(int a, double b) {
  if (instance.int_plus_modified)
     return plusSlow(a,b);
  else
     return (double)a + b;
}

static double plusSlow(int a, double b) {
  return ((Number)InvokerHelper.invokeMethod("plus", a, b)).doubleValue();
}

instance.int_plus_modified is global flag tracked during meta class
creation and modification. If such modification happen we go to slow
path - method plusSlow does call via regular Groovy machinery, then
cast result to Number and returns doubleValue of this number. But in
normal situation we call very fast direct bytecode for addition. What
we win is direct call instead of method selection (or call site cache
checking) and several unneded boxing/unboxing we don't need to do. In
fact we help to JIT a lot to perform as fast as possible but keeping
dynamic semantic.

There is only one small downside - if you want to overide Integer.plus
(with EMC or category) you should return Number otherwise you will
have CastException on runtime. You also should know that if for
example you return BigDecimal you might have loss of precision.

My question is it such important price for really great performance.
My answer is no but of course I want to know opinions of other
developers.

In theory Jochen invented nice solution for this problem: we can
compile two versions of each method - optimized and non-optimized -
add call one or another depending on global flags. I think it might
work perfectly and even faster compare to my solution, so we have
option to implement it in the future.

But I want to suggest another approach. Let us have a look on following code

int a(int i, int j) {
   i*i + j*j
}

double a2(int i, int j) {
  a(i,j) * a(j,i)
}

As described above we easy optimize method a. Unfortunately we can't
optimize method a2 because we can assume nothing about return type of
a(i,j). We know in fact which method will be called but we don't know
if it will be monkey patched or not. But IF we can assume that any
LEGAL monkey patching of method a SHOULD return something convertable
to int we are in the very good situation

Instead of a(i,j) we call

int check_and_call_a (int i, intj) {
  if (fastCheckThatMethodAModified())
     return ((Number)ScriptBytecodeAdapter.....).intValue();
  else
     return a(i,j);
}

And if we do that we will really A LOT.

So real question is "IS IT BIG PRICE"?

Best regards
Alex

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

tugwilson

Alex Tkachman wrote
Hi All!

Yesterday I commited patch which significanlly improves performance of
math operations on primitives. The idea is following: in the method

def method (int a, double b) {
   a + b
}

we compile call special method NumberModificationInfo.plus (a,b),
which takes int and double as parameters and returns double. The
method looks like

static double plus(int a, double b) {
  if (instance.int_plus_modified)
     return plusSlow(a,b);
  else
     return (double)a + b;
}

static double plusSlow(int a, double b) {
  return ((Number)InvokerHelper.invokeMethod("plus", a, b)).doubleValue();
}

instance.int_plus_modified is global flag tracked during meta class
creation and modification. If such modification happen we go to slow
path - method plusSlow does call via regular Groovy machinery, then
cast result to Number and returns doubleValue of this number. But in
normal situation we call very fast direct bytecode for addition. What
we win is direct call instead of method selection (or call site cache
checking) and several unneded boxing/unboxing we don't need to do. In
fact we help to JIT a lot to perform as fast as possible but keeping
dynamic semantic.

There is only one small downside - if you want to overide Integer.plus
(with EMC or category) you should return Number otherwise you will
have CastException on runtime. You also should know that if for
example you return BigDecimal you might have loss of precision.

My question is it such important price for really great performance.
My answer is no but of course I want to know opinions of other
developers.
[snip]

And if we do that we will really A LOT.

So real question is "IS IT BIG PRICE"?

The answer, unfortunately, is yes.

The problem is not so much that you constrain the replacement method to return Numeric. The problem is that you forcibly constrain the type of the result of the operation (i.e. int + double must always produce double).

This means that it becomes impossible to implement the graceful handling of overflow (e.g. int + int returns int unless it overflows in which case it returns long). This is a perfectly reasonable thing to want to do and it is already done in several dynamically typed languages.

In fact you can get this speed without having to constrain the return type of the replacement operation in *any* way.

if you define your method

static double plus(int a, double b) throws UseSlowMethodException {
  if (instance.int_plus_modified)
     throw useSlow;  // useSlow is a pre allocated exception
  else
     return a + b;
}

Then you can generate code which tries the fast method, catches the thrown exception and uses the slow method in the catch clause. (or you can just test the int_plus_modified field in the generated code - but that introduces some uncomfortable coupling in my opinion).

You could do a lot better than this is you had a richer MOP - personally I'd advise against this sort of special case optimisiation until you had a major MOP redesign (in Groovy 2.0).



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

Martin C. Martin-2
In reply to this post by Alex Tkachman


Alex Tkachman wrote:
> Hi All!
>
> Yesterday I commited patch which significanlly improves performance of
> math operations on primitives.

Cool Alex!

How about doing it for each statement, rather than each function call?
For example:

 > double a2(int i, int j) {
 >   a(i,j) * a(j,i)
 > }

Could compile to:

double a2(int i, int j) {
   if (fastCheckThatMethodAModified() ||
      NumberModificationInfo.int_times_modified)
   {
      Do it the slow way
   } else {
      We know all functions & their return types, so call them directly
      without boxing/unboxing.
   }
}

So your first example becomes:

> def method (int a, double b) {
>    a + b
> }

Object method(int a, double b) {
    if (NumberModificationInfo.int_plus_modified)
       return InvokerHelper.invokeMethod("plus", a, b);
    else
       return new Double(a + b);
}

That way, we have fast execution without any limitations.  It's in
between your function call level and blackdrag's method level.

What do you think?

Best,
Martin

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

tugwilson
In reply to this post by Alex Tkachman
I had a very quick look at the implementation:

1/ don't all your boolean flags have to be volatile?

2/ How do you deal with Categories modifying these behaviors?

John Wilson
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

Alex Tkachman
On Sat, Apr 26, 2008 at 10:55 PM, tugwilson <[hidden email]> wrote:
>
>  I had a very quick look at the implementation:
>
>  1/ don't all your boolean flags have to be volatile?
>

I don't think so. I don't care too much if changes done in another
thread will be caught a bit later.

>  2/ How do you deal with Categories modifying these behaviors?
>

You are right, it is not implemented yet, but with current
implementation of categories it is easy.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

Alex Tkachman
In reply to this post by tugwilson
>
>  The problem is not so much that you constrain the replacement method to
>  return Numeric. The problem is that you forcibly constrain the type of the
>  result of the operation (i.e. int + double must always produce double).
>
>  This means that it becomes impossible to implement the graceful handling of
>  overflow (e.g. int + int returns int unless it overflows in which case it
>  returns long). This is a perfectly reasonable thing to want to do and it is
>  already done in several dynamically typed languages.
>

I strongly believe that it is completely unreasonable behavior. It is
unpragmatic compare to cost to pay for that. 99.99999% of Groovy users
doesn't need it. Just my opinion.

>  In fact you can get this speed without having to constrain the return type
>  of the replacement operation in *any* way.
>
>  if you define your method
>
>  static double plus(int a, double b) throws UseSlowMethodException {
>
>   if (instance.int_plus_modified)
>      throw useSlow;  // useSlow is a pre allocated exception
>   else
>      return a + b;
>  }
>
>  Then you can generate code which tries the fast method, catches the thrown
>  exception and uses the slow method in the catch clause. (or you can just
>  test the int_plus_modified field in the generated code - but that introduces
>  some uncomfortable coupling in my opinion).
>

I don't understand your approach.

int a, b, c

(a+b) * c

I want to use fast operations for both + and *. For that I need to
know for sure that (a+b) will be evaluated to int. In your approach I
can do it for + but not for *. Do I miss something?

>  You could do a lot better than this is you had a richer MOP - personally I'd
>  advise against this sort of special case optimisiation until you had a major
>  MOP redesign (in Groovy 2.0).
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

tugwilson

Alex Tkachman wrote
>
>  The problem is not so much that you constrain the replacement method to
>  return Numeric. The problem is that you forcibly constrain the type of the
>  result of the operation (i.e. int + double must always produce double).
>
>  This means that it becomes impossible to implement the graceful handling of
>  overflow (e.g. int + int returns int unless it overflows in which case it
>  returns long). This is a perfectly reasonable thing to want to do and it is
>  already done in several dynamically typed languages.
>

I strongly believe that it is completely unreasonable behavior. It is
unpragmatic compare to cost to pay for that. 99.99999% of Groovy users
doesn't need it. Just my opinion.
You obviously know more about what Groovy users want than I do :)

However it is perfectly possible to provide the performance improvement without imposing arbitrary and unacceptable restrictions.

It's unfortunate that you have committed these changes without discussing them on groovy-dev. If you don't do that what is the point of having a public dev mailing list?
Alex Tkachman wrote
>  In fact you can get this speed without having to constrain the return type
>  of the replacement operation in *any* way.
>
>  if you define your method
>
>  static double plus(int a, double b) throws UseSlowMethodException {
>
>   if (instance.int_plus_modified)
>      throw useSlow;  // useSlow is a pre allocated exception
>   else
>      return a + b;
>  }
>
>  Then you can generate code which tries the fast method, catches the thrown
>  exception and uses the slow method in the catch clause. (or you can just
>  test the int_plus_modified field in the generated code - but that introduces
>  some uncomfortable coupling in my opinion).
>

I don't understand your approach.

int a, b, c

(a+b) * c

I want to use fast operations for both + and *. For that I need to
know for sure that (a+b) will be evaluated to int. In your approach I
can do it for + but not for *. Do I miss something?
You can compile this perfectly well using the mechanism I suggested.

You end up with slightly verbose code but the performance win is worth it.

John Wilson

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

tugwilson
In reply to this post by Alex Tkachman

Alex Tkachman wrote
On Sat, Apr 26, 2008 at 10:55 PM, tugwilson <tug@wilson.co.uk> wrote:
>
>  I had a very quick look at the implementation:
>
>  1/ don't all your boolean flags have to be volatile?
>

I don't think so. I don't care too much if changes done in another
thread will be caught a bit later.
You really should care (I do, and I use this language)

You really have to take multi threaded support seriously.

Alex Tkachman wrote
>  2/ How do you deal with Categories modifying these behaviors?
>

You are right, it is not implemented yet, but with current
implementation of categories it is easy.
That's good - when will it be implemented?

John Wilson
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

Alex Tkachman
In reply to this post by tugwilson
>
>  You can compile this perfectly well using the mechanism I suggested.
>
>  You end up with slightly verbose code but the performance win is worth it.
>

Seriously I don't understand.
Could you please explain what will be compiled code for (a+b)+c

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Groovy performance: return types

Alex Tkachman
In reply to this post by tugwilson
>
>  You really should care (I do, and I use this language)
>
>  You really have to take multi threaded support seriously.
>
>

I take multi threading support very seriously but I think in this case
it is not a problem.

>
>  Alex Tkachman wrote:
>  >
>  >
>  >>  2/ How do you deal with Categories modifying these behaviors?
>  >>
>  >
>  > You are right, it is not implemented yet, but with current
>  > implementation of categories it is easy.
>  >
>
>  That's good - when will it be implemented?
>

Not later than 1.6 final

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


1234
Loading...