Skip to content

Commit d19603d

Browse files
authored
Big arrays sliced from netty buffers (double) (#90745)
Based on #89668 but for doubles. This should allow aggregations down the road to read doubles values directly from netty buffer, rather than copying it from the netty buffer. Relates to #89437
1 parent cdddd85 commit d19603d

File tree

14 files changed

+282
-3
lines changed

14 files changed

+282
-3
lines changed

server/src/main/java/org/elasticsearch/common/bytes/AbstractBytesReference.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ public int getIntLE(int index) {
3131
return (get(index + 3) & 0xFF) << 24 | (get(index + 2) & 0xFF) << 16 | (get(index + 1) & 0xFF) << 8 | get(index) & 0xFF;
3232
}
3333

34+
@Override
35+
public long getLongLE(int index) {
36+
return (long) (get(index + 7) & 0xFF) << 56 | (long) (get(index + 6) & 0xFF) << 48 | (long) (get(index + 5) & 0xFF) << 40
37+
| (long) (get(index + 4) & 0xFF) << 32 | (long) (get(index + 3) & 0xFF) << 24 | (get(index + 2) & 0xFF) << 16 | (get(index + 1)
38+
& 0xFF) << 8 | get(index) & 0xFF;
39+
}
40+
41+
@Override
42+
public double getDoubleLE(int index) {
43+
return Double.longBitsToDouble(getLongLE(index));
44+
}
45+
3446
@Override
3547
public int indexOf(byte marker, int from) {
3648
final int to = length();

server/src/main/java/org/elasticsearch/common/bytes/BytesArray.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,9 @@ public void writeTo(OutputStream os) throws IOException {
136136
public int getIntLE(int index) {
137137
return ByteUtils.readIntLE(bytes, offset + index);
138138
}
139+
140+
@Override
141+
public double getDoubleLE(int index) {
142+
return ByteUtils.readDoubleLE(bytes, offset + index);
143+
}
139144
}

server/src/main/java/org/elasticsearch/common/bytes/BytesReference.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ static BytesReference fromByteArray(ByteArray byteArray, int length) {
129129
*/
130130
int getIntLE(int index);
131131

132+
/**
133+
* Returns the long read from the 8 bytes (LE) starting at the given index.
134+
*/
135+
long getLongLE(int index);
136+
137+
/**
138+
* Returns the double read from the 8 bytes (LE) starting at the given index.
139+
*/
140+
double getDoubleLE(int index);
141+
132142
/**
133143
* Finds the index of the first occurrence of the given marker between within the given bounds.
134144
* @param marker marker byte to search

server/src/main/java/org/elasticsearch/common/bytes/CompositeBytesReference.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,16 @@ public int getIntLE(int index) {
237237
}
238238
return super.getIntLE(index);
239239
}
240+
241+
@Override
242+
public double getDoubleLE(int index) {
243+
int i = getOffsetIndex(index);
244+
int idx = index - offsets[i];
245+
int end = idx + 8;
246+
BytesReference wholeDoublesLivesHere = references[i];
247+
if (end <= wholeDoublesLivesHere.length()) {
248+
return wholeDoublesLivesHere.getDoubleLE(idx);
249+
}
250+
return super.getDoubleLE(index);
251+
}
240252
}

server/src/main/java/org/elasticsearch/common/bytes/ReleasableBytesReference.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ public int getIntLE(int index) {
110110
return delegate.getIntLE(index);
111111
}
112112

113+
@Override
114+
public long getLongLE(int index) {
115+
assert hasReferences();
116+
return delegate.getLongLE(index);
117+
}
118+
119+
@Override
120+
public double getDoubleLE(int index) {
121+
assert hasReferences();
122+
return delegate.getDoubleLE(index);
123+
}
124+
113125
@Override
114126
public int indexOf(byte marker, int from) {
115127
assert hasReferences();

server/src/main/java/org/elasticsearch/common/util/BigArrays.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,13 @@ public void set(long index, byte[] buf, int offset, int len) {
312312
assert index >= 0 && index < size();
313313
System.arraycopy(buf, offset << 3, array, (int) index << 3, len << 3);
314314
}
315+
316+
@Override
317+
public void writeTo(StreamOutput out) throws IOException {
318+
int size = (int) size();
319+
out.writeVInt(size * 8);
320+
out.write(array, 0, size * Double.BYTES);
321+
}
315322
}
316323

317324
private static class ByteArrayAsFloatArrayWrapper extends AbstractArrayWrapper implements FloatArray {

server/src/main/java/org/elasticsearch/common/util/BigDoubleArray.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
import org.apache.lucene.util.ArrayUtil;
1212
import org.apache.lucene.util.RamUsageEstimator;
13+
import org.elasticsearch.common.io.stream.StreamOutput;
1314

15+
import java.io.IOException;
1416
import java.lang.invoke.MethodHandles;
1517
import java.lang.invoke.VarHandle;
1618
import java.nio.ByteOrder;
@@ -24,6 +26,12 @@
2426
*/
2527
final class BigDoubleArray extends AbstractBigArray implements DoubleArray {
2628

29+
static {
30+
if (ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN) {
31+
throw new Error("The deserialization assumes this class is written with little-endian numbers.");
32+
}
33+
}
34+
2735
private static final BigDoubleArray ESTIMATOR = new BigDoubleArray(0, BigArrays.NON_RECYCLING_INSTANCE, false);
2836

2937
static final VarHandle VH_PLATFORM_NATIVE_DOUBLE = MethodHandles.byteArrayViewVarHandle(double[].class, ByteOrder.nativeOrder());
@@ -123,4 +131,21 @@ public static long estimateRamBytes(final long size) {
123131
public void set(long index, byte[] buf, int offset, int len) {
124132
set(index, buf, offset, len, pages, 3);
125133
}
134+
135+
@Override
136+
public void writeTo(StreamOutput out) throws IOException {
137+
int size = (int) this.size;
138+
out.writeVInt(size * Double.BYTES);
139+
int lastPageEnd = size % DOUBLE_PAGE_SIZE;
140+
if (lastPageEnd == 0) {
141+
for (byte[] page : pages) {
142+
out.write(page);
143+
}
144+
return;
145+
}
146+
for (int i = 0; i < pages.length - 1; i++) {
147+
out.write(pages[i]);
148+
}
149+
out.write(pages[pages.length - 1], 0, lastPageEnd * Double.BYTES);
150+
}
126151
}

server/src/main/java/org/elasticsearch/common/util/DoubleArray.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@
88

99
package org.elasticsearch.common.util;
1010

11+
import org.elasticsearch.common.io.stream.StreamInput;
12+
import org.elasticsearch.common.io.stream.Writeable;
13+
14+
import java.io.IOException;
15+
1116
/**
1217
* Abstraction of an array of double values.
1318
*/
14-
public interface DoubleArray extends BigArray {
19+
public interface DoubleArray extends BigArray, Writeable {
20+
21+
static DoubleArray readFrom(StreamInput in) throws IOException {
22+
return new ReleasableDoubleArray(in);
23+
}
1524

1625
/**
1726
* Get an element given its index.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.common.util;
10+
11+
import org.apache.lucene.util.RamUsageEstimator;
12+
import org.elasticsearch.common.bytes.ReleasableBytesReference;
13+
import org.elasticsearch.common.io.stream.StreamInput;
14+
import org.elasticsearch.common.io.stream.StreamOutput;
15+
16+
import java.io.IOException;
17+
18+
class ReleasableDoubleArray implements DoubleArray {
19+
private static final long SHALLOW_SIZE = RamUsageEstimator.shallowSizeOfInstance(ReleasableDoubleArray.class);
20+
21+
private final ReleasableBytesReference ref;
22+
23+
ReleasableDoubleArray(StreamInput in) throws IOException {
24+
ref = in.readReleasableBytesReference();
25+
}
26+
27+
@Override
28+
public void writeTo(StreamOutput out) throws IOException {
29+
out.writeBytesReference(ref);
30+
}
31+
32+
@Override
33+
public long size() {
34+
return ref.length() / Long.BYTES;
35+
}
36+
37+
@Override
38+
public double get(long index) {
39+
if (index > Integer.MAX_VALUE / Long.BYTES) {
40+
// We can't serialize messages longer than 2gb anyway
41+
throw new ArrayIndexOutOfBoundsException();
42+
}
43+
return ref.getDoubleLE((int) index * Long.BYTES);
44+
}
45+
46+
@Override
47+
public double set(long index, double value) {
48+
throw new UnsupportedOperationException();
49+
}
50+
51+
@Override
52+
public double increment(long index, double inc) {
53+
throw new UnsupportedOperationException();
54+
}
55+
56+
@Override
57+
public void fill(long fromIndex, long toIndex, double value) {
58+
throw new UnsupportedOperationException();
59+
}
60+
61+
@Override
62+
public void set(long index, byte[] buf, int offset, int len) {
63+
throw new UnsupportedOperationException();
64+
}
65+
66+
@Override
67+
public long ramBytesUsed() {
68+
/*
69+
* If we return the size of the buffer that we've sliced
70+
* we're likely to double count things.
71+
*/
72+
return SHALLOW_SIZE;
73+
}
74+
75+
@Override
76+
public void close() {
77+
ref.decRef();
78+
}
79+
}

server/src/test/java/org/elasticsearch/common/bytes/BytesArrayTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,34 @@ public void testGetIntLE() {
7070
* the number of bytes in an int. Get it? I'm not sure I do either....
7171
*/
7272
}
73+
74+
public void testGetLongLE() {
75+
// first 8 bytes = 888, second 8 bytes = Long.MAX_VALUE
76+
// tag::noformat
77+
byte[] array = new byte[] {
78+
0x78, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
79+
-0x1, -0x1, -0x1, -0x1, -0x1, -0x1, -0x1, 0x7F
80+
};
81+
// end::noformat
82+
BytesReference ref = new BytesArray(array, 0, array.length);
83+
assertThat(ref.getLongLE(0), equalTo(888L));
84+
assertThat(ref.getLongLE(8), equalTo(Long.MAX_VALUE));
85+
Exception e = expectThrows(ArrayIndexOutOfBoundsException.class, () -> ref.getLongLE(9));
86+
assertThat(e.getMessage(), equalTo("Index 16 out of bounds for length 16"));
87+
}
88+
89+
public void testGetDoubleLE() {
90+
// first 8 bytes = 1.2, second 8 bytes = 1.4
91+
// tag::noformat
92+
byte[] array = new byte[] {
93+
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, -0xD, 0x3F,
94+
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, -0xA, 0x3F
95+
};
96+
// end::noformat
97+
BytesReference ref = new BytesArray(array, 0, array.length);
98+
assertThat(ref.getDoubleLE(0), equalTo(1.2));
99+
assertThat(ref.getDoubleLE(8), equalTo(1.4));
100+
Exception e = expectThrows(ArrayIndexOutOfBoundsException.class, () -> ref.getDoubleLE(9));
101+
assertThat(e.getMessage(), equalTo("Index 9 out of bounds for length 9"));
102+
}
73103
}

0 commit comments

Comments
 (0)