/*
* Copyright 2015 Terracotta, Inc., a Software AG company.
*
* Licensed 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.terracotta.offheapstore.storage.allocator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.junit.Test;
import org.terracotta.offheapstore.OffHeapHashMap;
import org.terracotta.offheapstore.buffersource.HeapBufferSource;
import org.terracotta.offheapstore.buffersource.OffHeapBufferSource;
import org.terracotta.offheapstore.paging.OffHeapStorageArea;
import org.terracotta.offheapstore.paging.UnlimitedPageSource;
import org.terracotta.offheapstore.storage.SerializableStorageEngine;
import org.terracotta.offheapstore.util.PointerSizeParameterizedTest;
import org.hamcrest.core.Is;
import org.junit.Assert;
public class BestFitAllocatorIT extends PointerSizeParameterizedTest {
@Test
public void testUniformSizedAllocations() {
OffHeapStorageArea test = new OffHeapStorageArea(getPointerSize(), null, new UnlimitedPageSource(new HeapBufferSource()), 2048, false, false);
int width = getPointerSize().byteSize();
for (int i = 0; i < 100; i++) {
Assert.assertEquals(2 * width + (i * 4 * width), test.allocate(1));
}
Assert.assertEquals(100 * 4 * width, test.getOccupiedMemory());
}
@Test
public void testUniformSizedFrees() {
OffHeapStorageArea test = new OffHeapStorageArea(getPointerSize(), null, new UnlimitedPageSource(new HeapBufferSource()), 2048, false, false);
List<Long> allocated = new ArrayList<Long>();
for (int i = 0; i < 100; i++) {
allocated.add(test.allocate(1));
}
Random rndm = new Random();
for (int i = 0; i < 100; i++) {
test.free(allocated.remove(rndm.nextInt(allocated.size())));
}
Assert.assertEquals(0, test.getOccupiedMemory());
}
@Test
public void testUniformRepeatedAllocFree() {
OffHeapStorageArea test = new OffHeapStorageArea(getPointerSize(), null, new UnlimitedPageSource(new HeapBufferSource()), 2048, false, false);
for (int i = 1; i < 100; i++) {
int count = (int) Math.floor(100d / i);
for (int j = 1; j <= count; j++) {
List<Long> pointers = new ArrayList<Long>();
for (int k = 0; k < j; k++) {
long p = test.allocate(i);
pointers.add(p);
}
for (Long p : pointers) {
test.free(p);
}
Assert.assertEquals("Testing " + j + " Regions of size " + i, 0, test.getOccupiedMemory());
}
}
}
@Test
public void testRandomAllocFree() {
for (int n = 0; n < 1000; n++) {
OffHeapStorageArea test = new OffHeapStorageArea(getPointerSize(), null, new UnlimitedPageSource(new HeapBufferSource()), 100 * 1024, false, false);
List<Long> allocated = new ArrayList<Long>();
Random rndm = new Random();
for (int i = 0; i < 100; i++) {
if (rndm.nextBoolean()) {
int size = rndm.nextInt(1024);
long p = test.allocate(size);
test.validateStorageArea();
if (p >= 0) {
allocated.add(p);
}
} else {
if (!allocated.isEmpty()) {
long p = allocated.remove(rndm.nextInt(allocated.size()));
test.free(p);
test.validateStorageArea();
}
}
}
}
}
@Test
public void testRandomAllocReallocFree() {
for (int n = 0; n < 1000; n++) {
OffHeapStorageArea test = new OffHeapStorageArea(getPointerSize(), null, new UnlimitedPageSource(new HeapBufferSource()), 100 * 1024, false, false);
List<Long> allocated = new ArrayList<Long>();
long seed = System.nanoTime();
Random rndm = new Random(seed);
for (int i = 0; i < 100; i++) {
switch (rndm.nextInt(2)) {
case 0: {
int size = rndm.nextInt(1024);
long p = test.allocate(size);
test.validateStorageArea();
if (p >= 0) {
allocated.add(p);
}
} break;
case 1: {
if (!allocated.isEmpty()) {
long p = allocated.remove(rndm.nextInt(allocated.size()));
test.free(p);
test.validateStorageArea();
}
} break;
}
}
for (Long p : allocated) {
test.free(p);
}
Assert.assertThat(test.getOccupiedMemory(), Is.is(0L));
}
}
@Test
public void repeatedPutTest() {
Map<String, byte[]> map = new OffHeapHashMap<String, byte[]>(new UnlimitedPageSource(new OffHeapBufferSource()), new SerializableStorageEngine(getPointerSize(), new UnlimitedPageSource(new OffHeapBufferSource()), 1024));
Random rndm = new Random();
for (int i = 0; i < 100; i++) {
byte[] value = new byte[rndm.nextInt(1024)];
rndm.nextBytes(value);
putAll(map, value);
}
}
private static void putAll(Map<String, byte[]> map, byte[] value) {
for (int j = 0; j < 100; j++) {
map.put(Integer.toString(j), value);
}
}
/*
* We were forgetting to clear the 'present' tree and small bin maps when
* clearing. This mean we were trying to use a '-1' invalid chunk pointer
* because the small bin root value was invalid even though the map said the
* bin was non-empty.
*/
@Test
public void testSmallMapClearing() {
OffHeapStorageArea storage = new OffHeapStorageArea(getPointerSize(), null, new UnlimitedPageSource(new OffHeapBufferSource()), 16 * 1024, false, false);
/*
* Allocate a small chunk
*/
long p = storage.allocate(13);
/*
* Make sure it doesn't border the top chunk.
*/
storage.allocate(13);
/*
* Free the small chunk - this ensures that the small bin for this size is
* non-empty.
*/
storage.free(p);
/*
* Clear the data area and allocator.
*/
storage.clear();
/*
* Allocate - this will attempt to allocate from an empty small bin if we
* didn't correctly clear the small map.
*/
storage.allocate(13);
}
}