Math.abs() discussion

I wanted to open up this discussion to those interested. I was running some tests of my own and found there was no difference between Math.abs() and ((a ^ (a >> 31)) - (a >> 31)). I just want to make sure that I am conducting these tests correctly.

First my machine specs:
HP Compaq P4 2.66GHz
768MB

EDIT: JDK = 1.5.0_05-b05

Here is the class that I set up:


public final class MathTest {
  public static int abs(int a) {
    return ((a ^ (a >> 31)) - (a >> 31));
  }
}

When I run with this test code:


public class Test {
    public static void main(String args[]) {
        int a = -2;
        int b;
        long time1;
        long time2;

        time1 = System.nanoTime();
            b = MathTest.abs(a);
        time2 = System.nanoTime();

        System.out.println((time2 - time1));
    }
}

The times are:
MathTest.abs() = 1084774 nano seconds
Math.abs() = 41346 nano seconds

but when I change the test code to this:


public class Test {
    public static void main(String args[]) {
        int a = -2;
        int b;
        long time1;
        long time2;

        for(int i=0;i<1000000;i++) {
            b = Math.abs(a);
        }

        time1 = System.nanoTime();
        for(int i=0;i<100000;i++) {
            b = Math.abs(a);
        }
        time2 = System.nanoTime();

        System.out.println((((double)time2 - (double)time1) / 100000.0));
    }
}

The times are:
MathTest.abs() = 2.34 nano seconds
Math.abs() = 2.34 nano seconds

So when I do a warm up loop and then average 100,000 calls, there is no difference. Am I running this test correctly?

The VM is smart… very smart, so try to use ‘b’ later on, or it will be optimized away. (at least the server VM is that smart)

Further, dividing by “100000.0” makes little sense, it should be 1,000,000.0, and then change “nano” into “milli”.

Also, don’t use the same value 100.000 times. Try using a randomly generated array of values.

Here’s a slightly cleaned up version of the test I used:

import java.util.*;

public class Foo
{
    public static int abs(int a)
    {
        return (a^(a>>31))-(a>>31);
    }
	
    public static void main(String[] args)
    {
        Random random = new Random();
        int[] vals = new int[1000000];

        for (int round = 0; round < 100; round++)
        {
            for (int i = 0; i < vals.length; i++)
            {
                vals[i] = random.nextInt();
            }
			
            for (int testNum = 0; testNum < 2; testNum++)
            {
                int res = 0;
                long pre = System.nanoTime();
                switch (testNum)
                {
                    case 0: // Math.abs
                        for (int i=0; i<vals.length; i++)
                        {
                            res += Math.abs(vals[i]);
                        }
                        break;
                    case 1: // New abs
                        for (int i=0; i<vals.length; i++)
                        {
                            res += Foo.abs(vals[i]);
                        }
                        break;
                    }
                long post = System.nanoTime();
                System.out.println(testNum + ": " + (post - pre)/(float)vals.length + " ns (combined result: "+res+")");
            }
        }
    }
}

Working on an int[] of 1000000 elements (4MB) will skew the results because of cpu-cache trashing.

Be that as it may, both functions suffer the same cpu-cache trashing.

And even with horrible cpu-cache trashing “skewing the results”, and with a relatively slow for loop, the new abs method is three times faster.

Thanks. I copied Markus’s code and had similar results. While the time taken was longer, but the speed was similar for both methods.


0: 18.60879 ns (combined result: -1315403454)
1: 19.868448 ns (combined result: -1315403454)
0: 18.246174 ns (combined result: -1865390775)
1: 18.293943 ns (combined result: -1865390775)
0: 18.0766 ns (combined result: 1080804409)
1: 18.343952 ns (combined result: 1080804409)
0: 18.157057 ns (combined result: 1758684426)
1: 18.083584 ns (combined result: 1758684426)
0: 18.365463 ns (combined result: -1076665754)
1: 18.197006 ns (combined result: -1076665754)
0: 18.03665 ns (combined result: -97081660)
1: 18.5423 ns (combined result: -97081660)
0: 18.180801 ns (combined result: -2120513054)
1: 18.668573 ns (combined result: -2120513054)
0: 17.952282 ns (combined result: 290568654)
1: 18.221869 ns (combined result: 290568654)
0: 17.981615 ns (combined result: 177362767)
1: 18.23919 ns (combined result: 177362767)
0: 18.165436 ns (combined result: -2077948072)
1: 18.30512 ns (combined result: -2077948072)
0: 18.764397 ns (combined result: -1748804390)
1: 18.363228 ns (combined result: -1748804390)
0: 18.34088 ns (combined result: 903271049)
1: 18.352053 ns (combined result: 903271049)
0: 18.325233 ns (combined result: 1483533930)
1: 18.052015 ns (combined result: 1483533930)
0: 18.109007 ns (combined result: 552949311)
1: 18.071293 ns (combined result: 552949311)
0: 17.983011 ns (combined result: 1747946045)
1: 18.616331 ns (combined result: 1747946045)
0: 17.972956 ns (combined result: 1956568510)
1: 18.451508 ns (combined result: 1956568510)
0: 17.87797 ns (combined result: 591958754)
1: 18.182756 ns (combined result: 591958754)
0: 18.145325 ns (combined result: 1098472751)
1: 18.1199 ns (combined result: 1098472751)
0: 18.083023 ns (combined result: 534516993)
1: 18.115152 ns (combined result: 534516993)
0: 18.054249 ns (combined result: -1745121369)
1: 18.065985 ns (combined result: -1745121369)
0: 18.03162 ns (combined result: -1843254525)
1: 18.634212 ns (combined result: -1843254525)
0: 17.956472 ns (combined result: -1270942001)
1: 18.389488 ns (combined result: -1270942001)
0: 17.879646 ns (combined result: 334485842)
1: 18.222708 ns (combined result: 334485842)
0: 18.13806 ns (combined result: 1077322536)
1: 18.3934 ns (combined result: 1077322536)
0: 18.077717 ns (combined result: 1432730059)
1: 18.250084 ns (combined result: 1432730059)
0: 18.11124 ns (combined result: 1705086187)
1: 18.082745 ns (combined result: 1705086187)
0: 18.276344 ns (combined result: 197915591)
1: 18.236116 ns (combined result: 197915591)
0: 17.951723 ns (combined result: -262464572)
1: 18.143648 ns (combined result: -262464572)
0: 17.982174 ns (combined result: -2080455328)
1: 18.117107 ns (combined result: -2080455328)
0: 18.088612 ns (combined result: -1106795676)
1: 18.082745 ns (combined result: -1106795676)
0: 18.6574 ns (combined result: 760014021)
1: 18.350376 ns (combined result: 760014021)
0: 18.914974 ns (combined result: -1724064827)
1: 18.925867 ns (combined result: -1724064827)
0: 18.038326 ns (combined result: 1044698636)
1: 18.59538 ns (combined result: 1044698636)
0: 18.097273 ns (combined result: -595048485)
1: 18.257908 ns (combined result: -595048485)
0: 18.121857 ns (combined result: 1677961454)
1: 18.162924 ns (combined result: 1677961454)
0: 18.448156 ns (combined result: -1623322489)
1: 18.417145 ns (combined result: -1623322489)
0: 18.19952 ns (combined result: 263470699)
1: 18.126604 ns (combined result: 263470699)
0: 17.972675 ns (combined result: -1060058100)
1: 18.153984 ns (combined result: -1060058100)
0: 18.000051 ns (combined result: 1205721433)
1: 18.12074 ns (combined result: 1205721433)
0: 18.116268 ns (combined result: 818869219)
1: 18.562136 ns (combined result: 818869219)
0: 17.962337 ns (combined result: 1878388999)
1: 18.430555 ns (combined result: 1878388999)
0: 18.309032 ns (combined result: -2113436117)
1: 18.55124 ns (combined result: -2113436117)
0: 18.074924 ns (combined result: 1820666565)
1: 18.240866 ns (combined result: 1820666565)
0: 18.386135 ns (combined result: 1229473012)
1: 18.161247 ns (combined result: 1229473012)
0: 18.02408 ns (combined result: 1958246077)
1: 18.202871 ns (combined result: 1958246077)
0: 17.903671 ns (combined result: 2067500862)
1: 18.388927 ns (combined result: 2067500862)
0: 18.18639 ns (combined result: 1324947411)
1: 18.226896 ns (combined result: 1324947411)
0: 17.956192 ns (combined result: -1924925087)
1: 18.101463 ns (combined result: -1924925087)
0: 17.86568 ns (combined result: -532989700)
1: 18.20846 ns (combined result: -532989700)
0: 18.183596 ns (combined result: 513265447)
1: 18.16488 ns (combined result: 513265447)
0: 18.100904 ns (combined result: 2060306131)
1: 18.351772 ns (combined result: 2060306131)
0: 18.158731 ns (combined result: 2071619586)
1: 18.098667 ns (combined result: 2071619586)
0: 18.169067 ns (combined result: -471254426)
1: 18.295065 ns (combined result: -471254426)
0: 18.249529 ns (combined result: -611431217)
1: 18.32244 ns (combined result: -611431217)
0: 18.23444 ns (combined result: -299034051)
1: 18.184992 ns (combined result: -299034051)
0: 18.132193 ns (combined result: 1430033366)
1: 18.31322 ns (combined result: 1430033366)
0: 18.205385 ns (combined result: -887723694)
1: 18.821108 ns (combined result: -887723694)
0: 18.36211 ns (combined result: 737382911)
1: 18.259584 ns (combined result: 737382911)
0: 18.096155 ns (combined result: 380839450)
1: 18.126884 ns (combined result: 380839450)
0: 18.024635 ns (combined result: -1108004076)
1: 18.1903 ns (combined result: -1108004076)
0: 18.10286 ns (combined result: 1716848877)
1: 18.714949 ns (combined result: 1716848877)
0: 18.047266 ns (combined result: 473761060)
1: 18.38446 ns (combined result: 473761060)
0: 18.183876 ns (combined result: -985866728)
1: 17.968485 ns (combined result: -985866728)
0: 18.158451 ns (combined result: 1244643750)
1: 18.419937 ns (combined result: 1244643750)
0: 18.235277 ns (combined result: -993873792)
1: 18.250645 ns (combined result: -993873792)
0: 18.329704 ns (combined result: -641453691)
1: 18.160408 ns (combined result: -641453691)
0: 18.049223 ns (combined result: 1027949056)
1: 18.528893 ns (combined result: 1027949056)
0: 18.077436 ns (combined result: 228195184)
1: 18.170465 ns (combined result: 228195184)
0: 18.183315 ns (combined result: -233048849)
1: 18.193094 ns (combined result: -233048849)
0: 17.937475 ns (combined result: 1751515530)
1: 18.11096 ns (combined result: 1751515530)
0: 17.995024 ns (combined result: -1862998016)
1: 18.170189 ns (combined result: -1862998016)
0: 17.97016 ns (combined result: -968791412)
1: 18.506542 ns (combined result: -968791412)
0: 18.184155 ns (combined result: -1635237483)
1: 18.197285 ns (combined result: -1635237483)
0: 17.892498 ns (combined result: -1406768969)
1: 18.1646 ns (combined result: -1406768969)
0: 18.403456 ns (combined result: 1802608728)
1: 18.31825 ns (combined result: 1802608728)
0: 18.21265 ns (combined result: -1428482136)
1: 18.224941 ns (combined result: -1428482136)
0: 18.167393 ns (combined result: 1189860402)
1: 18.43642 ns (combined result: 1189860402)
0: 18.2783 ns (combined result: 701333708)
1: 18.476929 ns (combined result: 701333708)
0: 17.976028 ns (combined result: 1069482517)
1: 18.224384 ns (combined result: 1069482517)
0: 18.000612 ns (combined result: 530740760)
1: 18.286123 ns (combined result: 530740760)
0: 17.965693 ns (combined result: -1496340562)
1: 18.140297 ns (combined result: -1496340562)
0: 18.563251 ns (combined result: -1123274341)
1: 18.34088 ns (combined result: -1123274341)
0: 18.386135 ns (combined result: 1518703820)
1: 18.051456 ns (combined result: 1518703820)
0: 18.020725 ns (combined result: 715089612)
1: 18.232763 ns (combined result: 715089612)
0: 18.003407 ns (combined result: -973747273)
1: 18.19114 ns (combined result: -973747273)
0: 18.104536 ns (combined result: 1886468757)
1: 18.67444 ns (combined result: 1886468757)
0: 18.227177 ns (combined result: 1201661615)
1: 18.298695 ns (combined result: 1201661615)
0: 18.484192 ns (combined result: 848677264)
1: 18.046148 ns (combined result: 848677264)
0: 18.03246 ns (combined result: -1128007728)
1: 18.159012 ns (combined result: -1128007728)
0: 18.033298 ns (combined result: 688562529)
1: 18.19114 ns (combined result: 688562529)
0: 17.955912 ns (combined result: 500414561)
1: 18.262936 ns (combined result: 500414561)
0: 17.954515 ns (combined result: -778665564)
1: 18.198402 ns (combined result: -778665564)
0: 18.204548 ns (combined result: 400803130)
1: 18.136662 ns (combined result: 400803130)
0: 18.255114 ns (combined result: -1668956835)
1: 18.15594 ns (combined result: -1668956835)
0: 17.896969 ns (combined result: -40335860)
1: 18.136105 ns (combined result: -40335860)
0: 17.975748 ns (combined result: -654445336)
1: 18.178848 ns (combined result: -654445336)
0: 18.088053 ns (combined result: 2068805083)
1: 18.544256 ns (combined result: 2068805083)
0: 18.065985 ns (combined result: 1850106056)
1: 18.64762 ns (combined result: 1850106056)
0: 18.656282 ns (combined result: -1791631815)
1: 18.166277 ns (combined result: -1791631815)
0: 18.048943 ns (combined result: -2023037636)
1: 18.987328 ns (combined result: -2023037636)

I should mention that this is under WIndows 2000 workstation. What kind of machine do you run Markus?

Windows XP, some kind of dual core intel cpu, java 1.5.0_06

I added a third test that just does “res += vals[i];” for benchmarking purposes, and made it run with varying array sizes.
The combined result of the raw method doesn’t match (naturally) because it doesn’t abs the value at all.

Running test with vals[10]
Math.abs(): 93.663999710083 ns (combined result: -54233294)
Foo.abs(): 82.00599899291993 ns (combined result: -54233294)
raw (no abs): 81.54599906921386 ns (combined result: -1355128174)

Running test with vals[100]
Math.abs(): 20.077400035858155 ns (combined result: -856536926)
Foo.abs(): 10.829700002670288 ns (combined result: -856536926)
raw (no abs): 11.216599912643433 ns (combined result: -829359714)

Running test with vals[1000]
Math.abs(): 15.26091004371643 ns (combined result: 224407370)
Foo.abs(): 6.911370005607605 ns (combined result: 224407370)
raw (no abs): 6.515409989356995 ns (combined result: 1516536884)

Running test with vals[10000]
Math.abs(): 14.709900970458984 ns (combined result: -1976783927)
Foo.abs(): 6.338977026939392 ns (combined result: -1976783927)
raw (no abs): 5.448884983062744 ns (combined result: -1349739601)

Running test with vals[100000]
Math.abs(): 14.452681474685669 ns (combined result: -971028899)
Foo.abs(): 5.4027831172943115 ns (combined result: -971028899)
raw (no abs): 5.539101600646973 ns (combined result: 1645818807)

Must be your CPU. I did the same and got the same results.


0: 19.37425 ns (combined result: -536942588)
1: 19.841349 ns (combined result: -536942588)
2: 0.001397 ns (combined result: 0)
0: 18.103975 ns (combined result: -624993057)
1: 18.235836 ns (combined result: -624993057)
2: 0.002235 ns (combined result: 0)
0: 17.929932 ns (combined result: 67879535)
1: 18.14616 ns (combined result: 67879535)
2: 0.001677 ns (combined result: 0)
0: 17.901716 ns (combined result: -242554893)
1: 18.668016 ns (combined result: -242554893)
2: 0.001676 ns (combined result: 0)

Combined result 0 for the raw one?
That’s highly unlikely… You must’ve made a mistake, causing the jvm to optimise it out.

case 2:
    for (int i = 0; i < vals.length; i++)
    {
        res += vals[i];
    }
    break;

That’s what it’s supposed to look like.

Just to make sure it wasn’t some kind of issue with the Math class itself, I changed Foo.abs into this:

public static int abs2(int a)
{
    return (a < 0) ? -a : a;
}

And got this:

--------------------------------------------------------------------
Test results with vals[10]: (Average of 100 runs)
  Math.abs(): 72.73299858093262 ns (combined result: -996863761)
   Foo.abs(): 72.42699935913086 ns (combined result: -996863761)
raw (no abs): 63.051000633239745 ns (combined result: 1848957913)
--------------------------------------------------------------------
Test results with vals[100]: (Average of 100 runs)
  Math.abs(): 23.246200065612793 ns (combined result: 1647719572)
   Foo.abs(): 23.09040014266968 ns (combined result: 1647719572)
raw (no abs): 13.481200141906738 ns (combined result: 2001841642)
--------------------------------------------------------------------
Test results with vals[1000]: (Average of 100 runs)
  Math.abs(): 14.749670057296752 ns (combined result: 685150735)
   Foo.abs(): 14.555380010604859 ns (combined result: 685150735)
raw (no abs): 6.045389981269836 ns (combined result: 433340265)
--------------------------------------------------------------------
Test results with vals[10000]: (Average of 100 runs)
  Math.abs(): 14.555736017227172 ns (combined result: 966036701)
   Foo.abs(): 14.627489070892334 ns (combined result: 966036701)
raw (no abs): 5.03886999130249 ns (combined result: -1169738463)
--------------------------------------------------------------------
Test results with vals[100000]: (Average of 100 runs)
  Math.abs(): 14.596469192504882 ns (combined result: -1558903801)
   Foo.abs(): 14.690511283874512 ns (combined result: -1558903801)
raw (no abs): 5.81084680557251 ns (combined result: 1243173235)

Accidentaly put case 3: instead of case 2:

Here it is.


0: 19.011356 ns (combined result: 45605829)
1: 19.629032 ns (combined result: 45605829)
2: 3.061003 ns (combined result: 728343281)
0: 17.960941 ns (combined result: -1060178124)
1: 18.297018 ns (combined result: -1060178124)
2: 3.307683 ns (combined result: -840133284)
0: 18.10258 ns (combined result: 123241034)
1: 18.174376 ns (combined result: 123241034)
2: 3.410769 ns (combined result: -1528109838)

And here is a test switching the new abs with the way the Math class does it


0: 18.6479 ns (combined result: -178609940)
1: 19.83716 ns (combined result: -178609940)
2: 3.032229 ns (combined result: 109507854)
0: 18.042517 ns (combined result: 959397402)
1: 17.889145 ns (combined result: 959397402)
2: 2.946464 ns (combined result: 81418436)
0: 18.365185 ns (combined result: 1902673406)
1: 18.029943 ns (combined result: 1902673406)
2: 3.650184 ns (combined result: -954166960)
0: 17.775164 ns (combined result: -706146910)

Maybe it is the bitshift that people were talking about. My CPU might not be able to do 31 bit shift as efficiently as yours. This is also my work machine. I will have to try this at home as well. I have an AMD Athlon 64 3000+ at home.

I’ll try it at home as well, on my spanking new AMD 64 X2 4800+ (!).

This is fun. :smiley:

Seems like a strange coincidence that the bitshifts would end up exactly the same speed as a branch… hmm

I bow to your power and drool with envy at the same time. ;D

`Average:
Math.abs = 72.35221229553223
Foo.abs = 38.08332901000976
Foo.abs2 = 64.98413646697998
raw = 27.265814304351807

Lowest:
Math.abs = 66.32128143310547
Foo.abs = 35.168434143066406
Foo.abs2 = 58.325008392333984
raw = 26.240205764770508`

100x1000000 (alternating) runs (and 50x1000000 warmup).

The CPU is a K7 500 and the vm is 1.5.0_06 client.

I get these results on a 3.06GHz P4

0: 15.937469 ns (combined result: -703529116)
1: 4.216863 ns (combined result: -703529116)
0: 16.258211 ns (combined result: -782838401)
1: 3.796482 ns (combined result: -782838401)
0: 15.778359 ns (combined result: -1823151896)
1: 3.805004 ns (combined result: -1823151896)
0: 15.950707 ns (combined result: 1636766866)
1: 4.353477 ns (combined result: 1636766866)

As I noted on the other thread, some P4 processors have a very slow shift operation.

That’s what I was going to say, at some point the dropped the barrel shifter from the hardware, iirc.

At home, with the old code:

0: 11.317748 ns (combined result: -2119606919)
1: 3.853558 ns (combined result: -2119606919)
0: 11.536717 ns (combined result: 575031759)
1: 4.281537 ns (combined result: 575031759)
0: 11.29487 ns (combined result: -1044030673)
1: 3.956503 ns (combined result: -1044030673)
0: 11.513899 ns (combined result: -423433946)
1: 4.126713 ns (combined result: -423433946)

They attempted this idiocy at Northwood. They reintroduced barrel shifter in Presscott.