# HG changeset patch # User redestad # Date 1554919387 -7200 # Node ID 9d0ae9508d5337b0dc7cc4684be42888c4023755 # Parent a805bf992bf186108bece8573d41aeb7effccf21 8222029: Optimize Math.floorMod Reviewed-by: aph, darcy diff -r a805bf992bf1 -r 9d0ae9508d53 src/java.base/share/classes/java/lang/Math.java --- a/src/java.base/share/classes/java/lang/Math.java Wed Apr 10 05:15:50 2019 -0700 +++ b/src/java.base/share/classes/java/lang/Math.java Wed Apr 10 20:03:07 2019 +0200 @@ -1274,7 +1274,12 @@ * @since 1.8 */ public static int floorMod(int x, int y) { - return x - floorDiv(x, y) * y; + int mod = x % y; + // if the signs are different and modulo not zero, adjust result + if ((mod ^ y) < 0 && mod != 0) { + mod += y; + } + return mod; } /** @@ -1301,7 +1306,7 @@ */ public static int floorMod(long x, int y) { // Result cannot overflow the range of int. - return (int)(x - floorDiv(x, y) * y); + return (int)floorMod(x, (long)y); } /** @@ -1327,7 +1332,12 @@ * @since 1.8 */ public static long floorMod(long x, long y) { - return x - floorDiv(x, y) * y; + long mod = x % y; + // if the signs are different and modulo not zero, adjust result + if ((x ^ y) < 0 && mod != 0) { + mod += y; + } + return mod; } /** diff -r a805bf992bf1 -r 9d0ae9508d53 test/jdk/java/lang/Math/DivModTests.java --- a/test/jdk/java/lang/Math/DivModTests.java Wed Apr 10 05:15:50 2019 -0700 +++ b/test/jdk/java/lang/Math/DivModTests.java Wed Apr 10 20:03:07 2019 +0200 @@ -91,6 +91,10 @@ testIntFloorDivMod(Integer.MIN_VALUE, 3, -715827883, 1); testIntFloorDivMod(Integer.MIN_VALUE + 1, 3, -715827883, 2); testIntFloorDivMod(Integer.MIN_VALUE + 1, -1, Integer.MAX_VALUE, 0); + testIntFloorDivMod(Integer.MAX_VALUE, Integer.MAX_VALUE, 1, 0); + testIntFloorDivMod(Integer.MAX_VALUE, Integer.MIN_VALUE, -1, -1); + testIntFloorDivMod(Integer.MIN_VALUE, Integer.MIN_VALUE, 1, 0); + testIntFloorDivMod(Integer.MIN_VALUE, Integer.MAX_VALUE, -2, 2147483646); // Special case of integer overflow testIntFloorDivMod(Integer.MIN_VALUE, -1, Integer.MIN_VALUE, 0); } @@ -179,6 +183,10 @@ testLongFloorDivMod(Long.MIN_VALUE, 3L, Long.MIN_VALUE / 3L - 1L, 1L); testLongFloorDivMod(Long.MIN_VALUE + 1L, 3L, Long.MIN_VALUE / 3L - 1L, 2L); testLongFloorDivMod(Long.MIN_VALUE + 1, -1, Long.MAX_VALUE, 0L); + testLongFloorDivMod(Long.MAX_VALUE, Long.MAX_VALUE, 1L, 0L); + testLongFloorDivMod(Long.MAX_VALUE, Long.MIN_VALUE, -1L, -1L); + testLongFloorDivMod(Long.MIN_VALUE, Long.MIN_VALUE, 1L, 0L); + testLongFloorDivMod(Long.MIN_VALUE, Long.MAX_VALUE, -2L, 9223372036854775806L); // Special case of integer overflow testLongFloorDivMod(Long.MIN_VALUE, -1, Long.MIN_VALUE, 0L); } @@ -283,6 +291,10 @@ testLongIntFloorDivMod(Long.MIN_VALUE, 3, Long.MIN_VALUE / 3L - 1L, 1L); testLongIntFloorDivMod(Long.MIN_VALUE + 1L, 3, Long.MIN_VALUE / 3L - 1L, 2L); testLongIntFloorDivMod(Long.MIN_VALUE + 1, -1, Long.MAX_VALUE, 0L); + testLongIntFloorDivMod(Long.MAX_VALUE, Integer.MAX_VALUE, 4294967298L, 1); + testLongIntFloorDivMod(Long.MAX_VALUE, Integer.MIN_VALUE, -4294967296L, -1); + testLongIntFloorDivMod(Long.MIN_VALUE, Integer.MIN_VALUE, 4294967296L, 0); + testLongIntFloorDivMod(Long.MIN_VALUE, Integer.MAX_VALUE, -4294967299L, 2147483645); // Special case of integer overflow testLongIntFloorDivMod(Long.MIN_VALUE, -1, Long.MIN_VALUE, 0L); } diff -r a805bf992bf1 -r 9d0ae9508d53 test/micro/org/openjdk/bench/java/lang/MathBench.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/micro/org/openjdk/bench/java/lang/MathBench.java Wed Apr 10 20:03:07 2019 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class MathBench { + + @Param("0") + public long seed; + + public int dividend; + public int divisor; + + public long longDividend; + public long longDivisor; + + @Setup + public void setupValues() { + Random random = new Random(seed); + dividend = Math.abs(random.nextInt() + 4711); + divisor = Math.abs(random.nextInt(dividend) + 17); + longDividend = Math.abs(random.nextLong() + 4711L); + longDivisor = Math.abs(random.nextLong() + longDividend); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int floorModIntIntPositive() { + return Math.floorMod(dividend, divisor); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int floorModIntInt() { + return Math.floorMod( dividend, divisor) + + Math.floorMod( dividend, -divisor) + + Math.floorMod(-dividend, divisor) + + Math.floorMod(-dividend, -divisor); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int floorModLongInt() { + return Math.floorMod( longDividend, divisor) + + Math.floorMod( longDividend, -divisor) + + Math.floorMod(-longDividend, divisor) + + Math.floorMod(-longDividend, -divisor); + } + + @Benchmark + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public long floorModLongLong() { + return Math.floorMod( longDividend, longDivisor) + + Math.floorMod( longDividend, -longDivisor) + + Math.floorMod(-longDividend, longDivisor) + + Math.floorMod(-longDividend, -longDivisor); + } + +}