blob: 2b37b00a485a0d5f47d8326d878ca4c4f724d391 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.datasketches.quantiles;
import static org.apache.datasketches.common.MemorySegmentStatus.isSameResource;
import static org.apache.datasketches.quantiles.PreambleUtil.COMBINED_BUFFER;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import org.testng.annotations.Test;
/**
* The concept for these tests is that the "MemorySegment Manager" classes below are proxies for the
* implementation that <i>owns</i> the native MemorySegment allocations, thus is responsible for
* allocating larger MemorySegment when requested and the actual freeing of the old MemorySegment allocations.
*/
public class DirectQuantilesMemoryRequestTest {
@Test
public void checkLimitedMemoryScenarios() { //Requesting application
final int k = 128;
final int u = 40 * k;
final int initBytes = ((2 * k) + 4) << 3; //just the BaseBuffer
//########## Owning Implementation
// This part would actually be part of the application owning the MemorySegment so it is faked here
MemorySegment wseg;
try (Arena arena = Arena.ofConfined()) {
wseg = arena.allocate(initBytes);
println("Initial seg size: " + wseg.byteSize());
//########## Receiving Application
// The receiving application has been given wseg to use for a sketch,
// but alas, it is not ultimately large enough.
final UpdatableQuantilesDoublesSketch usk = QuantilesDoublesSketch.builder().setK(k).build(wseg);
assertTrue(usk.isEmpty());
//Load the sketch
for (int i = 0; i < u; i++) {
// The sketch uses The MemorySegmentRequestto acquire more MemorySegment as needed,
// which, for the default, the new MemorySegments will be on-heap.
usk.update(i);
}
final double result = usk.getQuantile(0.5);
println("Result: " + result);
assertEquals(result, u / 2.0, 0.05 * u); //Success
//########## Owning Implementation
//The actual MemorySegment has been re-allocated several times,
// so the sketch is using a different object.
final MemorySegment wseg2 = usk.getMemorySegment();
println("\nFinal seg size: " + wseg2.byteSize());
}
assertFalse(wseg.scope().isAlive());
}
@Test
public void checkGrowBaseBuf() {
final int k = 128;
final int u = 32; // don't need the BB to fill here
final int initBytes = (4 + (u / 2)) << 3; // not enough to hold everything
MemorySegment wseg;
try (Arena arena = Arena.ofConfined()) {
wseg = arena.allocate(initBytes);
println("Initial seg size: " + wseg.byteSize());
final UpdatableQuantilesDoublesSketch usk1 = QuantilesDoublesSketch.builder().setK(k).build(wseg);
for (int i = 1; i <= u; i++) {
usk1.update(i);
}
final int currentSpace = usk1.getCombinedBufferItemCapacity();
println("curCombBufItemCap: " + currentSpace);
assertEquals(currentSpace, 2 * k);
assertTrue(wseg.scope().isAlive());
}
assertFalse(wseg.scope().isAlive());
}
@Test
public void checkGrowCombBuf() {
final int k = 128;
final int u = (2 * k) - 1; //just to fill the BB
final int initBytes = ((2 * k) + 4) << 3; //just room for BB
MemorySegment wseg;
try (Arena arena = Arena.ofConfined()) {
wseg = arena.allocate(initBytes);
println("Initial seg size: " + wseg.byteSize());
final UpdatableQuantilesDoublesSketch usk1 = QuantilesDoublesSketch.builder().setK(k).build(wseg);
for (int i = 1; i <= u; i++) {
usk1.update(i);
}
final int currentSpace = usk1.getCombinedBufferItemCapacity();
println("curCombBufItemCap: " + currentSpace);
final double[] newCB = usk1.growCombinedBuffer(currentSpace, 3 * k);
final int newSpace = usk1.getCombinedBufferItemCapacity();
println("newCombBurItemCap: " + newSpace);
assertEquals(newCB.length, 3 * k);
assertTrue(wseg.scope().isAlive());
}
assertFalse(wseg.scope().isAlive());
}
@Test
public void checkUpdatableStorageBytes() {
final int k = 16;
final int initBytes = QuantilesDoublesSketch.getUpdatableStorageBytes(k, 1);
println("Predicted Updatable Storage Bytes: " + initBytes);
final UpdatableQuantilesDoublesSketch usk1 = QuantilesDoublesSketch.builder().setK(k).build();
usk1.update(1.0);
final byte[] uarr = usk1.toByteArray();
println("Actual Storage Bytes " + uarr.length);
assertEquals(initBytes, uarr.length);
assertEquals(initBytes, 64);
}
@Test
public void checkGrowFromWrappedEmptySketch() {
final int k = 16;
final int n = 0;
final int initBytes = QuantilesDoublesSketch.getUpdatableStorageBytes(k, n); //empty: 8 bytes
final UpdatableQuantilesDoublesSketch usk1 = QuantilesDoublesSketch.builder().setK(k).build();
final MemorySegment origSketchSeg = MemorySegment.ofArray(usk1.toByteArray()); //on heap
MemorySegment wseg;
try (Arena arena = Arena.ofConfined()) {
wseg = arena.allocate(initBytes); //off heap
MemorySegment.copy(origSketchSeg, 0, wseg, 0, initBytes);
final UpdatableQuantilesDoublesSketch usk2 = DirectUpdateDoublesSketch.wrapInstance(wseg, null);
assertTrue(isSameResource(wseg, usk2.getMemorySegment()));
assertEquals(wseg.byteSize(), initBytes);
assertTrue(wseg.isNative());
assertTrue(usk2.isEmpty());
//update the sketch forcing it to grow on-heap
usk2.update(1.0);
assertEquals(usk2.getN(), 1);
final MemorySegment seg2 = usk2.getMemorySegment();
assertFalse(isSameResource(wseg, seg2));
assertFalse(seg2.isNative()); //should now be on-heap
final int expectedSize = COMBINED_BUFFER + ((2 * k) << 3);
assertEquals(seg2.byteSize(), expectedSize);
assertTrue(wseg.scope().isAlive());
}
assertFalse(wseg.scope().isAlive());
}
@Test
public void printlnTest() {
println("PRINTING: " + this.getClass().getName());
}
/**
* @param s value to print
*/
static void println(final String s) {
//System.out.println(s); //disable here
}
}