Increased performance of non-random-access read/write by switching to block-aligned mode only when necessary.

This commit is contained in:
Sebastian Stenzel
2016-01-06 10:50:31 +01:00
parent 94ed3a6b7c
commit a8f53b7084
4 changed files with 91 additions and 1 deletions

View File

@@ -20,6 +20,11 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
private final int blockSize;
private final ByteBuffer currentBlockBuffer;
private boolean eofReached = false;
private Mode mode = Mode.PASSTHROUGH;
private enum Mode {
BLOCK_ALIGNED, PASSTHROUGH;
}
public BlockAlignedReadableFile(ReadableFile delegate, int blockSize) {
super(delegate);
@@ -28,11 +33,12 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
}
this.blockSize = blockSize;
this.currentBlockBuffer = ByteBuffer.allocate(blockSize);
this.currentBlockBuffer.flip(); // so the next attempt will read from source.
this.currentBlockBuffer.flip(); // so remaining() is 0 -> next read will read from physical source.
}
@Override
public void position(long logicalPosition) throws UncheckedIOException {
switchToBlockAlignedMode();
long blockNumber = logicalPosition / blockSize;
long physicalPosition = blockNumber * blockSize;
assert physicalPosition <= logicalPosition;
@@ -45,8 +51,24 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
currentBlockBuffer.position(diff);
}
// visible for testing
void switchToBlockAlignedMode() {
mode = Mode.BLOCK_ALIGNED;
}
@Override
public int read(ByteBuffer target) throws UncheckedIOException {
switch (mode) {
case PASSTHROUGH:
return super.read(target);
case BLOCK_ALIGNED:
return readBlockAligned(target);
default:
throw new IllegalStateException("Unsupported mode " + mode);
}
}
private int readBlockAligned(ByteBuffer target) {
int read = -1;
while (!eofReached && target.hasRemaining()) {
read += ByteBuffers.copy(currentBlockBuffer, target);

View File

@@ -21,6 +21,11 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
private final int blockSize;
private final ReadableFile readableFile;
private final ByteBuffer currentBlockBuffer;
private Mode mode = Mode.PASSTHROUGH;
private enum Mode {
BLOCK_ALIGNED, PASSTHROUGH;
}
public BlockAlignedWritableFile(WritableFile delegate, ReadableFile readableFile, int blockSize) {
super(delegate);
@@ -31,6 +36,7 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
@Override
public void position(long logicalPosition) throws UncheckedIOException {
switchToBlockAlignedMode();
long blockNumber = logicalPosition / blockSize;
long physicalPosition = blockNumber * blockSize;
readableFile.position(physicalPosition);
@@ -40,8 +46,24 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
super.position(physicalPosition);
}
// visible for testing
void switchToBlockAlignedMode() {
mode = Mode.BLOCK_ALIGNED;
}
@Override
public int write(ByteBuffer source) throws UncheckedIOException {
switch (mode) {
case PASSTHROUGH:
return super.write(source);
case BLOCK_ALIGNED:
return writeBlockAligned(source);
default:
throw new IllegalStateException("Unsupported mode " + mode);
}
}
private int writeBlockAligned(ByteBuffer source) {
int written = 0;
while (source.hasRemaining()) {
written += ByteBuffers.copy(source, currentBlockBuffer);

View File

@@ -18,6 +18,7 @@ import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class BlockAlignedReadableFileTest {
@@ -27,6 +28,26 @@ public class BlockAlignedReadableFileTest {
ReadableFile r = new BlockAlignedReadableFile(null, 0);
}
@Test
public void testSwitchingModes() {
FileSystem fs = new InMemoryFileSystem();
File file = fs.file("test");
try (WritableFile w = file.openWritable()) {
w.write(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}));
}
BlockAlignedReadableFile readable = Mockito.spy(new BlockAlignedReadableFile(file.openReadable(), 2));
ByteBuffer firstRead = ByteBuffer.allocate(4);
readable.read(firstRead);
Mockito.verify(readable, Mockito.never()).switchToBlockAlignedMode();
readable.position(0);
Mockito.verify(readable).switchToBlockAlignedMode();
ByteBuffer secondRead = ByteBuffer.allocate(4);
readable.read(secondRead);
Assert.assertArrayEquals(firstRead.array(), secondRead.array());
readable.close();
}
@Test
public void testRead() {
FileSystem fs = new InMemoryFileSystem();

View File

@@ -17,9 +17,34 @@ import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class BlockAlignedWritableFileTest {
@Test
public void testSwitchingModes() {
FileSystem fs = new InMemoryFileSystem();
File file = fs.file("test");
try (WritableFile w = file.openWritable()) {
w.write(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}));
}
BlockAlignedWritableFile writable = Mockito.spy(new BlockAlignedWritableFile(file.openWritable(), file.openReadable(), 2));
writable.write(ByteBuffer.wrap(new byte[] {0x11, 0x12, 0x13}));
Mockito.verify(writable, Mockito.never()).switchToBlockAlignedMode();
writable.position(1);
Mockito.verify(writable).switchToBlockAlignedMode();
writable.write(ByteBuffer.wrap(new byte[] {0x14, 0x15, 0x16}));
writable.close();
try (ReadableFile r = file.openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(10);
r.read(buf);
buf.flip();
Assert.assertArrayEquals(new byte[] {0x11, 0x14, 0x15, 0x16, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}, buf.array());
}
}
@Test
public void testWrite() {
FileSystem fs = new InMemoryFileSystem();