/*
* 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.ignite.internal.benchmarks.jmh.tree;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.configuration.MemoryPolicyConfiguration;
import org.apache.ignite.internal.benchmarks.jmh.JmhAbstractBenchmark;
import org.apache.ignite.internal.benchmarks.jmh.runner.JmhIdeBenchmarkRunner;
import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
import org.apache.ignite.internal.pagemem.FullPageId;
import org.apache.ignite.internal.pagemem.PageIdAllocator;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl;
import org.apache.ignite.internal.processors.cache.database.MemoryMetricsImpl;
import org.apache.ignite.internal.processors.cache.database.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.database.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.cache.database.tree.io.BPlusInnerIO;
import org.apache.ignite.internal.processors.cache.database.tree.io.BPlusLeafIO;
import org.apache.ignite.internal.processors.cache.database.tree.io.IOVersions;
import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseBag;
import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
import org.apache.ignite.logger.java.JavaLogger;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
/**
*
*/
@State(Scope.Benchmark)
public class BPlusTreeBenchmark extends JmhAbstractBenchmark {
/** */
private static final short LONG_INNER_IO = 30000;
/** */
private static final short LONG_LEAF_IO = 30001;
/** */
private static final int PAGE_SIZE = 256;
/** */
private static final long MB = 1024 * 1024;
/** */
private static final int CPUS = Runtime.getRuntime().availableProcessors();
/** */
static int MAX_PER_PAGE = 0;
/** */
private static final int CACHE_ID = 100500;
/** */
private static final int KEYS = 1_000_000;
/** */
private TestTree tree;
/** */
private PageMemory pageMem;
/**
* Fake reuse list.
*/
private static class FakeReuseList implements ReuseList {
/** */
private final ConcurrentLinkedDeque<Long> deque = new ConcurrentLinkedDeque<>();
/** {@inheritDoc} */
@Override public void addForRecycle(ReuseBag bag) throws IgniteCheckedException {
long pageId;
while ((pageId = bag.pollFreePage()) != 0L)
deque.addFirst(pageId);
}
/** {@inheritDoc} */
@Override public long takeRecycledPage() throws IgniteCheckedException {
Long pageId = deque.pollFirst();
return pageId == null ? 0L : pageId;
}
/** {@inheritDoc} */
@Override public long recycledPagesCount() throws IgniteCheckedException {
return deque.size();
}
}
/**
* @return Allocated meta page ID.
* @throws IgniteCheckedException If failed.
*/
private FullPageId allocateMetaPage() throws IgniteCheckedException {
return new FullPageId(pageMem.allocatePage(CACHE_ID, PageIdAllocator.INDEX_PARTITION, PageIdAllocator.FLAG_IDX), CACHE_ID);
}
/**
* @throws Exception If failed.
*/
@Setup
public void setup() throws Exception {
pageMem = createPageMemory();
tree = new TestTree(new FakeReuseList(), CACHE_ID, pageMem, allocateMetaPage().pageId());
for (long l = 0; l < KEYS; l++)
tree.put(l);
}
/**
* @throws Exception If failed.
*/
@TearDown
public void tearDown() throws Exception {
tree.destroy();
pageMem.stop();
}
/**
* @throws Exception If failed.
* @return Value.
*/
@Benchmark
public Long get() throws Exception {
Long key = ThreadLocalRandom.current().nextLong(KEYS);
return tree.findOne(key);
}
/**
* @throws Exception If failed.
* @return Value.
*/
@Benchmark
public Long put() throws Exception {
Long key = ThreadLocalRandom.current().nextLong(KEYS);
return tree.put(key);
}
/**
* Test tree.
*/
protected static class TestTree extends BPlusTree<Long, Long> {
/**
* @param reuseList Reuse list.
* @param cacheId Cache ID.
* @param pageMem Page memory.
* @param metaPageId Meta page ID.
* @throws IgniteCheckedException If failed.
*/
TestTree(ReuseList reuseList, int cacheId, PageMemory pageMem, long metaPageId)
throws IgniteCheckedException {
super("test", cacheId, pageMem, null, new AtomicLong(), metaPageId, reuseList,
new IOVersions<>(new LongInnerIO()), new IOVersions<>(new LongLeafIO()));
PageIO.registerTest(latestInnerIO(), latestLeafIO());
initTree(true);
}
/** {@inheritDoc} */
@Override protected int compare(BPlusIO<Long> io, long pageAddr, int idx, Long n2)
throws IgniteCheckedException {
Long n1 = io.getLookupRow(this, pageAddr, idx);
return Long.compare(n1, n2);
}
/** {@inheritDoc} */
@Override protected Long getRow(BPlusIO<Long> io, long pageAddr, int idx, Object ignore)
throws IgniteCheckedException {
assert io.canGetRow() : io;
return io.getLookupRow(this, pageAddr, idx);
}
}
/**
* @return Page memory.
* @throws Exception If failed.
*/
private PageMemory createPageMemory() throws Exception {
long[] sizes = new long[CPUS];
for (int i = 0; i < sizes.length; i++)
sizes[i] = 1024 * MB / CPUS;
MemoryPolicyConfiguration plcCfg = new MemoryPolicyConfiguration().setMaxSize(1024 * MB);
PageMemory pageMem = new PageMemoryNoStoreImpl(new JavaLogger(),
new UnsafeMemoryProvider(new JavaLogger()),
null,
PAGE_SIZE,
plcCfg,
new MemoryMetricsImpl(plcCfg),
false);
pageMem.start();
return pageMem;
}
/**
* Run benchmarks.
*
* @param args Arguments.
* @throws Exception If failed.
*/
public static void main(String[] args) throws Exception {
run(8);
}
/**
* Run benchmark.
*
* @param threads Amount of threads.
* @throws Exception If failed.
*/
private static void run(int threads) throws Exception {
JmhIdeBenchmarkRunner.create()
.forks(1)
.threads(threads)
.warmupIterations(10)
.measurementIterations(10)
.benchmarks(BPlusTreeBenchmark.class.getSimpleName())
.jvmArguments("-Xms4g", "-Xmx4g")
.run();
}
/**
* Long inner.
*/
private static final class LongInnerIO extends BPlusInnerIO<Long> {
/**
*/
LongInnerIO() {
super(LONG_INNER_IO, 1, true, 8);
}
/** {@inheritDoc} */
@Override public int getMaxCount(long buf, int pageSize) {
if (MAX_PER_PAGE != 0)
return MAX_PER_PAGE;
return super.getMaxCount(buf, pageSize);
}
/** {@inheritDoc} */
@Override public void store(long dst, int dstIdx, BPlusIO<Long> srcIo, long src, int srcIdx)
throws IgniteCheckedException {
Long row = srcIo.getLookupRow(null, src, srcIdx);
store(dst, dstIdx, row, null, false);
}
/** {@inheritDoc} */
@Override public void storeByOffset(long pageAddr, int off, Long row) {
PageUtils.putLong(pageAddr, off, row);
}
/** {@inheritDoc} */
@Override public Long getLookupRow(BPlusTree<Long,?> tree, long pageAddr, int idx) {
return PageUtils.getLong(pageAddr, offset(idx));
}
}
/**
* Long leaf.
*/
private static final class LongLeafIO extends BPlusLeafIO<Long> {
/**
*/
LongLeafIO() {
super(LONG_LEAF_IO, 1, 8);
}
/** {@inheritDoc} */
@Override public int getMaxCount(long pageAddr, int pageSize) {
if (MAX_PER_PAGE != 0)
return MAX_PER_PAGE;
return super.getMaxCount(pageAddr, pageSize);
}
/** {@inheritDoc} */
@Override public void storeByOffset(long pageAddr, int off, Long row) {
PageUtils.putLong(pageAddr, off, row);
}
/** {@inheritDoc} */
@Override public void store(long dst, int dstIdx, BPlusIO<Long> srcIo, long src, int srcIdx) {
assert srcIo == this;
PageUtils.putLong(dst, offset(dstIdx), PageUtils.getLong(src, offset(srcIdx)));
}
/** {@inheritDoc} */
@Override public Long getLookupRow(BPlusTree<Long,?> tree, long pageAddr, int idx) {
return PageUtils.getLong(pageAddr, offset(idx));
}
}
}