Fix contention in StringHelper.randomId

Seeing this mutex contended up to O(10ms) in Elasticsearch
at times. Moving to CAS and removing the unnecessary alloction
of a new instance for the bitwise-and with the mask makes
this perform+scale much better.
This commit is contained in:
Armin Braun 2024-09-21 12:31:43 +02:00
parent 53d1c2bd2f
commit dd006f56f7
1 changed files with 12 additions and 15 deletions

View File

@ -22,6 +22,7 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
/**
* Methods for manipulating strings.
@ -210,16 +211,9 @@ public abstract class StringHelper {
}
// Holds 128 bit unsigned value:
private static BigInteger nextId;
private static final BigInteger mask128;
private static final Object idLock = new Object();
private static final AtomicReference<BigInteger> nextId;
static {
// 128 bit unsigned mask
byte[] maskBytes128 = new byte[16];
Arrays.fill(maskBytes128, (byte) 0xff);
mask128 = new BigInteger(1, maskBytes128);
String prop = System.getProperty("tests.seed");
// State for xorshift128:
@ -287,7 +281,7 @@ public abstract class StringHelper {
BigInteger unsignedX1 = BigInteger.valueOf(x1).and(mask64);
// Concatentate bits of x0 and x1, as unsigned 128 bit integer:
nextId = unsignedX0.shiftLeft(64).or(unsignedX1);
nextId = new AtomicReference<>(unsignedX0.shiftLeft(64).or(unsignedX1));
}
/** length in bytes of an ID */
@ -310,12 +304,15 @@ public abstract class StringHelper {
// what impact that has on the period, whereas the simple ++ (mod 2^128)
// we use here is guaranteed to have the full period.
byte[] bits;
synchronized (idLock) {
bits = nextId.toByteArray();
nextId = nextId.add(BigInteger.ONE).and(mask128);
}
BigInteger current;
BigInteger next;
do {
current = nextId.get();
next = current.add(BigInteger.ONE);
// 128bit value -> unsigned overflow once the 129th bit at index 128 is set
next = next.testBit(128) ? BigInteger.ZERO : next;
} while (nextId.weakCompareAndSetVolatile(current, next) == false);
byte[] bits = current.toByteArray();
// toByteArray() always returns a sign bit, so it may require an extra byte (always zero)
if (bits.length > ID_LENGTH) {
assert bits.length == ID_LENGTH + 1;