Skip to content

Commit 7c1e605

Browse files
blambovdjatnieks
authored andcommitted
Improve typed reads in RandomAccessReader
1 parent eec2ef1 commit 7c1e605

File tree

1 file changed

+81
-127
lines changed

1 file changed

+81
-127
lines changed

src/java/org/apache/cassandra/io/util/RandomAccessReader.java

Lines changed: 81 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.apache.cassandra.io.util;
1919

2020
import java.io.IOException;
21+
import java.nio.ByteBuffer;
2122
import java.nio.ByteOrder;
2223
import java.nio.FloatBuffer;
2324
import java.nio.IntBuffer;
@@ -92,165 +93,118 @@ public ByteOrder order()
9293
@Override
9394
public void read(float[] dest, int offset, int count) throws IOException
9495
{
95-
var copied = 0;
96-
while (copied < count)
96+
for (int inBuffer = buffer.remaining() / Float.BYTES;
97+
inBuffer < count;
98+
inBuffer = buffer.remaining() / Float.BYTES)
9799
{
98-
var bh = bufferHolder;
99-
long position = getPosition();
100-
101-
FloatBuffer floatBuffer;
102-
if (bh.offset() == 0 && position % Float.BYTES == 0 && bh.order() == order)
103-
{
104-
// this is a separate code path because buffer() and asFloatBuffer() both allocate
105-
// new and relatively expensive xBuffer objects, so we want to avoid doing that
106-
// twice, where possible. If the BufferHandler has a different underlying
107-
// byte order, we duplicate first because there is not yet a way to configure
108-
// the buffer handler to use the correct byte order.
109-
floatBuffer = bh.floatBuffer();
110-
floatBuffer.position(Ints.checkedCast(position / Float.BYTES));
111-
}
112-
else
100+
if (inBuffer >= 1)
113101
{
114-
// bufferHolder offset is non-zero, and probably not aligned to Float.BYTES, so
115-
// set the position before converting to FloatBuffer.
116-
var bb = bh.buffer();
117-
bb.order(order);
118-
bb.position(Ints.checkedCast(position - bh.offset()));
119-
floatBuffer = bb.asFloatBuffer();
102+
// read as much as we can from the buffer
103+
readFloats(buffer, order, dest, offset, inBuffer);
104+
offset += inBuffer;
105+
count -= inBuffer;
120106
}
121107

122-
var remaining = floatBuffer.remaining();
123-
if (remaining == 0)
108+
if (buffer.remaining() > 0)
124109
{
125-
// slow path -- the next float bytes are across a buffer boundary (we never start a loop iteration with
126-
// the "current" buffer fully exhausted, so `remaining == 0` truly means "some bytes remains, but not
127-
// enough for a float"), so we read that float individually (which will read it byte by byte,
128-
// reBuffering as needed). After that we loop, which will switch back to the faster path for any
129-
// remaining floats in the newly reloaded buffer.
130-
dest[offset + copied] = readFloat();
131-
seek(position + Float.BYTES);
132-
copied++;
110+
// read the buffer-spanning value using the slow path
111+
dest[offset++] = readFloat();
112+
--count;
133113
}
134114
else
135-
{
136-
var elementsToRead = Math.min(remaining, count - copied);
137-
floatBuffer.get(dest, offset + copied, elementsToRead);
138-
seek(position + ((long) elementsToRead * Float.BYTES));
139-
copied += elementsToRead;
140-
}
115+
reBuffer();
141116
}
117+
118+
readFloats(buffer, order, dest, offset, count);
142119
}
143120

144121
@Override
145-
public void readFully(long[] dest) throws IOException {
146-
int copied = 0;
147-
while (copied < dest.length)
148-
{
149-
var bh = bufferHolder;
150-
long position = getPosition();
122+
public void readFully(long[] dest) throws IOException
123+
{
124+
read(dest, 0, dest.length);
125+
}
151126

152-
LongBuffer longBuffer;
153-
if (bh.offset() == 0 && position % Long.BYTES == 0 && bh.order() == order)
154-
{
155-
// this is a separate code path because buffer() and asLongBuffer() both allocate
156-
// new and relatively expensive xBuffer objects, so we want to avoid doing that
157-
// twice, where possible. If the BufferHandler has a different underlying
158-
// byte order, we duplicate first because there is not yet a way to configure
159-
// the buffer handler to use the correct byte order.
160-
longBuffer = bh.longBuffer();
161-
longBuffer.position(Ints.checkedCast(position / Long.BYTES));
162-
}
163-
else
127+
public void read(long[] dest, int offset, int count) throws IOException
128+
{
129+
for (int inBuffer = buffer.remaining() / Long.BYTES;
130+
inBuffer < count;
131+
inBuffer = buffer.remaining() / Long.BYTES)
132+
{
133+
if (inBuffer >= 1)
164134
{
165-
// offset is non-zero, and probably not aligned to Long.BYTES, so
166-
// set the position before converting to LongBuffer.
167-
var bb = bh.buffer();
168-
bb.order(order);
169-
bb.position(Ints.checkedCast(position - bh.offset()));
170-
longBuffer = bb.asLongBuffer();
135+
// read as much as we can from the buffer
136+
readLongs(buffer, order, dest, offset, inBuffer);
137+
offset += inBuffer;
138+
count -= inBuffer;
171139
}
172140

173-
var remaining = longBuffer.remaining();
174-
if (remaining == 0)
141+
if (buffer.remaining() > 0)
175142
{
176-
// slow path -- the next long bytes are across a buffer boundary (we never start a loop iteration with
177-
// the "current" buffer fully exhausted, so `remaining == 0` truly means "some bytes remains, but not
178-
// enough for a long"), so we read that long individually (which will read it byte by byte,
179-
// reBuffering as needed). After that we loop, which will switch back to the faster path for any
180-
// remaining longs in the newly reloaded buffer.
181-
dest[copied] = readLong();
182-
seek(position + Long.BYTES);
183-
copied++;
143+
// read the buffer-spanning value using the slow path
144+
dest[offset++] = readLong();
145+
--count;
184146
}
185147
else
186-
{
187-
var elementsToRead = Math.min(remaining, dest.length - copied);
188-
longBuffer.get(dest, copied, elementsToRead);
189-
seek(position + ((long) elementsToRead * Long.BYTES));
190-
copied += elementsToRead;
191-
}
148+
reBuffer();
192149
}
150+
151+
readLongs(buffer, order, dest, offset, count);
193152
}
194153

195-
/**
196-
* Read ints into an int[], starting at the current position.
197-
*
198-
* @param dest the array to read into
199-
* @param offset the offset in the array at which to start writing ints
200-
* @param count the number of ints to read
201-
*
202-
* Will change the buffer position.
203-
*/
204154
@Override
205155
public void read(int[] dest, int offset, int count) throws IOException
206156
{
207-
int copied = 0;
208-
while (copied < count)
157+
for (int inBuffer = buffer.remaining() / Integer.BYTES;
158+
inBuffer < count;
159+
inBuffer = buffer.remaining() / Integer.BYTES)
209160
{
210-
var bh = bufferHolder;
211-
long position = getPosition();
212-
213-
IntBuffer intBuffer;
214-
if (bh.offset() == 0 && position % Integer.BYTES == 0 && bh.order() == order)
215-
{
216-
// this is a separate code path because buffer() and asIntBuffer() both allocate
217-
// new and relatively expensive xBuffer objects, so we want to avoid doing that
218-
// twice, where possible. If the BufferHandler has a different underlying
219-
// byte order, we duplicate first because there is not yet a way to configure
220-
// the buffer handler to use the correct byte order.
221-
intBuffer = bh.intBuffer();
222-
intBuffer.position(Ints.checkedCast(position / Integer.BYTES));
223-
}
224-
else
161+
if (inBuffer >= 1)
225162
{
226-
// offset is non-zero, and probably not aligned to Integer.BYTES, so
227-
// set the position before converting to IntBuffer.
228-
var bb = bh.buffer();
229-
bb.order(order);
230-
bb.position(Ints.checkedCast(position - bh.offset()));
231-
intBuffer = bb.asIntBuffer();
163+
// read as much as we can from the buffer
164+
readInts(buffer, order, dest, offset, inBuffer);
165+
offset += inBuffer;
166+
count -= inBuffer;
232167
}
233168

234-
var remaining = intBuffer.remaining();
235-
if (remaining == 0)
169+
if (buffer.remaining() > 0)
236170
{
237-
// slow path -- the next int bytes are across a buffer boundary (we never start a loop iteration with
238-
// the "current" buffer fully exhausted, so `remaining == 0` truly means "some bytes remains, but not
239-
// enough for an int"), so we read that int individually (which will read it byte by byte,
240-
// reBuffering as needed). After that we loop, which will switch back to the faster path for any
241-
// remaining ints in the newly reloaded buffer.
242-
dest[offset + copied] = readInt();
243-
seek(position + Integer.BYTES);
244-
copied++;
171+
// read the buffer-spanning value using the slow path
172+
dest[offset++] = readInt();
173+
--count;
245174
}
246175
else
247-
{
248-
var elementsToRead = Math.min(remaining, count - copied);
249-
intBuffer.get(dest, offset + copied, elementsToRead);
250-
seek(position + ((long) elementsToRead * Integer.BYTES));
251-
copied += elementsToRead;
252-
}
176+
reBuffer();
253177
}
178+
179+
readInts(buffer, order, dest, offset, count);
180+
}
181+
182+
private static void readFloats(ByteBuffer buffer, ByteOrder order, float[] dest, int offset, int count)
183+
{
184+
FloatBuffer floatBuffer = updateBufferByteOrderIfNeeded(buffer, order).asFloatBuffer();
185+
floatBuffer.get(dest, offset, count);
186+
buffer.position(buffer.position() + count * Float.BYTES);
187+
}
188+
189+
private static void readLongs(ByteBuffer buffer, ByteOrder order, long[] dest, int offset, int count)
190+
{
191+
LongBuffer longBuffer = updateBufferByteOrderIfNeeded(buffer, order).asLongBuffer();
192+
longBuffer.get(dest, offset, count);
193+
buffer.position(buffer.position() + count * Long.BYTES);
194+
}
195+
196+
private static void readInts(ByteBuffer buffer, ByteOrder order, int[] dest, int offset, int count)
197+
{
198+
IntBuffer intBuffer = updateBufferByteOrderIfNeeded(buffer, order).asIntBuffer();
199+
intBuffer.get(dest, offset, count);
200+
buffer.position(buffer.position() + count * Integer.BYTES);
201+
}
202+
203+
private static ByteBuffer updateBufferByteOrderIfNeeded(ByteBuffer buffer, ByteOrder order)
204+
{
205+
return buffer.order() != order
206+
? buffer.duplicate().order(order)
207+
: buffer; // Note: ?: rather than if to hit one-liner inlining path
254208
}
255209

256210
@Override

0 commit comments

Comments
 (0)