Java Forum / General / February 2006
signum of long
Roedy Green - 30 Jan 2006 12:17 GMT When you do a comparator you are supposed to return a + - or 0 int, even if you have been comparing longs.
Lets say you have + - or 0 long and you want some sort of + 0 or 0 int.
What is the fastest way to get it.
You could do
if ( a > 0 ) return 1; if ( a < 0 ) return -1; return 0;
I can't come up with one liner using shift and masks that is less complex.
if ( a == 0 ) return 0; else return (int) ( a >>> 32);
since booleans are not bit masks, you can use them in the traditional Forthian ways.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Robert Klemme - 30 Jan 2006 12:28 GMT > When you do a comparator you are supposed to return a + - or 0 int, > even if you have been comparing longs. [quoted text clipped - 15 lines] > if ( a == 0 ) return 0; > else return (int) ( a >>> 32); You're almost there.
return a == 0 ? 0 : (int) (a >> 32);
I don't know whether it's really worth the effort as int arithmetic should be quite fast anyway. This is certainly something I'd only change from the straight forward impl (your first version) if that proves to be too slow.
Kind regards
robert
Roedy Green - 30 Jan 2006 12:49 GMT >I don't know whether it's really worth the effort as int arithmetic should >be quite fast anyway. This is certainly something I'd only change from >the straight forward impl (your first version) if that proves to be too >slow. This is a pattern that comes up often writing comparators. In those cases I like to figure out the fastest way to do it, and use that consistently.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Robert Klemme - 30 Jan 2006 15:17 GMT >> I don't know whether it's really worth the effort as int arithmetic >> should be quite fast anyway. This is certainly something I'd only [quoted text clipped - 4 lines] > cases I like to figure out the fastest way to do it, and use that > consistently. That's easy: put the code into a static method. If measurements show it's too slow, change the implementation. The JVM is going to inline this anyway if it's a hot spot.
robert
Roedy Green - 30 Jan 2006 12:52 GMT >> if ( a == 0 ) return 0; >> else return (int) ( a >>> 32); > >You're almost there. > >return a == 0 ? 0 : (int) (a >> 32); I think from the JVM lever they may generate identical instructions. I spent so much time back in the 80s fine tuning assembler, it bothers me to use conditional jumps in stereotypical instances like that. Conditional jumps are just about the slowest operation because they tend to screw up pipeline lookahead, caching etc. You can afford to do quite a bit of arithmetic to avoid one. The Power PC took much of the penalty away. I don't know about the modern Pentiums.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Robert Klemme - 30 Jan 2006 15:17 GMT >>> if ( a == 0 ) return 0; >>> else return (int) ( a >>> 32); [quoted text clipped - 9 lines] > do quite a bit of arithmetic to avoid one. The Power PC took much of > the penalty away. I don't know about the modern Pentiums. Don'f forget that there's the JVM between that also. Usually these things are not worth the effort until they proove to be a source of performance problems.
robert
Roedy Green - 30 Jan 2006 20:59 GMT >Don'f forget that there's the JVM between that also. Usually these things >are not worth the effort until they proove to be a source of performance >problems. Agreed. This is more a obsessive thing, just wanting to know the fastest way. Then I can use the fastest way routinely, mindlessly, and know there is one thing I don't have to think about that may need tweaking. This assumes the fastest way is not so ugly it would confuse maintenance programmers. If it is encapsulated and inlined, you can explain it.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Eric Sosman - 30 Jan 2006 17:20 GMT Robert Klemme wrote On 01/30/06 07:28,:
>>When you do a comparator you are supposed to return a + - or 0 int, >>even if you have been comparing longs. [quoted text clipped - 19 lines] > > return a == 0 ? 0 : (int) (a >> 32); This doesn't look right: It returns zero for `a' equal to forty-two, for example.
Best I can think of is something like
return (a > 0) ? 1 : (int)(a >> 32);
 Signature Eric.Sosman@sun.com
opalpa@gmail.com opalinski from opalpaweb - 30 Jan 2006 13:08 GMT There is got to be very good reason to distance oneself from an idiom. Code clarity reduces comprehension time by a lot. It's kind of hard to compare time reduction in code execution with time reduction in code maintanance.
This snippet may very well be slower to excute (two if statements in it and a subtract):
((a > 0)?1:0) - ((a<0)?1:0)
is functionaly equivalent to:
if ( a > 0 ) return 1; if ( a < 0 ) return -1; return 0;
if a is positive the first part equates to 1 and the second to 0 and the subtraction yields 1. if a is zero the first and second part are both zero and the subtraction yields 0. if a is negative the first part is 0, the second 1, and the subtraction yields -1.
Opalinski opalpa@gmail.com http://www.geocities.com/opalpaweb/
Boris Stumm - 30 Jan 2006 16:33 GMT > When you do a comparator you are supposed to return a + - or 0 int, > even if you have been comparing longs. [quoted text clipped - 3 lines] > > What is the fastest way to get it. return Long.signum(a);
or, as comparison: return Long.signum(a-b);
Roedy Green - 30 Jan 2006 21:21 GMT On Mon, 30 Jan 2006 17:33:13 +0100, Boris Stumm <stumm@informatik.uni-kl.de> wrote, quoted or indirectly quoted someone who said :
>return Long.signum(a); > >or, as comparison: >return Long.signum(a-b); that came in with JDK 1.5.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 30 Jan 2006 22:26 GMT On Mon, 30 Jan 2006 21:21:10 GMT, Roedy Green <my_email_is_posted_on_my_website@munged.invalid> wrote, quoted or indirectly quoted someone who said :
>>return Long.signum(a); >> >>or, as comparison: >>return Long.signum(a-b); > >that came in with JDK 1.5. see http://mindprod.com/jgloss/signum.html for my little essay on the subject.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Piotr Kobzda - 31 Jan 2006 09:16 GMT > see http://mindprod.com/jgloss/signum.html > for my little essay on the subject. Well, I think there is something wrong with your code:
<cite> /** * alternate to signum for use in compare. * Not a true signum, since it returns ints * other than +-1. Where there is any possibility of overflow, * you should compare two longs with < rather than subtraction. * * @param diff usually represents the difference of two long. * * @return signum of diff, some -ve int, 0 or some -ve int. * */ public static final int signOf ( long diff ) { return (a != 0) ? (int)(a >>> 32) : 0; }
</cite>
First of all a trivial refactoring of 'a' to 'diff' is needed. Next, consider replacement with the following code:
return (int)(a >>> 32);
Both are equivalent, but the latter should be faster in most cases.
Worst thing is, it doesn't work as described in your comment (if I understand it well).
What is expected result for e.g. signOf(1L)? My expectation is some positive integer, but your code produce *0* in this case?!...
Let's check the consequences:
Long[] test = { 1L, 0L, 4L, 2L, 3L }; Arrays.sort(test, new Comparator<Long>() { public int compare(Long a1, Long a2) { long a = a1 - a2; return (a != 0) ? (int)(a >>> 32) : 0; } }); System.out.println(Arrays.asList(test));
The code above produce the following output:
[-1, 0, -4, -2, -3]
Not the expected one, I guess...
I think, your intention is the code like this:
return (a <= 0) ? (int)(a >>> 32) : 1;
Which works fine.
But there are some faster solutions available, e.g:
return (int)(a >>> 32) | (int)a >>> 1 | (int)a & 1;
My simple performance tests shows the latter is even a little faster than Java 5's Long.signum(). But I haven't tested it intensively...
Regards, piotr
Roedy Green - 31 Jan 2006 11:45 GMT >public static final int signOf ( long diff ) > { > return (a != 0) ? (int)(a >>> 32) : 0; > } > ></cite> oops.
>First of all a trivial refactoring of 'a' to 'diff' is needed. >Next, consider replacement with the following code: > > return (int)(a >>> 32); > >Both are equivalent, but the latter should be faster in most cases. consider diff = 3. that would turn into 0 with your method.
The following is still not quite right. See if you can see the flaw: return (int) ( (a >>> 32) | ( a & 0x07fffff ));
that would have been faster on the old 8086, the assembler I spent most time in.
the essential problem is you must examine/summarise all 64 bits to detect 0, and == 0 does to return an integer you can do anything with. All you can do with a boolean are conditional jumps.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Piotr Kobzda - 31 Jan 2006 15:01 GMT >>public static final int signOf ( long diff ) >> { [quoted text clipped - 13 lines] > > consider diff = 3. that would turn into 0 with your method. The method is _yours_, mine is optimized version of it only. :)
The result of your signOf(3) is 0. For each 'diff' ranged from 0 to 0xFFFFFFFFL result is 0 as well.
The reason is loose of 32 youngest bits of argument.
> The following is still not quite right. See if you can see the flaw: > return (int) ( (a >>> 32) | ( a & 0x07fffff )); > > that would have been faster on the old 8086, the assembler I spent > most time in. Right, but you have similar problem here -- lost bit #31 of 'a'.
> the essential problem is you must examine/summarise all 64 bits to > detect 0, and == 0 does to return an integer you can do anything with. > All you can do with a boolean are conditional jumps. I'm not sure I understand you right. My suggestion, I mean: return (int)(a >>> 32) | (int)a >>> 1 | (int)a & 1;
behaves exactly as you've mentioned. There is bitwise sum of all 64 bits, with special care on "sign" bit, that's all.
The correction on your site looks better, but IMHO is still buggy...
Regards, piotr
Roedy Green - 31 Jan 2006 19:31 GMT >The result of your signOf(3) is 0. >For each 'diff' ranged from 0 to 0xFFFFFFFFL result is 0 as well. > >The reason is loose of 32 youngest bits of argument. How about this:
/** * alternate to signum for use in compare. * Not a true signum, since it returns ints * other than +-1. Where there is any possibility of overflow, * you should compare two longs with < rather than subtraction. * * @param diff usually represents the difference of two long. * * @return signum of diff, some -ve int, 0 or some -ve int. * */ public static final int signOf ( long diff ) { return ( diff != 0 ) ? ( (int)( diff >>> 32 ) | 1 ) : 0; }
If I screw up this time, I write a test harness.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Piotr Kobzda - 31 Jan 2006 21:51 GMT > public static final int signOf ( long diff ) > { > return ( diff != 0 ) ? ( (int)( diff >>> 32 ) | 1 ) : 0; > } > > If I screw up this time, I write a test harness. You don't have to, it works fine now. :-)
My suggestion (shifts and ors) is a little faster under "client" VM, under "server" VM your method wins (positions swaps). But we both beat the competition... :))) Nevertheless, it seams to me there is no way to firmly say which method is fastest for all possible JVMs... But who cares? ... ;-)
Regards, piotr
Stefan Ram - 31 Jan 2006 12:59 GMT >First of all a trivial refactoring of 'a' to 'diff' is needed. Refactoring means making changes that don't add or change functionality for clarity. Here you are rectifying a mistake.
Piotr Kobzda - 31 Jan 2006 15:07 GMT >>First of all a trivial refactoring of 'a' to 'diff' is needed. > > Refactoring means making changes that don't add or change > functionality for clarity. Here you are rectifying a mistake. Refactoring means also modifying source code without changing its *external behavior*.
The fragment I've modified is a single line of code, without any change of its (the line) external behavior... :-)
The refactoring scope may vary.
Regards, piotr
opalpa@gmail.com opalinski from opalpaweb - 30 Jan 2006 21:42 GMT > or, as comparison: > return Long.signum(a-b); Might be fast, but it is incorrect:
package experiment; public class signum { static public void main(String args[]) { long a = java.lang.Long.MIN_VALUE; long b = 1; int cmpsignum = Long.signum(a-b); int cmpcorrect = basicCmp(a,b); System.out.println("maybe="+cmpsignum+" really="+cmpcorrect); } static private int basicCmp(long a, long b) { if ( a > b ) return 1; if ( a < b ) return -1; return 0; } }
Opalinski opalpa@gmail.com http://www.geocities.com/opalpaweb/
Thomas Hawtin - 31 Jan 2006 20:10 GMT > When you do a comparator you are supposed to return a + - or 0 int, > even if you have been comparing longs. [quoted text clipped - 18 lines] > since booleans are not bit masks, you can use them in the traditional > Forthian ways. How about this (not tested or even compiled, just for added excitement)?
public static int sgn(long value) { return ((int)(value>>63)) | ((int)((~value)>>>63)) }
(that's a tilde not a minus)
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Eric Sosman - 31 Jan 2006 20:30 GMT Thomas Hawtin wrote On 01/31/06 15:21,:
> How about this (not tested or even compiled, just for added excitement)? > [quoted text clipped - 3 lines] > > (that's a tilde not a minus) sgn(0L) -> 1
 Signature Eric.Sosman@sun.com
Stefan Ram - 31 Jan 2006 20:47 GMT >> public static int sgn(long value) { >> return ((int)(value>>63)) | ((int)((~value)>>>63)) >> } > sgn(0L) -> 1 I have not really an idea what this thread is about, but:
public class Main { public static int isLessThan( final long left, final long right ) { return( int )-(( left - right )>> 63 ); } public static int signum( final long value ) { return isLessThan( 0, value )-isLessThan( value, 0 ); } public static void main( final java.lang.String[] args ) { java.lang.System.out.println( signum( 2L )); java.lang.System.out.println( signum( 1L )); java.lang.System.out.println( signum( 0L )); java.lang.System.out.println( signum( -1L )); java.lang.System.out.println( signum( -2L )); }}
Roedy Green - 31 Jan 2006 23:31 GMT >public class Main >{ public static int isLessThan( final long left, final long right ) [quoted text clipped - 7 lines] > java.lang.System.out.println( signum( -1L )); > java.lang.System.out.println( signum( -2L )); }} your algorithm fails at -9223372036854775808 giving 0
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Thomas Hawtin - 31 Jan 2006 23:19 GMT > Thomas Hawtin wrote On 01/31/06 15:21,: >> How about this (not tested or even compiled, just for added excitement)? [quoted text clipped - 6 lines] > > sgn(0L) -> 1 Er, yeah. The comment is exactly wrong.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Roedy Green - 31 Jan 2006 23:19 GMT On Tue, 31 Jan 2006 20:21:34 +0000, Thomas Hawtin <usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone who said :
> public static int sgn(long value) { > return ((int)(value>>63)) | ((int)((~value)>>>63)) > } I decided to write a test harness to test and time these contributions:
Here in the output from eclipse: standard = done with if Sun = (int) ((i >> 63) | (-i >>> 63)) Long.signum. signOf = return ( diff != 0 ) ? ( (int) ( diff >>> 32 ) | 1 ) : 0; sgn = (int) ( diff >> 63 ) ) | ( (int) ( ( ~diff ) >>> 63 );
sgn failed on 0. All the others passed my conformance tests.
On Eclipse the speed the ordering was standard best, signOf, sgn, sun
Checking conformance on crucial values... done Checking conformance on 10 million random longs... sgn fails at 0 giving 1 done testing standard signum... standard: 55984 0 testing Sun's signum... Sun: 77578 0 testing signOf... signof: 57469 0 testing sgn... sgn: 60156 200000000
on Java.exe the speed the ordering was laso standard best, signOf, sgn, sun
Checking conformance on crucial values... sgn fails at 0 giving 1 done
Checking conformance on 10 million random longs... done testing standard signum... standard: 49656 0 testing Sun's signum... Sun: 70438 0 testing signOf... signof: 53968 0 testing sgn... sgn: 63235 200000000 finished
with Jet things change drastically:
Now SignOf in a clear winner, twice as fast the runner up and 4 times as fast as Sun's. The order is signof, standard, sgn, Sun.
Checking conformance on crucial values... sgn fails at 0 giving 1 done Checking conformance on 10 million random longs... done testing standard signum... standard: 31016 0 testing Sun's signum... Sun: 74390 0 testing signOf... signof: 16438 0 testing sgn... sgn: 67953 200000000 finished
The other interesting fact is that is using Jet with signOf is 4.7 times faster than using Sun's Long.signum with Java.exe.
You might wonder why signof is so much faster.
Hint: always use unsigned shifts when you can. Clever optimisers can use machine instructions for example that will store the high 32 bits of a long directly to memory or another register. ~ & and | are very cheap operations, same as + -. conditional jumps are expensive.
Here is the test harness so you can experiment on your own platform.
package com.mindprod.example; import java.util.Random; /** * Compare four different ways of doing a signum. * * @author Roedy Green */ public class TestSignum {
/** * crucial values to test for conformance. */ private static long testValues[] = { Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2, Integer.MIN_VALUE - 2, Integer.MIN_VALUE - 1, Integer.MIN_VALUE, Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 2, -2, -1, 0, 1, 2, Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, Integer.MAX_VALUE + 1, Integer.MAX_VALUE + 2, Long.MAX_VALUE - 2, Long.MAX_VALUE - 1, Long.MAX_VALUE };
/** * test all four signum-like algorithms for conformance. To pass must return * a + int for a positive long, a -ve int for a negitive long and 0 for a * zero long. * * @param diff * number to be collapsed to an int preserving sign and zeroness. */ static void checkConforms ( long diff ) { int standard = signum( diff );
// Sun's Long.signum is implemented as (int) ((i >> 63) | (-i
>>> 63)); int sun = Long.signum( diff );
int signOf = signOf( diff ); int sgn = sgn( diff );
if ( signum( sun ) != standard ) { System.err.println( "sun fails at " + diff + " giving " + sun ); }
if ( signum( signOf ) != standard ) { System.err .println( "signOf fails at " + diff + " giving " + signOf ); }
if ( signum( sgn ) != standard ) { System.err.println( "sgn fails at " + diff + " giving " + sgn ); } }
/** * Test harness to compare the various signum methods. * * @param args * not used */ public static void main ( String[] args ) {
System.out.println( "Checking conformance on crucial values..." ); for ( long diff : testValues ) { checkConforms( diff ); } System.out.println( "done" );
System.out .println( "Checking conformance on 10 million random longs..." ); Random wheel = new Random(); for ( int i = 0; i < 10000000; i++ ) { checkConforms( wheel.nextLong() ); } System.out.println( "done" ); /** timing test */ /** run timing test 200 million times */ timeStandard(); timeSun(); timeSignOf(); timeSgn(); System.out.println( "finished" ); }
/** how many times to run the timing tests */ final static int timingIterations = 200000000;
/** * Time the standard signum implementation */ private static void timeStandard () { System.out.println( "testing standard signum..." ); int sum = 0; long start = System.currentTimeMillis(); for ( int i = 0; i < timingIterations; i++ ) { for ( long diff : testValues ) { sum += signum( diff ); } } // print sum so code to compute it won't be optimised away System.out.println( "standard: " + ( System.currentTimeMillis() - start ) + " " + sum ); }
/** * Time the signOf signum implementation */ private static void timeSignOf () { System.out.println( "testing signOf..." ); int sum = 0; long start = System.currentTimeMillis(); for ( int i = 0; i < timingIterations; i++ ) { for ( long diff : testValues ) { sum += signOf( diff ); } } // print sum so code to compute it won't be optimised away System.out.println( "signof: " + ( System.currentTimeMillis() - start ) + " " + sum ); }
/** * Time the signOf signum implementation */ private static void timeSgn () { System.out.println( "testing sgn..." ); int sum = 0; long start = System.currentTimeMillis(); for ( int i = 0; i < timingIterations; i++ ) { for ( long diff : testValues ) { sum += sgn( diff ); } } // print sum so code to compute it won't be optimised away System.out.println( "sgn: " + ( System.currentTimeMillis() - start ) + " " + sum ); }
/** * Time the sun's signum implementation */ private static void timeSun () { System.out.println( "testing Sun's signum..." ); int sum = 0; long start = System.currentTimeMillis(); for ( int i = 0; i < timingIterations; i++ ) { for ( long diff : testValues ) { sum += Long.signum( diff ); } } // print sum so code to compute it won't be optimised away System.out.println( "Sun: " + ( System.currentTimeMillis() - start ) + " " + sum ); }
/** * Thomas Hawtin's algorithm Alternate to signum for use in compare. Not a * true signum, since it returns ints other than +-1. Where there is any * possibility of overflow, you should compare two longs with < rather than * subtraction. * * @param diff * number to be collapsed to an int preserving sign and zeroness. * usually represents the difference of two long. * @return sign of diff, some -ve int, 0 or some -ve int. */ public static int sgn ( long diff ) { return ( (int) ( diff >> 63 ) ) | ( (int) ( ( ~diff ) >>> 63 ) ) ; } /** * alternate to signum for use in compare. Not a true signum, since it * returns ints other than +-1. Where there is any possibility of overflow, * you should compare two longs with < rather than subtraction. * * @param diff * number to be collapsed to an int preserving sign and zeroness. * usually represents the difference of two long. * @return sign of diff, some -ve int, 0 or some -ve int. */ public static final int signOf ( long diff ) { return ( diff != 0 ) ? ( (int) ( diff >>> 32 ) | 1 ) : 0; }
/** * Collapse number down to +1 0 or -1 depending on sign. Typically used in * compare routines to collapse a difference of two longs to an int. * * @param diff * number to be collapsed to an int preserving sign and zeroness. * usually represents the difference of two long. * @return true signum of diff, +1, 0 or -1. */ public static final int signum ( long diff ) { if ( diff > 0 ) { return 1; } if ( diff < 0 ) { return -1; } else { return 0; } } // end signum }
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Wibble - 01 Feb 2006 02:40 GMT > On Tue, 31 Jan 2006 20:21:34 +0000, Thomas Hawtin > <usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone [quoted text clipped - 356 lines] > } // end signum > } You can do a little better than signof or sgn:
public static final int wibble ( long diff ) { return ( ((int)( diff >>> 32 )) | (((int)diff)>>>1)&0x7fffffff | (((int)diff)&1)); } // end signum
Wibble - 01 Feb 2006 02:48 GMT >> On Tue, 31 Jan 2006 20:21:34 +0000, Thomas Hawtin >> <usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone [quoted text clipped - 357 lines] > | (((int)diff)&1)); > } // end signum Oops, dont need the &0xfffffff, not much worse than standard on my box....
public static final int mdw ( long diff ) { return ( ((int)( diff >>> 32 )) | (((int)diff)>>>1) | (((int)diff)&1)); }
Roedy Green - 01 Feb 2006 09:11 GMT >Oops, dont need the &0xfffffff, not much worse than standard on my box.... > [quoted text clipped - 4 lines] > | (((int)diff)&1)); > } you also don't need that forest of (). I have added your revised algorithm to the harness. Results to come shortly. It takes about half an hour to run the battery. My last test was interrupted by the announcement of new Eclipse updates. .
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 01 Feb 2006 10:06 GMT >Oops, dont need the &0xfffffff, not much worse than standard on my box.... > [quoted text clipped - 4 lines] > | (((int)diff)&1)); > } here's the latest results. As expected, wibble2 is better than wibble. What I don't understand is why signOf has pulled ahead of wibble and wibble2. There should be no GC going on to confuse the issue.
the latest benchmark harness is posted at http://mindprod.com/jgloss/benchmark.html
* Compare eight different ways of doing a signum. * Results of benchmarks to find the fastest Long.signum implementation. * * stephan fails at -9223372036854775808 giving 0 * sgn fails at 0 giving 1 * * Candidates at the top with the lowest elapsed time in nanoseconds are fastest. * * Under Eclipse 3.1 * nanoseconds : candidate * 45768960301 : wibble2 * 49644018723 : wibble * 53844393072 : Standard 2 ifs * 57757400172 : signOf * 62539824754 : sgn * 70119185818 : Sun Long.signum * 93615190478 : stephan * * under Java.exe -client 1.5 * nanoseconds : candidate * 45046931943 : wibble2 * 52720203825 : Standard 2 ifs * 53114062821 : signOf * 54456221112 : wibble * 65598471873 : sgn * 67578554258 : Sun Long.signum * 89407076039 : stephan * * under Java.exe -server 1.6 * nanoseconds : candidate * 30604675175 : signOf * 30795535466 : wibble2 * 30916536599 : wibble * 37537113795 : sgn * 38492840011 : Standard 2 ifs * 40344760018 : Sun Long.signum * 44422669361 : stephan * * under Jet 4.0 * nanoseconds : candidate * 14847425301 : signOf * 36774335007 : wibble2 * 39282795997 : wibble * 48173823210 : Standard 2 ifs * 73310262058 : sgn * 74333541274 : Sun Long.signum * 79269158511 : stephan
signOf with Jet is the fastest combination.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Wibble - 01 Feb 2006 12:16 GMT >>Oops, dont need the &0xfffffff, not much worse than standard on my box.... >> [quoted text clipped - 64 lines] > > signOf with Jet is the fastest combination. I get crazy variablilty between runs and when I reorder the tests. How many times did you run the benchmarks? Maybe -Xbatch would give you more control of when the optimizer runs.
I'm a lisp programmer. I like forests of parens :)))))
Roedy Green - 01 Feb 2006 12:42 GMT >I get crazy variablilty between runs and when I reorder >the tests. How many times did you run the benchmarks? >Maybe -Xbatch would give you more control of when the >optimizer runs. those are just the results of one run. This is tedious business. Even that batch took 30 minutes during which time I could not use my computer least I disturb the results. Maybe to get the final optimimizations, I should run the tests, discard the result the run them again in the same JVM, hoping the world settles to a steady state.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 01 Feb 2006 08:11 GMT >You can do a little better than signof or sgn: > [quoted text clipped - 4 lines] > | (((int)diff)&1)); > } // end signum Results of benchmarks to find the fastest Long.signum implementation
stephan fails at -9223372036854775808 giving 0 sgn fails at 0 giving 1
Under Eclipse millis : candidate 49079 : wibble 53500 : Standard 2 ifs 57469 : signOf 62281 : sgn 69765 : Sun Long.signum 93156 : stephan
under Java.exe -client 1.5 millis : candidate 52000 : Standard 2 ifs 52781 : signOf 53907 : wibble 65094 : sgn 66938 : Sun Long.signum 88468 : stephan
under Java.exe -server 1.6 millis : candidate 29188 : signOf 29985 : wibble 35297 : sgn 37562 : Standard 2 ifs 37625 : Sun Long.signum 42968 : stephan
under Jet 4.0 millis : candidate 13156 : wibble 16562 : signOf 34266 : Standard 2 ifs 69954 : sgn 74906 : Sun Long.signum 113859 : stephan
So what have we discovered:
1. fastest code depends on the JVM. You don't know till you measure the alternatives.
2. the fastest time was wibble under Jet. This is four times faster than under Java -client.
3. Though Sun's Long.signum algorithm is terse, it is a pretty much a pig. Sun's Long.signum is implemented as (int) ((i >> 63) | (-i >>> 63));
I have posted the improved test harness at http://mindprod.com/jgloss/benchmark.html
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 01 Feb 2006 09:08 GMT On Wed, 01 Feb 2006 08:11:51 GMT, Roedy Green <my_email_is_posted_on_my_website@munged.invalid> wrote, quoted or indirectly quoted someone who said :
>1. fastest code depends on the JVM. You don't know till you measure >the alternatives. You might wonder what the heck is going on. the variability is more than most people would expect.
Consider that longs are faked as a pair of 32 bit registers on 32-bit machines. This make long operations take more than twice as long as in ones. The trick is to prune your calculations back to int at the earliest opportunity. (Wibble2 does this.)
Consider that at the hardware level you have pipelines. This allows parallel execution of the algorithm if it nicely decomposes into independent parts.
Consider that on some hardware you have speculative execution of both a true and false branch in parallel.
Consider that on some hardware jumps are free. The pipeline preprocessor deals with them.
Consider that on some hardware jumps are very expensive. They clear caches and pipelines.
Consider that some hardware has special instructions for moving the upper and lower halves of a long or int.
Consider that >>> is inherently a simpler operation than >>.
Consider that though & | ~ are exotic to most Java programmers, they are one-cycle ops just like + and -.
Consider that with hotspot, you run interpretively for awhile, then take time out to compile to native code, and later to optimise. You are also paying the penalty for this translation time. The behaviour of interpreted code is vastly different from optimised machine code. There you have a heavy overhead per JVM instruction so the trick is to use as few JVM instructions as possible relatively independently of what they do.
I'd like to see some results from some more exotic hardware, especially the Power PC whose architecture I find aesthetically pleasing. Also I'd like to see how the horse-race changes on a 64-bit machine. I suspect Sun's algorithm will shine there.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Piotr Kobzda - 01 Feb 2006 03:05 GMT > Here is the test harness so you can experiment on your own platform. Just for comparison, below are listed results for my platform (XP, 1x1,8GHz, 1GB RAM), with additional contribution of the following method:
public static final int signShift ( long diff ) { return (int)(diff >>> 32) | (int)diff >>> 1 | (int)diff & 1; }
Sun's 1.5.0_06 "client" VM: signShift best, standard, sgn, signOf, sun
Checking conformance on crucial values... done Checking conformance on 10 million random longs... done testing standard signum... sgn fails at 0 giving 1 standard: 64125 0 testing Sun's signum... Sun: 78703 0 testing signOf... signof: 75344 0 testing sgn... sgn: 73453 200000000 testing signShift... signShift: 60547 2094967296 finished
Sun's 1.5.0_06 "server" VM: standard best, signOf, sgn, signShift, sun
Checking conformance on crucial values... done Checking conformance on 10 million random longs... done testing standard signum... sgn fails at 0 giving 1 standard: 29609 0 testing Sun's signum... Sun: 55032 0 testing signOf... signof: 39140 0 testing sgn... sgn: 46813 200000000 testing signShift... signShift: 48672 2094967296 finished
Have no Jet things here to test with.
There are a few differences in your results comparing to my previous tests. Interesting because the only significant difference in testing method I've found is shorter timing loop and time measurement using System.nanoTime() in my tests. Biggest surprise is very good performance of the "standard" method, not observed before by me. The only similarity to my tests I can see under the client VM tests results.
Regards, piotr
Thomas Hawtin - 01 Feb 2006 08:13 GMT > signOf = return ( diff != 0 ) ? ( (int) ( diff >>> 32 ) | 1 ) : 0;
> sum += signOf( diff ); I'd like to call foul here. If diff is zero, effectively the result is ignored.
OTOH, it occurs to me that the typical use of a comparator function is to 'switch' on the result. (Sorry to bring things back closer to reality.) Therefore, you actually want the conditional jumps anyway. 'Cleverness' will just clog up the pipeline. In terms of assempler, actually all you want the processor to do is compare against zero (possibly available as a side-effect of a load or move).
Intel-style deep pipelines will lose heavily with such bi-twiddling. Niagra-style single-dispatch, multiple-hardware thread architectures should do better.
There's also the standard problem with the microbenchmarks: The test methods have hot inner loops lumped together with large single-use I/O and timing code. Perhaps a better test would be using some sort.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Roedy Green - 01 Feb 2006 08:27 GMT On Wed, 01 Feb 2006 08:30:06 +0000, Thomas Hawtin <usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone who said :
> sum += signOf( diff ); I have changed this is my latest version making sum local rather than static. It makes quite a difference. My intent was to pare non-candidate code to the bone in the inner loop.
I accumpulate the sum to a static at the end just in case the optimiser thinks these calculations are so much hot air.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 01 Feb 2006 08:23 GMT On Mon, 30 Jan 2006 12:17:01 GMT, Roedy Green <my_email_is_posted_on_my_website@munged.invalid> wrote, quoted or indirectly quoted someone who said :
>When you do a comparator you are supposed to return a + - or 0 int, >even if you have been comparing longs. [quoted text clipped - 3 lines] > >What is the fastest way to get it. Why would you care? Because sorting is often the bottleneck, and the compare is always the bottleneck of sorting and many compares contain signum logic.
Knuth says that premature optimisation is the root of all evil. However, routines that are core and used by everyone are ripe for optimisation, if you just want to exercise your skills.
You might as well do it right. People often affect the nutty idea that it is virtuous to write knowingly slow code even when you know of faster simpler ways. Many folk irrationally believe it is virtuous to pretend not to have any concerns for speed at ANY time.
What you DON'T do is fiddle with tiny bits of code making them harder to maintain, until you have demonstrated they are bottlenecks. There is no reason at all to avoid fast library code or fast algorithms first time out.
If you know on square 1 that the fastest algorithm to compute something and the code for it exists, you might as well use it. There is no more work than using some lame algorithm.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 02 Feb 2006 23:28 GMT On Mon, 30 Jan 2006 12:17:01 GMT, Roedy Green <my_email_is_posted_on_my_website@munged.invalid> wrote, quoted or indirectly quoted someone who said :
>When you do a comparator you are supposed to return a + - or 0 int, >even if you have been comparing longs. latest benchmark results of nine candidates are posted at http://mindprod.com/jgloss/benchmark.html
the new overall winner is signOf under Java server 1.5.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Piotr Kobzda - 03 Feb 2006 03:10 GMT >>When you do a comparator you are supposed to return a + - or 0 int, >>even if you have been comparing longs. > > latest benchmark results of nine candidates are posted at > http://mindprod.com/jgloss/benchmark.html Hmm, 9th candidate is here:
return (int)( diff >>> 32 ) | ( (int)diff | (int)diff << 1 ) >>> 1;
:-)
> the new overall winner is signOf under Java server 1.5. On my box under the Sun server 1.5 JVM the winner is 'Standard 2 ifs'. Under the JRockit a different animal is the winner too.
Here are my results (the 9th candidate is called "piotr"):
C:\Eclipse\Workspaces\testy\signum\bin>c:\java\jdk1.5.0_06\bin\java -Xbatch -client com.mindprod.example.TestSignum Checking conformance on crucial corner values... stephan fails at -9223372036854775808 giving 0 sgn fails at 0 giving 1 Checking conformance on 10 million random longs... Running timing tests with 200000000 iterations per candidate. ......... nanoseconds : candidate 66588911719 : piotr 67003112686 : wibble 71864425228 : kobzda 82698686006 : sgn 84221045692 : signOf 88460140884 : signHalf 93383637865 : Standard 2 ifs 94397889397 : Sun Long.signum 131756354230 : stephan finished
C:\Eclipse\Workspaces\testy\signum\bin>c:\java\jdk1.5.0_06\bin\java -Xbatch -server com.mindprod.example.TestSignum Checking conformance on crucial corner values... stephan fails at -9223372036854775808 giving 0 sgn fails at 0 giving 1 Checking conformance on 10 million random longs... Running timing tests with 200000000 iterations per candidate. ......... nanoseconds : candidate 39823319724 : Standard 2 ifs 41425703546 : signOf 45461844732 : signHalf 58202726883 : kobzda 59459250267 : wibble 62102067441 : sgn 64176119591 : piotr 65859999245 : Sun Long.signum 80201802184 : stephan finished
C:\Eclipse\Workspaces\testy\signum\bin>c:\java\jrockit-R26.0.0-jdk1.5.0_04\bin\java com.mindprod.example.TestSignum Checking conformance on crucial corner values... stephan fails at -9223372036854775808 giving 0 sgn fails at 0 giving 1 Checking conformance on 10 million random longs... Running timing tests with 200000000 iterations per candidate. ......... nanoseconds : candidate 44890325167 : piotr 48472883285 : kobzda 48476054359 : wibble 52179261889 : sgn 54405104001 : Sun Long.signum 66876197953 : signOf 81686631122 : stephan 88293991555 : signHalf 108119541450 : Standard 2 ifs finished
Regards, piotr
Roedy Green - 03 Feb 2006 08:40 GMT >Hmm, 9th candidate is here: > >return (int)( diff >>> 32 ) | ( (int)diff | (int)diff << 1 ) >>> 1; wibble2 in now ascribed to piotr under the name kobzda.
public static final int kobzda ( long diff ) { // the beauty of this method is most of the work is in int, not long // which works well on 32-bit machines. return (int) ( diff >>> 32 ) | (int)diff >>> 1 | (int)diff & 1; }
Poiter has onother algorithm he called signHalf public static final int signHalf ( long diff ) { return ( diff <= 0 ) ? (int) ( diff >>> 32 ) : 1; }
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Piotr Kobzda - 03 Feb 2006 09:04 GMT >>Hmm, 9th candidate is here: >> >>return (int)( diff >>> 32 ) | ( (int)diff | (int)diff << 1 ) >>> 1; You have missed my advice. This one is my next candidate. :)) Following the code carefully, you should see the difference...
Your benchmark (version 1.1) has *eight* candidates (not nine). I've added my new candidate (9th in the collection) under the name "piotr" into your benchmark, the results are in my previous post.
Regards, piotr
Roedy Green - 03 Feb 2006 11:57 GMT >You have missed my advice. This one is my next candidate. :)) >Following the code carefully, you should see the difference... Look at the latest version with two of your entries. See http://mindprod.com/jgloss/benchmark.html
The fastest overall is the piotr algorithm under Jet. It is four times faster than on Java 1.5 client.
The results are much less reproducible than I would have expected. I don't know why. I shut everything down I could.
IF you want to run the benchmark, you can download it by clicking download. Post the result of the latest version on your hardware if it reasonably different from mine or if your results differ significantly. see http://mindprod.com/contact/equipment.html
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Roedy Green - 03 Feb 2006 09:14 GMT >Hmm, 9th candidate is here: > >return (int)( diff >>> 32 ) | ( (int)diff | (int)diff << 1 ) >>> 1; I have added this 9th candidate into the mix as piotr. I will be posting the new results in a bout 30 minutes
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|