Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6296690

Math.round() is extremely slow

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • tbd
    • 5.0
    • core-libs
    • x86
    • windows_xp

      FULL PRODUCT VERSION :
      Tested on both JDK 1.5.0_04 and 1.6.0-ea-b42.


      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]

      A DESCRIPTION OF THE PROBLEM :
      Seems that Math.round() is extremely slow.

      On my Centrino laptop it takes about 200 cycles to complete, when resonable value is about 5 cycles, I would say.

      It is so slow, that doing it manually, in Java, using Double.doubleToRawLongBits, actually makes it 10 times faster!

      Normally I would not submit a performance issue as a bug, but this one really extreme.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Just call Math.round, and see how slow it is.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      That calling it will take ~5 machine cycles.
      ACTUAL -
      It takes ~200 machine cycles.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------

      /**
       * Demo program, that shows how slow {@link Math#round(double)} is.
       *
       * @author Doron Rajwan, 2005.
       */
      public class TestRound {

          private static final double x0 = 500000.0, y0=3000000.0, inverseBinSizeX=1.0/30.0, inverseBinSizeY=1.0/20.0;
          private static final int samples = 12345678;
          private static final double deltaX = 12345.0 / (5 + samples);
          private static final double deltaY = 23456.0 / (5 + samples);

          private static void performanceTestStandard() {
              long start = System.nanoTime();
              int sum = 0;
              double xx = 777.1, yy = 888.1;
              for (int i = 0; i < samples; ++i) {
                  xx += deltaX; yy += deltaY;
                  int binI = (int) Math.round((xx - x0) * inverseBinSizeX);
                  int binJ = (int) Math.round((yy - y0) * inverseBinSizeY);
                  sum += binI + binJ;
              }
              long time = System.nanoTime() - start;
              System.out.println("Using standard round: time per iteration=" + ((double)time / samples) + " ns, sum=" + sum);
              if (sum != 1734030338)
                  throw new AssertionError(); // force assertions here...
          }

          private static void performanceTestFast() {
              long start = System.nanoTime();
              int sum = 0;
              double xx = 777.1, yy = 888.1;
              for (int i = 0; i < samples; ++i) {
                  xx += deltaX; yy += deltaY;
                  int binI = fastRound((xx - x0) * inverseBinSizeX);
                  int binJ = fastRound((yy - y0) * inverseBinSizeY);
                  sum += binI + binJ;
              }
              long time = System.nanoTime() - start;
              System.out.println("Using FAST round: time per iteration=" + ((double)time / samples) + " ns, sum=" + sum);
              if (sum != 1734030338)
                  throw new AssertionError(); // force assertions here...
          }

          private static final double twoToThe52 = (double)(1L << 52); // 2^52

          // Works like round(), but with the following differences:
          // 1. more than x10 faster.
          // 2. rounds halfs towards even.
          private static int fastRound(double a) {
              double dd = twoToThe52 + Math.abs(a);
              int ll = (int)Double.doubleToRawLongBits(dd);
              int signMask = (int)(Double.doubleToRawLongBits(a) >> 63); // 0 or -1.
              return (ll ^ signMask) - signMask;
          }

          public static void main(String[] args) {
              performanceTestStandard(); performanceTestFast();
              performanceTestStandard(); performanceTestFast();
              performanceTestStandard(); performanceTestFast();
              performanceTestStandard(); performanceTestFast();
              performanceTestStandard(); performanceTestFast();
          }

          // Sample output on my 1.5GHz Centrino laptop, using "-server -Xbatch" command line:
          // Using standard round: time per iteration=223.8108062594861 ns, sum=1734030338
          // Using FAST round: time per iteration=22.15142999841726 ns, sum=1734030338
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      See my sample code for workaround...
      ###@###.### 2005-07-14 00:29:52 GMT

            bpb Brian Burkhalter
            gmanwanisunw Girish Manwani (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: