Skip to content

Commit 801b013

Browse files
Make o.e.index.codec.ForUtil a pure utility class (#107348)
We've run into heap dumps that had instances of this class consume tens and in one case more than a hundred MB of heap. It seems reasonable to use a thread-local for the `tmp` long array and trade the cost of looking up the thread-local for the memory savings and cycles saved for allocating and assigning instances.
1 parent 07aa9cd commit 801b013

File tree

7 files changed

+30
-35
lines changed

7 files changed

+30
-35
lines changed

server/src/main/java/org/elasticsearch/index/codec/ForUtil.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ public final class ForUtil {
2222

2323
public static final int BLOCK_SIZE = 128;
2424
private static final int BLOCK_SIZE_LOG2 = 7;
25-
private final long[] tmp = new long[BLOCK_SIZE / 2];
25+
private static final ThreadLocal<long[]> scratch = ThreadLocal.withInitial(() -> new long[BLOCK_SIZE / 2]);
26+
27+
private ForUtil() {}
2628

2729
private static long expandMask32(long mask32) {
2830
return mask32 | (mask32 << 32);
@@ -118,7 +120,7 @@ private static void collapse32(long[] arr) {
118120
}
119121

120122
/** Encode 128 integers from {@code longs} into {@code out}. */
121-
public void encode(long[] longs, int bitsPerValue, DataOutput out) throws IOException {
123+
public static void encode(long[] longs, int bitsPerValue, DataOutput out) throws IOException {
122124
final int nextPrimitive;
123125
final int numLongs;
124126
if (bitsPerValue <= 8) {
@@ -138,6 +140,7 @@ public void encode(long[] longs, int bitsPerValue, DataOutput out) throws IOExce
138140
final int numLongsPerShift = bitsPerValue * 2;
139141
int idx = 0;
140142
int shift = nextPrimitive - bitsPerValue;
143+
final long[] tmp = scratch.get();
141144
for (int i = 0; i < numLongsPerShift; ++i) {
142145
tmp[i] = longs[idx++] << shift;
143146
}
@@ -191,7 +194,7 @@ public void encode(long[] longs, int bitsPerValue, DataOutput out) throws IOExce
191194
}
192195

193196
/** Number of bytes required to encode 128 integers of {@code bitsPerValue} bits per value. */
194-
public int numBytes(int bitsPerValue) {
197+
public static int numBytes(int bitsPerValue) {
195198
return bitsPerValue << (BLOCK_SIZE_LOG2 - 3);
196199
}
197200

@@ -299,7 +302,8 @@ private static void shiftLongs(long[] a, int count, long[] b, int bi, int shift,
299302
private static final long MASK32_24 = MASKS32[24];
300303

301304
/** Decode 128 integers into {@code longs}. */
302-
public void decode(int bitsPerValue, DataInput in, long[] longs) throws IOException {
305+
public static void decode(int bitsPerValue, DataInput in, long[] longs) throws IOException {
306+
final long[] tmp = scratch.get();
303307
switch (bitsPerValue) {
304308
case 1:
305309
decode1(in, tmp, longs);
@@ -410,7 +414,8 @@ public void decode(int bitsPerValue, DataInput in, long[] longs) throws IOExcept
410414
* [0..63], and values [64..127] are encoded in the low-order bits of {@code longs} [0..63]. This
411415
* representation may allow subsequent operations to be performed on two values at a time.
412416
*/
413-
public void decodeTo32(int bitsPerValue, DataInput in, long[] longs) throws IOException {
417+
public static void decodeTo32(int bitsPerValue, DataInput in, long[] longs) throws IOException {
418+
final long[] tmp = scratch.get();
414419
switch (bitsPerValue) {
415420
case 1:
416421
decode1(in, tmp, longs);

server/src/main/java/org/elasticsearch/index/codec/postings/ES812PostingsReader.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ final class ES812PostingsReader extends PostingsReaderBase {
6161
private final IndexInput posIn;
6262
private final IndexInput payIn;
6363

64-
private final int version;
65-
6664
/** Sole constructor. */
6765
ES812PostingsReader(SegmentReadState state) throws IOException {
6866
boolean success = false;
@@ -78,7 +76,7 @@ final class ES812PostingsReader extends PostingsReaderBase {
7876
String docName = IndexFileNames.segmentFileName(state.segmentInfo.name, state.segmentSuffix, ES812PostingsFormat.DOC_EXTENSION);
7977
try {
8078
docIn = state.directory.openInput(docName, state.context);
81-
version = CodecUtil.checkIndexHeader(
79+
int version = CodecUtil.checkIndexHeader(
8280
docIn,
8381
DOC_CODEC,
8482
VERSION_START,
@@ -279,7 +277,7 @@ public ImpactsEnum impacts(FieldInfo fieldInfo, BlockTermState state, int flags)
279277

280278
final class BlockDocsEnum extends PostingsEnum {
281279

282-
final PForUtil pforUtil = new PForUtil(new ForUtil());
280+
final PForUtil pforUtil = new PForUtil();
283281

284282
private final long[] docBuffer = new long[BLOCK_SIZE + 1];
285283
private final long[] freqBuffer = new long[BLOCK_SIZE];
@@ -526,7 +524,7 @@ public long cost() {
526524
// Also handles payloads + offsets
527525
final class EverythingEnum extends PostingsEnum {
528526

529-
final PForUtil pforUtil = new PForUtil(new ForUtil());
527+
final PForUtil pforUtil = new PForUtil();
530528

531529
private final long[] docBuffer = new long[BLOCK_SIZE + 1];
532530
private final long[] freqBuffer = new long[BLOCK_SIZE + 1];
@@ -999,7 +997,7 @@ public long cost() {
999997

1000998
final class BlockImpactsDocsEnum extends ImpactsEnum {
1001999

1002-
final PForUtil pforUtil = new PForUtil(new ForUtil());
1000+
final PForUtil pforUtil = new PForUtil();
10031001

10041002
private final long[] docBuffer = new long[BLOCK_SIZE + 1];
10051003
private final long[] freqBuffer = new long[BLOCK_SIZE];
@@ -1196,7 +1194,7 @@ public long cost() {
11961194

11971195
final class BlockImpactsPostingsEnum extends ImpactsEnum {
11981196

1199-
final PForUtil pforUtil = new PForUtil(new ForUtil());
1197+
final PForUtil pforUtil = new PForUtil();
12001198

12011199
private final long[] docBuffer = new long[BLOCK_SIZE];
12021200
private final long[] freqBuffer = new long[BLOCK_SIZE];
@@ -1476,7 +1474,7 @@ public long cost() {
14761474

14771475
final class BlockImpactsEverythingEnum extends ImpactsEnum {
14781476

1479-
final PForUtil pforUtil = new PForUtil(new ForUtil());
1477+
final PForUtil pforUtil = new PForUtil();
14801478

14811479
private final long[] docBuffer = new long[BLOCK_SIZE];
14821480
private final long[] freqBuffer = new long[BLOCK_SIZE];

server/src/main/java/org/elasticsearch/index/codec/postings/ES812PostingsWriter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import org.apache.lucene.util.BitUtil;
3636
import org.apache.lucene.util.BytesRef;
3737
import org.elasticsearch.core.IOUtils;
38-
import org.elasticsearch.index.codec.ForUtil;
3938
import org.elasticsearch.index.codec.postings.ES812PostingsFormat.IntBlockTermState;
4039

4140
import java.io.IOException;
@@ -110,7 +109,7 @@ final class ES812PostingsWriter extends PushPostingsWriterBase {
110109
boolean success = false;
111110
try {
112111
CodecUtil.writeIndexHeader(docOut, DOC_CODEC, VERSION_CURRENT, state.segmentInfo.getId(), state.segmentSuffix);
113-
pforUtil = new PForUtil(new ForUtil());
112+
pforUtil = new PForUtil();
114113
if (state.fieldInfos.hasProx()) {
115114
posDeltaBuffer = new long[BLOCK_SIZE];
116115
String posFileName = IndexFileNames.segmentFileName(

server/src/main/java/org/elasticsearch/index/codec/postings/PForUtil.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,12 @@ static boolean allEqual(long[] l) {
5252
return true;
5353
}
5454

55-
private final ForUtil forUtil;
5655
// buffer for reading exception data; each exception uses two bytes (pos + high-order bits of the
5756
// exception)
5857
private final byte[] exceptionBuff = new byte[MAX_EXCEPTIONS * 2];
5958

60-
PForUtil(ForUtil forUtil) {
59+
PForUtil() {
6160
assert ForUtil.BLOCK_SIZE <= 256 : "blocksize must fit in one byte. got " + ForUtil.BLOCK_SIZE;
62-
this.forUtil = forUtil;
6361
}
6462

6563
/** Encode 128 integers from {@code longs} into {@code out}. */
@@ -114,7 +112,7 @@ void encode(long[] longs, DataOutput out) throws IOException {
114112
} else {
115113
final int token = (numExceptions << 5) | patchedBitsRequired;
116114
out.writeByte((byte) token);
117-
forUtil.encode(longs, patchedBitsRequired, out);
115+
ForUtil.encode(longs, patchedBitsRequired, out);
118116
}
119117
out.writeBytes(exceptions, exceptions.length);
120118
}
@@ -127,7 +125,7 @@ void decode(DataInput in, long[] longs) throws IOException {
127125
if (bitsPerValue == 0) {
128126
Arrays.fill(longs, 0, ForUtil.BLOCK_SIZE, in.readVLong());
129127
} else {
130-
forUtil.decode(bitsPerValue, in, longs);
128+
ForUtil.decode(bitsPerValue, in, longs);
131129
}
132130
for (int i = 0; i < numExceptions; ++i) {
133131
longs[Byte.toUnsignedInt(in.readByte())] |= Byte.toUnsignedLong(in.readByte()) << bitsPerValue;
@@ -153,15 +151,15 @@ void decodeAndPrefixSum(DataInput in, long base, long[] longs) throws IOExceptio
153151
}
154152
} else {
155153
// decode the deltas then apply the prefix sum logic
156-
forUtil.decodeTo32(bitsPerValue, in, longs);
154+
ForUtil.decodeTo32(bitsPerValue, in, longs);
157155
prefixSum32(longs, base);
158156
}
159157
} else {
160158
// pack two values per long so we can apply prefixes two-at-a-time
161159
if (bitsPerValue == 0) {
162160
fillSameValue32(longs, in.readVLong());
163161
} else {
164-
forUtil.decodeTo32(bitsPerValue, in, longs);
162+
ForUtil.decodeTo32(bitsPerValue, in, longs);
165163
}
166164
applyExceptions32(bitsPerValue, numExceptions, in, longs);
167165
prefixSum32(longs, base);
@@ -177,7 +175,7 @@ void skip(DataInput in) throws IOException {
177175
in.readVLong();
178176
in.skipBytes((numExceptions << 1));
179177
} else {
180-
in.skipBytes(forUtil.numBytes(bitsPerValue) + (numExceptions << 1));
178+
in.skipBytes(ForUtil.numBytes(bitsPerValue) + (numExceptions << 1));
181179
}
182180
}
183181

server/src/main/java/org/elasticsearch/index/codec/tsdb/DocValuesForUtil.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public class DocValuesForUtil {
2020
private static final int BITS_IN_FIVE_BYTES = 5 * Byte.SIZE;
2121
private static final int BITS_IN_SIX_BYTES = 6 * Byte.SIZE;
2222
private static final int BITS_IN_SEVEN_BYTES = 7 * Byte.SIZE;
23-
private final ForUtil forUtil = new ForUtil();
2423
private final int blockSize;
2524
private final byte[] encoded;
2625

@@ -50,7 +49,7 @@ public static int roundBits(int bitsPerValue) {
5049

5150
public void encode(long[] in, int bitsPerValue, final DataOutput out) throws IOException {
5251
if (bitsPerValue <= 24) { // these bpvs are handled efficiently by ForUtil
53-
forUtil.encode(in, bitsPerValue, out);
52+
ForUtil.encode(in, bitsPerValue, out);
5453
} else if (bitsPerValue <= 32) {
5554
collapse32(in);
5655
for (int i = 0; i < blockSize / 2; ++i) {
@@ -76,7 +75,7 @@ private void encodeFiveSixOrSevenBytesPerValue(long[] in, int bitsPerValue, fina
7675

7776
public void decode(int bitsPerValue, final DataInput in, long[] out) throws IOException {
7877
if (bitsPerValue <= 24) {
79-
forUtil.decode(bitsPerValue, in, out);
78+
ForUtil.decode(bitsPerValue, in, out);
8079
} else if (bitsPerValue <= 32) {
8180
in.readLongs(out, 0, blockSize / 2);
8281
expand32(out);

server/src/test/java/org/elasticsearch/index/codec/ForUtilTests.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ public void testEncodeDecode() throws IOException {
5353
{
5454
// encode
5555
IndexOutput out = d.createOutput("test.bin", IOContext.DEFAULT);
56-
final ForUtil forUtil = new ForUtil();
5756

5857
for (int i = 0; i < iterations; ++i) {
5958
long[] source = new long[ForUtil.BLOCK_SIZE];
@@ -64,7 +63,7 @@ public void testEncodeDecode() throws IOException {
6463
}
6564
final int bpv = PackedInts.bitsRequired(or);
6665
out.writeByte((byte) bpv);
67-
forUtil.encode(source, bpv, out);
66+
ForUtil.encode(source, bpv, out);
6867
}
6968
endPointer = out.getFilePointer();
7069
out.close();
@@ -73,12 +72,11 @@ public void testEncodeDecode() throws IOException {
7372
{
7473
// decode
7574
IndexInput in = d.openInput("test.bin", IOContext.READONCE);
76-
final ForUtil forUtil = new ForUtil();
7775
for (int i = 0; i < iterations; ++i) {
7876
final int bitsPerValue = in.readByte();
7977
final long currentFilePointer = in.getFilePointer();
8078
final long[] restored = new long[ForUtil.BLOCK_SIZE];
81-
forUtil.decode(bitsPerValue, in, restored);
79+
ForUtil.decode(bitsPerValue, in, restored);
8280
int[] ints = new int[ForUtil.BLOCK_SIZE];
8381
for (int j = 0; j < ForUtil.BLOCK_SIZE; ++j) {
8482
ints[j] = Math.toIntExact(restored[j]);
@@ -88,7 +86,7 @@ public void testEncodeDecode() throws IOException {
8886
ArrayUtil.copyOfSubArray(values, i * ForUtil.BLOCK_SIZE, (i + 1) * ForUtil.BLOCK_SIZE),
8987
ints
9088
);
91-
assertEquals(forUtil.numBytes(bitsPerValue), in.getFilePointer() - currentFilePointer);
89+
assertEquals(ForUtil.numBytes(bitsPerValue), in.getFilePointer() - currentFilePointer);
9290
}
9391
assertEquals(endPointer, in.getFilePointer());
9492
in.close();

server/src/test/java/org/elasticsearch/index/codec/tsdb/DocValuesForUtilTests.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
import java.util.Random;
3131

3232
public class DocValuesForUtilTests extends LuceneTestCase {
33-
private final ES87TSDBDocValuesEncoder encoder = new ES87TSDBDocValuesEncoder();
34-
private static final ForUtil forUtil = new ForUtil();
3533

3634
public void testEncodeDecode() throws IOException {
3735
final int iterations = RandomNumbers.randomIntBetween(random(), 50, 1000);
@@ -105,14 +103,14 @@ public void testEncodeDecodeBitsPerValue() throws IOException {
105103
// Encode
106104
DataOutput dataOutput = new ByteArrayDataOutput(dataOutputBuffer);
107105
long[] encodeBuffer = Arrays.copyOf(values, values.length);
108-
forUtil.encode(encodeBuffer, bitsPerValue, dataOutput);
106+
ForUtil.encode(encodeBuffer, bitsPerValue, dataOutput);
109107

110108
// Prepare for decoding
111109
DataInput dataInput = new ByteArrayDataInput(dataInputBuffer);
112110
System.arraycopy(dataOutputBuffer, 0, dataInputBuffer, 0, dataOutputBuffer.length);
113111

114112
// Decode
115-
forUtil.decode(bitsPerValue, dataInput, decodeBuffer);
113+
ForUtil.decode(bitsPerValue, dataInput, decodeBuffer);
116114

117115
assertArrayEquals(decodeBuffer, values);
118116
}

0 commit comments

Comments
 (0)