Skip to content

Commit

Permalink
timeseries, implement BigDoubleBuffer with testability without bytebu…
Browse files Browse the repository at this point in the history
…ffer

Signed-off-by: HARPER Jon <jon.harper87@gmail.com>
  • Loading branch information
jonenst committed Mar 3, 2023
1 parent 5b0cf6f commit 4414c9b
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.nio.DoubleBuffer;
import java.util.Objects;
import java.util.function.IntFunction;
import java.util.function.IntConsumer;

/**
* @author Jon Harper <jon.harper at rte-france.com>
Expand All @@ -23,8 +24,13 @@ public class BigDoubleBuffer {
private DoubleBuffer[] buffers;
private long size;

public BigDoubleBuffer(IntFunction<ByteBuffer> byteBufferAllocator, long size) {
Objects.requireNonNull(byteBufferAllocator);
//To remove if we ever get it from somewhere else
//package private for tests
@FunctionalInterface interface IntIntBiConsumer { public void accept(int a, int b); }

//using a lambda to test independently from java.nio.ByteBuffer
//package private for tests
static void withSizes(long size, IntConsumer bufferContainerInitializer, IntIntBiConsumer bufferInitializer) {
if (size < 0) {
throw new IllegalArgumentException("Invalid buffer size: " + size);
}
Expand All @@ -38,30 +44,49 @@ public BigDoubleBuffer(IntFunction<ByteBuffer> byteBufferAllocator, long size) {
if (size > 0 && lastBufferSizeBytes == 0) {
lastBufferSizeBytes = BUFFER_SIZE_BYTES;
}
buffers = new DoubleBuffer[bufferCount];
bufferContainerInitializer.accept(bufferCount);
for (int i = 0; i < bufferCount - 1; i++) {
buffers[i] = byteBufferAllocator.apply(BUFFER_SIZE_BYTES).asDoubleBuffer();
bufferInitializer.accept(i, BUFFER_SIZE_BYTES);
}
if (lastBufferSizeBytes > 0) {
buffers[bufferCount - 1] = byteBufferAllocator.apply(lastBufferSizeBytes).asDoubleBuffer();
bufferInitializer.accept(bufferCount - 1, lastBufferSizeBytes);
}
}

public BigDoubleBuffer(IntFunction<ByteBuffer> byteBufferAllocator, long size) {
Objects.requireNonNull(byteBufferAllocator);
withSizes(size,
bufferCount -> buffers = new DoubleBuffer[bufferCount],
(i, bufferSize) -> buffers[i] = byteBufferAllocator.apply(bufferSize).asDoubleBuffer()
);
this.size = size;
}

public void put(long index, double value) {
//To remove if we ever get it from somewhere else
//package private for tests
@FunctionalInterface interface IntIntToDoubleBiFunction { public double applyAsDouble(int a, int b); }

//using a lambda to test independently from java.nio.ByteBuffer
//package private for tests
static double withIndices(long index, IntIntToDoubleBiFunction indicesConsumer) {
long computedBufferIndex = index >> BUFFER_SHIFT;
long computedSecondIndex = index & BUFFER_MASK;
int bufferIndex = (int) computedBufferIndex;
int secondIndex = (int) computedSecondIndex;
buffers[bufferIndex].put(secondIndex, value);
return indicesConsumer.applyAsDouble(bufferIndex, secondIndex);
}

public void put(long index, double value) {
withIndices(index, (bufferIndex, secondIndex) -> {
buffers[bufferIndex].put(secondIndex, value);
return Double.NaN; // just to reuse the withIndices code
});
}

public double get(long index) {
long computedBufferIndex = index >> BUFFER_SHIFT;
long computedSecondIndex = index & BUFFER_MASK;
int bufferIndex = (int) computedBufferIndex;
int secondIndex = (int) computedSecondIndex;
return buffers[bufferIndex].get(secondIndex);
return withIndices(index, (bufferIndex, secondIndex) ->
buffers[bufferIndex].get(secondIndex)
);
}

public long capacity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,79 +7,112 @@
package com.powsybl.timeseries;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.nio.ByteBuffer;
import java.util.function.IntConsumer;
import com.powsybl.timeseries.BigDoubleBuffer.IntIntBiConsumer;
import com.powsybl.timeseries.BigDoubleBuffer.IntIntToDoubleBiFunction;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

/**
* @author Jon Harper <jon.harper at rte-france.com>
*/
class BigDoubleBufferTest extends AbstractBigBufferTest {
class BigDoubleBufferTest {

private static final int BUFFER_SIZE_DOUBLES = 1 << 27;

private void bufferTester(long size) {
protected int allocatorCount;

protected ByteBuffer testAllocator(int capacity) {
try {
ByteBuffer bytebuffer = ByteBuffer.allocate(capacity);
allocatorCount++;
return bytebuffer;
} catch (Exception e) {
throw new RuntimeException("error in allocator test", e);
}
}

@Test
void testSimple() {
long size = 10L;
BigDoubleBuffer buffer = new BigDoubleBuffer(this::testAllocator, size);
assertEquals(1, allocatorCount);
//Simple writes at the begining
for (int i = 0; i < 10; i++) {
buffer.put(i, i);
}
//writes around first buffer change
if (size > BUFFER_SIZE_DOUBLES + 10) {
for (int i = BUFFER_SIZE_DOUBLES - 10; i < BUFFER_SIZE_DOUBLES + 10; i++) {
buffer.put(i, i);
}
}
//writes at the end
for (long i = size - 10; i < size; i++) {
buffer.put(i, i);
}

for (int i = 0; i < 10; i++) {
assertEquals(i, buffer.get(i), 0);
}
if (size > BUFFER_SIZE_DOUBLES + 10) {
for (int i = BUFFER_SIZE_DOUBLES - 10; i < BUFFER_SIZE_DOUBLES + 10; i++) {
assertEquals(i, buffer.get(i), 0);
}
}
for (long i = size - 10; i < size; i++) {
assertEquals(i, buffer.get(i), 0);
}
}

@Test
void testSimple() {
bufferTester(10);
assertEquals(1, channels.size());
void testWithIndices() {
IntIntToDoubleBiFunction indicesConsumer = Mockito.mock(IntIntToDoubleBiFunction.class);
BigDoubleBuffer.withIndices(10, indicesConsumer);
verify(indicesConsumer).applyAsDouble(0, 10);
verifyNoMoreInteractions(indicesConsumer);

//writes around first buffer change
BigDoubleBuffer.withIndices(BUFFER_SIZE_DOUBLES - 1, indicesConsumer);
verify(indicesConsumer).applyAsDouble(0, BUFFER_SIZE_DOUBLES - 1);
verifyNoMoreInteractions(indicesConsumer);
BigDoubleBuffer.withIndices(BUFFER_SIZE_DOUBLES, indicesConsumer);
verify(indicesConsumer).applyAsDouble(1, 0);
verifyNoMoreInteractions(indicesConsumer);

//writes around random buffer change
BigDoubleBuffer.withIndices(7 * BUFFER_SIZE_DOUBLES - 1, indicesConsumer);
verify(indicesConsumer).applyAsDouble(6, BUFFER_SIZE_DOUBLES - 1);
verifyNoMoreInteractions(indicesConsumer);
BigDoubleBuffer.withIndices(7 * BUFFER_SIZE_DOUBLES, indicesConsumer);
verify(indicesConsumer).applyAsDouble(7, 0);
verifyNoMoreInteractions(indicesConsumer);
}

void indicesTester(long size, int bufferCount) {
IntConsumer bufferContainerInitializer = Mockito.mock(IntConsumer.class);
IntIntBiConsumer bufferInitializer = Mockito.mock(IntIntBiConsumer.class);
BigDoubleBuffer.withSizes(size,
bufferContainerInitializer,
bufferInitializer
);
verify(bufferContainerInitializer).accept(bufferCount);
verifyNoMoreInteractions(bufferContainerInitializer);
for (int i = 0; i < bufferCount - 1; i++) {
verify(bufferInitializer).accept(i, BUFFER_SIZE_DOUBLES * Double.BYTES);
}
verify(bufferInitializer).accept(bufferCount - 1, (int) (size - (bufferCount - 1) * BUFFER_SIZE_DOUBLES) * Double.BYTES);
verifyNoMoreInteractions(bufferInitializer);
}

@Test
void testMultipleBuffers() {
bufferTester(200000000);
assertEquals(2, channels.size());
indicesTester(200000000, 2);
}

@Test
void testHuge() {
bufferTester(10000000000L);
assertEquals(75, channels.size());
indicesTester(10000000000L, 75);
}

@Test
void testSizeBufferMinus1() {
bufferTester(BUFFER_SIZE_DOUBLES - 1);
assertEquals(1, channels.size());
indicesTester(BUFFER_SIZE_DOUBLES - 1, 1);
}

@Test
void testSizeBufferExact() {
bufferTester(BUFFER_SIZE_DOUBLES);
assertEquals(1, channels.size());
indicesTester(BUFFER_SIZE_DOUBLES, 1);
}

@Test
void testSizeBufferPlus1() {
bufferTester(BUFFER_SIZE_DOUBLES + 1);
assertEquals(2, channels.size());
indicesTester(BUFFER_SIZE_DOUBLES + 1, 2);
}
}

0 comments on commit 4414c9b

Please sign in to comment.