Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / February 2006

Tip: Looking for answers? Try searching our database.

signum of long

Thread view: 
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 Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.