You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by "Marshall, Simon" <Si...@misys.com> on 2016/01/21 16:39:47 UTC

A @CompileStatic for class members

Hi all, I'd like class field access in specific classes to be without groovy magic, so that it is as fast as possible.  I guess this means disabling runtime metaprogramming for specific classes (or fields).

At first, the http://docs.groovy-lang.org/latest/html/api/groovy/transform/CompileStatic.html annotation looked like it might do the trick, as it mentions class properties being covered.  It certainly works for class methods, but in my testing it didn't make any difference to class members.  Setting the field "directly" (ie, via t.member below) is still an order of magnitude slower than using its implicit setter directly (ie, t.setMember()).  Similarly for field access vs getter.  That annotation does not seem to cover field access or assignment.

So, is there a way to make field access as quick as using the implicit getter/setter?  Thanks, Simon.

For example, on my 3+GHz Xeon with jdk1.8.0_66/groovy-all-2.4.3, the below code gives me:

t.member = val          34.577
t.setMember(val)        3.529
t.with { member = val } 127.922

package test

import groovy.transform.CompileStatic

@CompileStatic
class Test {
    String member
}

String val = 'test'
Test t = new Test()

for (long i = 0; i < 100 * 1000 * 1000; ++i) {
    t.setMember(val)
}

long time = 1000 * 1000 * 1000
long beg = System.currentTimeMillis()
for (long i = 0; i < time; ++i) {
    t.member = val
}
println('t.member = val\t\t'+(System.currentTimeMillis() - beg) / 1000.0)

beg = System.currentTimeMillis()
for (long i = 0; i < time; ++i) {
    t.setMember(val)
}
println('t.setMember(val)\t'+(System.currentTimeMillis() - beg) / 1000.0)

beg = System.currentTimeMillis()
for (long i = 0; i < time; ++i) {
    t.with { member = val }
}
println('t.with { member = val }\t'+(System.currentTimeMillis() - beg) / 1000.0)

"Misys" is the trade name of the Misys group of companies. This email and any attachments have been scanned for known viruses using multiple scanners. This email message is intended for the named recipient only. It may be privileged and/or confidential. If you are not the named recipient of this email please notify us immediately and do not copy it or use it for any purpose, nor disclose its contents to any other person. This email does not constitute the commencement of legal relations between you and Misys. Please refer to the executed contract between you and the relevant member of the Misys group for the identity of the contracting party with which you are dealing.

Re: A @CompileStatic for class members

Posted by Jochen Theodorou <bl...@gmx.org>.

On 21.01.2016 17:31, Winnebeck, Jason wrote:
> What surprises me is that accessing by property is a lot slower than
> setter. I suppose I figured once Groovy fully initialized the MOP and
> call site that t.member = 1 would perform essentially exactly the same
> as t.setMember(1).

I think there is no callsite caching for setting properties... well, for 
the normal callsite aching... the indy version has it.

Then again, if you are in an open block, like in the example using with, 
there is no way to do caching at all. The Closure can always give it a 
different meaning.


bye Jochen

RE: A @CompileStatic for class members

Posted by "Winnebeck, Jason" <Ja...@windstream.com>.
What surprises me is that accessing by property is a lot slower than setter. I suppose I figured once Groovy fully initialized the MOP and call site that t.member = 1 would perform essentially exactly the same as t.setMember(1).

However, this test might have the normal micro-benchmark faults, since the side-effects are not used, we can’t really predict what the JIT might do (such as eliminating some instructions entirely), or triggering a method optimization on the script’s run method after the first for loop but before the second (I don’t remember precisely how JIT works when you are not re-entering the method continuously, but I don’t think every instruction is a possible point where it can jump to the JIT-ed version). I wonder if using a tool like JMH would change the results.

Jason

From: Cédric Champeau [mailto:cedric.champeau@gmail.com]
Sent: Thursday, January 21, 2016 10:52 AM
To: users@groovy.apache.org
Subject: Re: A @CompileStatic for class members

Your benchmark is wrong, because the caller is not statically compiled. What you have statically compiled is the `Test` class, but the caller is not. So when you do `t.setMember(..)`, the call is dynamic. It's the caller that you have to statically compile (or both, ideally).

2016-01-21 16:39 GMT+01:00 Marshall, Simon <Si...@misys.com>>:
Hi all, I’d like class field access in specific classes to be without groovy magic, so that it is as fast as possible.  I guess this means disabling runtime metaprogramming for specific classes (or fields).

At first, the http://docs.groovy-lang.org/latest/html/api/groovy/transform/CompileStatic.html annotation looked like it might do the trick, as it mentions class properties being covered.  It certainly works for class methods, but in my testing it didn’t make any difference to class members.  Setting the field “directly” (ie, via t.member below) is still an order of magnitude slower than using its implicit setter directly (ie, t.setMember()).  Similarly for field access vs getter.  That annotation does not seem to cover field access or assignment.

So, is there a way to make field access as quick as using the implicit getter/setter?  Thanks, Simon.

For example, on my 3+GHz Xeon with jdk1.8.0_66/groovy-all-2.4.3, the below code gives me:

t.member = val          34.577
t.setMember(val)        3.529
t.with { member = val } 127.922

package test

import groovy.transform.CompileStatic

@CompileStatic
class Test {
    String member
}

String val = 'test'
Test t = new Test()

for (long i = 0; i < 100 * 1000 * 1000; ++i) {
    t.setMember(val)
}

long time = 1000 * 1000 * 1000
long beg = System.currentTimeMillis()
for (long i = 0; i < time; ++i) {
    t.member = val
}
println('t.member = val\t\t'+(System.currentTimeMillis() - beg) / 1000.0)

beg = System.currentTimeMillis()
for (long i = 0; i < time; ++i) {
    t.setMember(val)
}
println('t.setMember(val)\t'+(System.currentTimeMillis() - beg) / 1000.0)

beg = System.currentTimeMillis()
for (long i = 0; i < time; ++i) {
    t.with { member = val }
}
println('t.with { member = val }\t'+(System.currentTimeMillis() - beg) / 1000.0)

"Misys" is the trade name of the Misys group of companies. This email and any attachments have been scanned for known viruses using multiple scanners. This email message is intended for the named recipient only. It may be privileged and/or confidential. If you are not the named recipient of this email please notify us immediately and do not copy it or use it for any purpose, nor disclose its contents to any other person. This email does not constitute the commencement of legal relations between you and Misys. Please refer to the executed contract between you and the relevant member of the Misys group for the identity of the contracting party with which you are dealing.


----------------------------------------------------------------------
This email message and any attachments are for the sole use of the intended recipient(s). Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message and any attachments.

Re: A @CompileStatic for class members

Posted by Cédric Champeau <ce...@gmail.com>.
Your benchmark is wrong, because the caller is not statically compiled.
What you have statically compiled is the `Test` class, but the caller is
not. So when you do `t.setMember(..)`, the call is dynamic. It's the caller
that you have to statically compile (or both, ideally).

2016-01-21 16:39 GMT+01:00 Marshall, Simon <Si...@misys.com>:

> Hi all, I’d like class field access in specific classes to be without
> groovy magic, so that it is as fast as possible.  I guess this means
> disabling runtime metaprogramming for specific classes (or fields).
>
>
>
> At first, the
> http://docs.groovy-lang.org/latest/html/api/groovy/transform/CompileStatic.html
> annotation looked like it might do the trick, as it mentions class
> properties being covered.  It certainly works for class methods, but in my
> testing it didn’t make any difference to class members.  Setting the field
> “directly” (ie, via t.member below) is still an order of magnitude slower
> than using its implicit setter directly (ie, t.setMember()).  Similarly for
> field access vs getter.  That annotation does not seem to cover field
> access or assignment.
>
>
>
> So, is there a way to make field access as quick as using the implicit
> getter/setter?  Thanks, Simon.
>
>
>
> For example, on my 3+GHz Xeon with jdk1.8.0_66/groovy-all-2.4.3, the below
> code gives me:
>
>
>
> t.member = val          34.577
>
> t.setMember(val)        3.529
>
> t.with { member = val } 127.922
>
>
>
> *package* test
>
>
>
> *import* groovy.transform.CompileStatic
>
>
>
> @CompileStatic
>
> *class* Test {
>
>     String member
>
> }
>
>
>
> String val = 'test'
>
> Test t = *new* Test()
>
>
>
> *for* (*long* i = 0; i < 100 * 1000 * 1000; ++i) {
>
>     t.setMember(val)
>
> }
>
>
>
> *long* time = 1000 * 1000 * 1000
>
> *long* beg = System.*currentTimeMillis*()
>
> *for* (*long* i = 0; i < time; ++i) {
>
>     t.member = val
>
> }
>
> println('t.member = val\t\t'+(System.*currentTimeMillis*() - beg) / 1000.0
> )
>
>
>
> beg = System.*currentTimeMillis*()
>
> *for* (*long* i = 0; i < time; ++i) {
>
>     t.setMember(val)
>
> }
>
> println('t.setMember(val)\t'+(System.*currentTimeMillis*() - beg) / 1000.0
> )
>
>
>
> beg = System.*currentTimeMillis*()
>
> *for* (*long* i = 0; i < time; ++i) {
>
>     t.*with* { member = val }
>
> }
>
> println('t.with { member = val }\t'+(System.*currentTimeMillis*() - beg)
> / 1000.0)
>
>
> "Misys" is the trade name of the Misys group of companies. This email and
> any attachments have been scanned for known viruses using multiple
> scanners. This email message is intended for the named recipient only. It
> may be privileged and/or confidential. If you are not the named recipient
> of this email please notify us immediately and do not copy it or use it for
> any purpose, nor disclose its contents to any other person. This email does
> not constitute the commencement of legal relations between you and Misys.
> Please refer to the executed contract between you and the relevant member
> of the Misys group for the identity of the contracting party with which you
> are dealing.
>

Re: A @CompileStatic for class members

Posted by Jochen Theodorou <bl...@gmx.org>.

On 21.01.2016 16:39, Marshall, Simon wrote:
[...]
> For example, on my 3+GHz Xeon with jdk1.8.0_66/groovy-all-2.4.3, the
> below code gives me:
>
> t.member = val          34.577
>
> t.setMember(val)        3.529
>
> t.with { member = val } 127.922
>
> *package*test
>
> *import*groovy.transform.CompileStatic
>
> @CompileStatic
>
> *class*Test {
>
>      String member
>
> }
>
> String val = 'test'
>
> Test t = *new*Test()
>
> *for*(*long*i = 0; i < 100* 1000* 1000; ++i) {
>
>      t.setMember(val)
>
> }

this code does statically compile Test, but not code, that uses test. If 
you meassure t.setMember(val) here, it will be the dynamic code you 
test, not static compiled code. In other words: the method that does 
your field access must be annotated by @CompileStatic.. smae with the 
other variants of course. In your code I would actually expect no real 
difference in numbers even if you left out the @CompilStatic on Test, 
because the setter, does essentially what Java would do (I think there 
is no meta class init in there even)

and of course there is the issue of you doing all the tests together. On 
the JVM you have something like three states to consider... (1) warmup, 
(2) hot (3) deoptimizing.

You usually start with a warmup, get into a hot phase, which might be 
followed by an deoptimization, which then again leads to a warmup. 
Warmup means the optimizations are applied, the thresholds are crossed. 
So if you meassure the warmup, you do not meassure peak performance, you 
meassure all the optimization work and running non-optimized code. The 
peak performance you get if you meassure only in the hot phase which is 
usually done by running the code first several thousands of times (some 
thresholds are quite high) and then you do the loop with the actual 
meassurement.

Now.. if you do access in a different way, the JVM may have to throw 
away optimizations it did before. This can result in significantly 
slower code than even in the warmup phase... meanig it makes very much 
sense to give the JVM some time to warmup again between meassurements.

If you want to "get rid" of the trouble of doing all those things, you 
could consider using JMH, which does not only an arbitrary warmup, it 
does even check for the times to get stable.

bye Jochen

RE: A @CompileStatic for class members

Posted by "Marshall, Simon" <Si...@misys.com>.
Thanks to all who responded on this and also to those that suggested JMH to give a more accurate sense of performance.

Now I understand it, it’s obvious, there is dynamic code in the caller at the call site.  Duh.  Yes, annotating the caller gives me what I want, though interestingly makes little difference to t.@member = val.  I guess that is because it is forcing the use of dynamic code at the call site.

Simon.

From: Alessio Stalla [mailto:alessiostalla@gmail.com]
Sent: 21 January 2016 15:52
To: users@groovy.apache.org
Subject: Re: A @CompileStatic for class members

I believe you should @CompileStatic the code doing t.member = val as well.

On 21 January 2016 at 16:47, Schalk Cronjé <ys...@gmail.com>> wrote:
No idea if this will improve it, but have you tried the following?

  t.@member<ma...@member> = val



"Misys" is the trade name of the Misys group of companies. This email and any attachments have been scanned for known viruses using multiple scanners. This email message is intended for the named recipient only. It may be privileged and/or confidential. If you are not the named recipient of this email please notify us immediately and do not copy it or use it for any purpose, nor disclose its contents to any other person. This email does not constitute the commencement of legal relations between you and Misys. Please refer to the executed contract between you and the relevant member of the Misys group for the identity of the contracting party with which you are dealing.

Re: A @CompileStatic for class members

Posted by Alessio Stalla <al...@gmail.com>.
I believe you should @CompileStatic the code doing t.member = val as well.

On 21 January 2016 at 16:47, Schalk Cronjé <ys...@gmail.com> wrote:

> No idea if this will improve it, but have you tried the following?
>
>   t.@member = val
>
>
>
> On 21/01/2016 15:39, Marshall, Simon wrote:
>
> Hi all, I’d like class field access in specific classes to be without
> groovy magic, so that it is as fast as possible.  I guess this means
> disabling runtime metaprogramming for specific classes (or fields).
>
>
>
> At first, the
> http://docs.groovy-lang.org/latest/html/api/groovy/transform/CompileStatic.html
> annotation looked like it might do the trick, as it mentions class
> properties being covered.  It certainly works for class methods, but in my
> testing it didn’t make any difference to class members.  Setting the field
> “directly” (ie, via t.member below) is still an order of magnitude slower
> than using its implicit setter directly (ie, t.setMember()).  Similarly for
> field access vs getter.  That annotation does not seem to cover field
> access or assignment.
>
>
>
> So, is there a way to make field access as quick as using the implicit
> getter/setter?  Thanks, Simon.
>
>
>
> For example, on my 3+GHz Xeon with jdk1.8.0_66/groovy-all-2.4.3, the below
> code gives me:
>
>
>
> t.member = val          34.577
>
> t.setMember(val)        3.529
>
> t.with { member = val } 127.922
>
>
>
> *package* test
>
>
>
> *import* groovy.transform.CompileStatic
>
>
>
> @CompileStatic
>
> *class* Test {
>
>     String member
>
> }
>
>
>
> String val = 'test'
>
> Test t = *new* Test()
>
>
>
> *for* (*long* i = 0; i < 100 * 1000 * 1000; ++i) {
>
>     t.setMember(val)
>
> }
>
>
>
> *long* time = 1000 * 1000 * 1000
>
> *long* beg = System.*currentTimeMillis*()
>
> *for* (*long* i = 0; i < time; ++i) {
>
>     t.member = val
>
> }
>
> println('t.member = val\t\t'+(System.*currentTimeMillis*() - beg) / 1000.0
> )
>
>
>
> beg = System.*currentTimeMillis*()
>
> *for* (*long* i = 0; i < time; ++i) {
>
>     t.setMember(val)
>
> }
>
> println('t.setMember(val)\t'+(System.*currentTimeMillis*() - beg) / 1000.0
> )
>
>
>
> beg = System.*currentTimeMillis*()
>
> *for* (*long* i = 0; i < time; ++i) {
>
>     t.*with* { member = val }
>
> }
>
> println('t.with { member = val }\t'+(System.*currentTimeMillis*() - beg)
> / 1000.0)
>
>
> "Misys" is the trade name of the Misys group of companies. This email and
> any attachments have been scanned for known viruses using multiple
> scanners. This email message is intended for the named recipient only. It
> may be privileged and/or confidential. If you are not the named recipient
> of this email please notify us immediately and do not copy it or use it for
> any purpose, nor disclose its contents to any other person. This email does
> not constitute the commencement of legal relations between you and Misys.
> Please refer to the executed contract between you and the relevant member
> of the Misys group for the identity of the contracting party with which you
> are dealing.
>
>
>
> --
> Schalk W. Cronjé
> Twitter / Ello / Toeter : @ysb33r
>
>

Re: A @CompileStatic for class members

Posted by Schalk Cronjé <ys...@gmail.com>.
No idea if this will improve it, but have you tried the following?

   t.@member = val



On 21/01/2016 15:39, Marshall, Simon wrote:
>
> Hi all, I’d like class field access in specific classes to be without 
> groovy magic, so that it is as fast as possible.  I guess this means 
> disabling runtime metaprogramming for specific classes (or fields).
>
> At first, the 
> http://docs.groovy-lang.org/latest/html/api/groovy/transform/CompileStatic.html 
> annotation looked like it might do the trick, as it mentions class 
> properties being covered.  It certainly works for class methods, but 
> in my testing it didn’t make any difference to class members.  Setting 
> the field “directly” (ie, via t.member below) is still an order of 
> magnitude slower than using its implicit setter directly (ie, 
> t.setMember()).  Similarly for field access vs getter.  That 
> annotation does not seem to cover field access or assignment.
>
> So, is there a way to make field access as quick as using the implicit 
> getter/setter?  Thanks, Simon.
>
> For example, on my 3+GHz Xeon with jdk1.8.0_66/groovy-all-2.4.3, the 
> below code gives me:
>
> t.member = val          34.577
>
> t.setMember(val)        3.529
>
> t.with { member = val } 127.922
>
> *package*test
>
> *import*groovy.transform.CompileStatic
>
> @CompileStatic
>
> *class*Test {
>
>     String member
>
> }
>
> String val = 'test'
>
> Test t = *new*Test()
>
> *for*(*long*i = 0; i < 100* 1000* 1000; ++i) {
>
>     t.setMember(val)
>
> }
>
> *long*time = 1000* 1000* 1000
>
> *long*beg = System./currentTimeMillis/()
>
> *for*(*long*i = 0; i < time; ++i) {
>
>     t.member= val
>
> }
>
> println('t.member = val\t\t'+(System./currentTimeMillis/() - beg) / 
> 1000.0)
>
> beg = System./currentTimeMillis/()
>
> *for*(*long*i = 0; i < time; ++i) {
>
>     t.setMember(val)
>
> }
>
> println('t.setMember(val)\t'+(System./currentTimeMillis/() - beg) / 
> 1000.0)
>
> beg = System./currentTimeMillis/()
>
> *for*(*long*i = 0; i < time; ++i) {
>
>     t./with/ { member= val }
>
> }
>
> println('t.with { member = val }\t'+(System./currentTimeMillis/() - 
> beg) / 1000.0)
>
> "Misys" is the trade name of the Misys group of companies. This email 
> and any attachments have been scanned for known viruses using multiple 
> scanners. This email message is intended for the named recipient only. 
> It may be privileged and/or confidential. If you are not the named 
> recipient of this email please notify us immediately and do not copy 
> it or use it for any purpose, nor disclose its contents to any other 
> person. This email does not constitute the commencement of legal 
> relations between you and Misys. Please refer to the executed contract 
> between you and the relevant member of the Misys group for the 
> identity of the contracting party with which you are dealing. 


-- 
Schalk W. Cronjé
Twitter / Ello / Toeter : @ysb33r