• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Odd issue - any ideas?

 
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I've run into a very odd performance issue with an enum I've just written. I've added the thread here because this is not a question about improving performance, but more about what people think the compiler (or Eclipse, which I'm using to test) might be doing.

The enum (called Bits) contains a bunch of bit-based utilities for each integer type, so obviously performance is quite important. I've therefore written a test module that runs each method a billion times in order to get an idea of how long they take.

One such method is called bit(index), which returns an int with a bit set at the supplied index, adjusted for the type on which it's called (assumed to be in the right-hand end of the int).
So, BYTE.bit(4) returns an int with bit 28 set and SHORT.bit(4) returns an int with bit 20 set. Obviously, INT.bit(4) returns an int with bit 4 set.

There is also a static final version of this method, called only(int), which basically replicates the function of INT.bit(), but doesn't have to do any index adjustment.

Hopefully you follow me so far. Now to my problem:

First, I would have assumed that only() would be slightly faster than bits(), but if I simply execute Bits.only() or Bits.INT.bit() in test code, the first takes, on average, about 9ns and the 2nd about 7ns.
BUT, if I include the following statement:
Bits iBits = Bits.INT;
the first now takes 1.1 ns, and the 2nd 1.5!

I don't even use the field, and yet it makes both methods run much faster, and also behave as I would expect.

Does anyone have any idea what might be going on here?

Winston

PS: I'm happy to include the code if people think it will help; but I'm not sure that it'll add anything to what I've described.
 
Java Cowboy
Posts: 16084
88
Android Scala IntelliJ IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It's hard to say. Maybe it's in your code, or maybe it's in the code that is doing the measurement. The JVM and JIT do many sophisticated optimizations, which can lead to surprising results when doing (micro-)benchmarks of Java code. Can you post your code, and the code that you use to run the test? Maybe there's something that you're overlooking in there that someone else might spot.
 
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm with Jesper on the "provide an SSCCE" part (but then, you knew I would be). However, one might speculate that, among that hoodoo magic optimization that Jesper mentioned, having the variable there results in Bint.INT being preloaded and cached, and later references to Bit.INT being recognized as that same value, whereas without it, for whatever reason, it's not noticed that all those Bits.INT reference the same thing, and so it's looked up each time.

Just a little Friday mornin' guessin'.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesper de Jong wrote:Can you post your code, and the code that you use to run the test?


Ooof. OK, but I'll have to do a lot of pruning to give you an SSCCE for the Bits class.
Here's the code for the test module though (sorry for delay; phone):the time (System.nanoseconds()) is only taken when s.start() and s.stopMultiple() are executed, and the difference is divided by times. I've used the Stopwatch class for a lot of testing, so I'm pretty confident that (a) it works, and (b) it affects overall timings minimally.

With line 16 included, timings are:
Event: bit(15)
Number of timings: 1000000000
Total elapse time: 1.129430440s
Total active time: 1.129430440s
Avg. active time: 1.129ns

Event: Bits.bit(15)
Number of timings: 1000000000
Total elapse time: 1.514076280s
Total active time: 1.514076280s
Avg. active time: 1.514ns

Event: Bits.only(15)
Number of timings: 1000000000
Total elapse time: 1.140658400s
Total active time: 1.140658400s
Avg. active time: 1.141ns


With it commented out, timings are:
Event: bit(15)
Number of timings: 1000000000
Total elapse time: 1.101924640s
Total active time: 1.101924640s
Avg. active time: 1.102ns

Event: Bits.bit(15)
Number of timings: 1000000000
Total elapse time: 7.143473600s
Total active time: 7.143473600s
Avg. active time: 7.143ns

Event: Bits.only(15)
Number of timings: 1000000000
Total elapse time: 8.945839840s
Total active time: 8.945839840s
Avg. active time: 8.946ns


Hope it helps. I'll include the "pruned" version as soon as I can.

Winston
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jeff Verdegan wrote:However, one might speculate that, among that hoodoo magic optimization that Jesper mentioned, having the variable there results in Bint.INT being preloaded and cached, and later references to Bit.INT being recognized as that same value, whereas without it, for whatever reason, it's not noticed that all those Bits.INT reference the same thing, and so it's looked up each time.

Just a little Friday mornin' guessin'.


Oddly enough, that was my thought too (obviously great minds think alike), but it doesn't explain why it would affect the static method, nor why that one should take longer to execute without it.

It's no great worry (Knuth et al - and 9ns is still pretty darn fast ), but it does puzzle me.

Winston
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The test looks about right to me. I certainly don't see anything to suggest other than the mysterious hand-wavy "explanation" I previously gave. I'd be very surprised if posting the Bits enum changes that.

Maybe somebody smart will have a more concrete idea though.
 
Jeff Verdegan
Bartender
Posts: 6109
6
Android IntelliJ IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Winston Gutkowski wrote:

Jeff Verdegan wrote:However, one might speculate that, among that hoodoo magic optimization that Jesper mentioned, having the variable there results in Bint.INT being preloaded and cached, and later references to Bit.INT being recognized as that same value, whereas without it, for whatever reason, it's not noticed that all those Bits.INT reference the same thing, and so it's looked up each time.

Just a little Friday mornin' guessin'.


Oddly enough, that was my thought too (obviously great minds think alike), but it doesn't explain why it would affect the static method, nor why that one should take longer to execute without it.



I could imagine that, with or without the magical line, the static method gets inlined (after 10k or so executions), so that ultimately, both the static and non-static tests are executing the same code. Then, if the "caching" comes in on top of that...
 
Jesper de Jong
Java Cowboy
Posts: 16084
88
Android Scala IntelliJ IDE Spring Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You could try disassembling the bytecode with javap -c to see if there is any significant difference between the versions with and without the line.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jeff Verdegan wrote:I'm with Jesper on the "provide an SSCCE" part


OK, here's my "pruned down" Bits class (still quite a mouthful, but a LOT smaller than the original).

I also ran it against the same test code and it produces the same results, so it does seem to be some intrinsic optimization:Any opinions would be gratefully received.

Winston
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesper de Jong wrote:You could try disassembling the bytecode with javap -c to see if there is any significant difference between the versions with and without the line.


Good thinking. Any idea how to get Eclipse to run 'javap' for you?

Winston
 
Sheriff
Posts: 22783
131
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What's wrong with using the command line?

I can perhaps understand why the first block (Bits.bit) takes longer without the Bits iBits = Bits.INT; line - the loading of class Bits is moved from before the time measurements to within them. That doesn't explain (at least to me) why the second block (Bits.only) takes longer as well. The class is already loaded.
 
Winston Gutkowski
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rob Spoor wrote:What's wrong with using the command line?


Yes of course; it's not like a compile. I'll give it a bash.

That doesn't explain (at least to me) why the second block (Bits.only) takes longer as well. The class is already loaded.


Seems we're thinking along the same lines. I'll report back if javap reveals anything startling; although my bytecode instruction knowledge is very sketchy.

Winston
 
Hey! You're stepping on my hand! Help me tiny ad!
a bit of art, as a gift, that will fit in a stocking
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic