{{ message }}
/ jdk Public
8275821: Optimize random number generators developed in JDK-8248862 u…
```…sing Math.unsignedMultiplyHigh()

vamsi-parasa authored and Sandhya Viswanathan committed Dec 3, 2021
1 parent 780b8b1 commit 38f525e96e767597d16698d4b243b681782acc9f
Showing 4 changed files with 75 additions and 31 deletions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
@@ -323,17 +323,7 @@ public long nextLong() {
// Update the LCG subgenerator
// The LCG is, in effect, s = ((1LL << 64) + ML) * s + a, if only we had 128-bit arithmetic.
final long u = ML * sl;

// Note that Math.multiplyHigh computes the high half of the product of signed values,
// but what we need is the high half of the product of unsigned values; for this we use the
// formula "unsignedMultiplyHigh(a, b) = multiplyHigh(a, b) + ((a >> 63) & b) + ((b >> 63) & a)";
// in effect, each operand is added to the result iff the sign bit of the other operand is 1.
// (See Henry S. Warren, Jr., _Hacker's Delight_ (Second Edition), Addison-Wesley (2013),
// Section 8-3, p. 175; or see the First Edition, Addison-Wesley (2003), Section 8-3, p. 133.)
// If Math.unsignedMultiplyHigh(long, long) is ever implemented, the following line can become:
// sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
// and this entire comment can be deleted.
sh = (ML * sh) + (Math.multiplyHigh(ML, sl) + ((ML >> 63) & sl) + ((sl >> 63) & ML)) + sl + ah;
sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
sl = u + al;
if (Long.compareUnsigned(sl, u) < 0) ++sh; // Handle the carry propagation from low half to high half.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
@@ -248,16 +248,7 @@ public long nextLong() {
// Update the LCG subgenerator
// The LCG is, in effect, s = ((1LL << 64) + ML) * s + a, if only we had 128-bit arithmetic.
final long u = ML * sl;
// Note that Math.multiplyHigh computes the high half of the product of signed values,
// but what we need is the high half of the product of unsigned values; for this we use the
// formula "unsignedMultiplyHigh(a, b) = multiplyHigh(a, b) + ((a >> 63) & b) + ((b >> 63) & a)";
// in effect, each operand is added to the result iff the sign bit of the other operand is 1.
// (See Henry S. Warren, Jr., _Hacker's Delight_ (Second Edition), Addison-Wesley (2013),
// Section 8-3, p. 175; or see the First Edition, Addison-Wesley (2003), Section 8-3, p. 133.)
// If Math.unsignedMultiplyHigh(long, long) is ever implemented, the following line can become:
// sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
// and this entire comment can be deleted.
sh = (ML * sh) + (Math.multiplyHigh(ML, sl) + ((ML >> 63) & sl) + ((sl >> 63) & ML)) + sl + ah;
sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
sl = u + al;
if (Long.compareUnsigned(sl, u) < 0) ++sh; // Handle the carry propagation from low half to high half.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
@@ -269,16 +269,7 @@ public long nextLong() {
// Update the LCG subgenerator
// The LCG is, in effect, s = ((1LL << 64) + ML) * s + a, if only we had 128-bit arithmetic.
final long u = ML * sl;
// Note that Math.multiplyHigh computes the high half of the product of signed values,
// but what we need is the high half of the product of unsigned values; for this we use the
// formula "unsignedMultiplyHigh(a, b) = multiplyHigh(a, b) + ((a >> 63) & b) + ((b >> 63) & a)";
// in effect, each operand is added to the result iff the sign bit of the other operand is 1.
// (See Henry S. Warren, Jr., _Hacker's Delight_ (Second Edition), Addison-Wesley (2013),
// Section 8-3, p. 175; or see the First Edition, Addison-Wesley (2003), Section 8-3, p. 133.)
// If Math.unsignedMultiplyHigh(long, long) is ever implemented, the following line can become:
// sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
// and this entire comment can be deleted.
sh = (ML * sh) + (Math.multiplyHigh(ML, sl) + ((ML >> 63) & sl) + ((sl >> 63) & ML)) + sl + ah;
sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
sl = u + al;
if (Long.compareUnsigned(sl, u) < 0) ++sh; // Handle the carry propagation from low half to high half.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
@@ -0,0 +1,72 @@
/*
* 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.util;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Param;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.concurrent.TimeUnit;

/**
* Tests java.util.random.RandomGenerator's different random number generators which use Math.unsignedMultiplyHigh().
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class RandomGeneratorNext {

RandomGenerator randomGenerator;

@Param({"L128X128MixRandom", "L128X256MixRandom", "L128X1024MixRandom"})
String randomGeneratorName;

long[] buffer;

@Param("1024")
int size;

@Setup
public void setup() {
buffer = new long[size];
randomGenerator = RandomGeneratorFactory.of(randomGeneratorName).create(randomGeneratorName.hashCode());
}

@Benchmark
public long testNextLong() {
return randomGenerator.nextLong();
}

@Benchmark
public long[] testFillBufferWithNextLong() {
for (int i = 0; i < size; i++) buffer[i] = randomGenerator.nextLong();
return buffer;
}

}