From 5df28fa2cafd9607edd746c78219e65649d55667 Mon Sep 17 00:00:00 2001 From: conghuhu <56248584+conghuhu@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:50:35 +0800 Subject: [PATCH] feat(perf): support JMH benchmark in HG-test module (#2238) * chore: add param to max cap * chore: add non argument constructor to IntMapByEcSegment --------- Co-authored-by: shiyi --- .../hugegraph/util/collection/IntMap.java | 6 +- hugegraph-dist/release-docs/LICENSE | 1 + .../licenses/LINCENSE-jopt-simple.txt | 24 +++ hugegraph-test/pom.xml | 35 +++++ .../benchmark/BenchmarkConstants.java | 23 +++ .../hugegraph/benchmark/SimpleRandom.java | 42 ++++++ .../map/MapRandomGetPutThroughputTest.java | 137 ++++++++++++++++++ pom.xml | 14 +- 8 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 hugegraph-dist/release-docs/licenses/LINCENSE-jopt-simple.txt create mode 100644 hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/BenchmarkConstants.java create mode 100644 hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/SimpleRandom.java create mode 100644 hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/map/MapRandomGetPutThroughputTest.java diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/util/collection/IntMap.java b/hugegraph-core/src/main/java/org/apache/hugegraph/util/collection/IntMap.java index 4465827464..02b7052255 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/util/collection/IntMap.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/util/collection/IntMap.java @@ -68,7 +68,7 @@ final class IntMapBySegments implements IntMap { private final int segmentMask; private final Function creator; - private static final int DEFAULT_SEGMENTS = IntSet.CPUS * 100; + private static final int DEFAULT_SEGMENTS = (IntSet.CPUS + 8) * 32; private static final Function DEFAULT_CREATOR = size -> new IntMapByFixedAddr(size); @@ -512,6 +512,10 @@ final class IntMapByEcSegment implements IntMap { private final MutableIntIntMap[] maps; private final int segmentMask; + public IntMapByEcSegment() { + this(IntMapBySegments.DEFAULT_SEGMENTS); + } + public IntMapByEcSegment(int segments) { segments = IntSet.sizeToPowerOf2Size(segments); this.segmentMask = segments - 1; diff --git a/hugegraph-dist/release-docs/LICENSE b/hugegraph-dist/release-docs/LICENSE index 5bfd9e606e..f1cc9686c8 100644 --- a/hugegraph-dist/release-docs/LICENSE +++ b/hugegraph-dist/release-docs/LICENSE @@ -539,6 +539,7 @@ See licenses/ for text of these licenses. (The MIT License) * mockito-core (org.mockito:mockito-core:3.3.3 - https://github.com/mockito/mockito) (The MIT License) * sourcecode (com.lihaoyi:sourcecode_2.12:0.1.4 - https://github.com/lihaoyi/sourcecode) (The MIT License) * Checker Qual (org.checkerframework:checker-qual:2.0.0 - http://checkerframework.org) + (The MIT License) * jopt-simple (net.sf.jopt-simple:jopt-simple:5.0.4 - https://github.com/jopt-simple/jopt-simple) ======================================================================== Third party Public Domain licenses diff --git a/hugegraph-dist/release-docs/licenses/LINCENSE-jopt-simple.txt b/hugegraph-dist/release-docs/licenses/LINCENSE-jopt-simple.txt new file mode 100644 index 0000000000..6984620c39 --- /dev/null +++ b/hugegraph-dist/release-docs/licenses/LINCENSE-jopt-simple.txt @@ -0,0 +1,24 @@ +/* + The MIT License + + Copyright (c) 2004-2021 Paul R. Holser, Jr. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/hugegraph-test/pom.xml b/hugegraph-test/pom.xml index 0bda198c4a..75e8caf664 100644 --- a/hugegraph-test/pom.xml +++ b/hugegraph-test/pom.xml @@ -120,6 +120,20 @@ + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + test + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + test + @@ -256,6 +270,27 @@ + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + microbenchmarks + + + org.openjdk.jmh.Main + + + + + + diff --git a/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/BenchmarkConstants.java b/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/BenchmarkConstants.java new file mode 100644 index 0000000000..1525e8143e --- /dev/null +++ b/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/BenchmarkConstants.java @@ -0,0 +1,23 @@ +/* + * 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.hugegraph.benchmark; + +public class BenchmarkConstants { + + public static String OUTPUT_PATH = "./hugegraph-test/target/"; +} diff --git a/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/SimpleRandom.java b/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/SimpleRandom.java new file mode 100644 index 0000000000..3338f10afb --- /dev/null +++ b/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/SimpleRandom.java @@ -0,0 +1,42 @@ +/* + * 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.hugegraph.benchmark; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Fairly fast random numbers + */ +public final class SimpleRandom { + + private static final long MULTIPLIER = 0x5DEECE66DL; + private static final long ADD_END = 0xBL; + private static final long MASK = (1L << 48) - 1; + private static final AtomicLong SEG = new AtomicLong(-715159705); + private long seed; + + public SimpleRandom() { + this.seed = System.nanoTime() + SEG.getAndAdd(129); + } + + public int next() { + long nextSeed = (this.seed * MULTIPLIER + ADD_END) & MASK; + this.seed = nextSeed; + return ((int) (nextSeed >>> 17)) & 0x7FFFFFFF; + } +} diff --git a/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/map/MapRandomGetPutThroughputTest.java b/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/map/MapRandomGetPutThroughputTest.java new file mode 100644 index 0000000000..eafe4b861f --- /dev/null +++ b/hugegraph-test/src/test/java/org/apache/hugegraph/benchmark/map/MapRandomGetPutThroughputTest.java @@ -0,0 +1,137 @@ +/* + * 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.hugegraph.benchmark.map; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import org.apache.hugegraph.benchmark.BenchmarkConstants; +import org.apache.hugegraph.benchmark.SimpleRandom; +import org.apache.hugegraph.util.collection.IntMap; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@BenchmarkMode({Mode.Throughput}) +@Warmup(iterations = 2, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 6, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(2) +public class MapRandomGetPutThroughputTest { + + @Param(value = {"1000", "10000", "100000", "1000000"}) + private int MAP_CAPACITY; + + private ConcurrentHashMap concurrentHashMapNonCap; + + private ConcurrentHashMap concurrentHashMap; + + private IntMap.IntMapBySegments intMapBySegments; + + private IntMap.IntMapByEcSegment intMapByEcSegments; + + private static final int THREAD_COUNT = 8; + + private static final String OUTPUT_FILE_NAME = "map_random_get_put_result.json"; + + @Setup(Level.Trial) + public void prepareMap() { + this.concurrentHashMapNonCap = new ConcurrentHashMap<>(); + this.concurrentHashMap = new ConcurrentHashMap<>(MAP_CAPACITY); + this.intMapBySegments = new IntMap.IntMapBySegments(MAP_CAPACITY); + this.intMapByEcSegments = new IntMap.IntMapByEcSegment(); + } + + /** + * The instantiated @State annotation only supports public classes. + */ + @State(Scope.Thread) + public static class ThreadState { + + private final SimpleRandom random = new SimpleRandom(); + + int next() { + return random.next(); + } + } + + @Benchmark + @Threads(THREAD_COUNT) + public void randomGetPutOfConcurrentHashMapWithNoneInitCap(ThreadState state) { + int key = state.next() & (MAP_CAPACITY - 1); + if (!this.concurrentHashMapNonCap.containsKey(key)) { + this.concurrentHashMapNonCap.put(key, state.next()); + } + this.concurrentHashMapNonCap.get(key); + } + + @Benchmark + @Threads(THREAD_COUNT) + public void randomGetPutOfConcurrentHashMapWithInitCap(ThreadState state) { + int key = state.next() & (MAP_CAPACITY - 1); + if (!this.concurrentHashMap.containsKey(key)) { + this.concurrentHashMap.put(key, state.next()); + } + this.concurrentHashMap.get(key); + } + + @Benchmark + @Threads(THREAD_COUNT) + public void randomGetPutOfIntMapBySegments(ThreadState state) { + int key = state.next() & (MAP_CAPACITY - 1); + if (!this.intMapBySegments.containsKey(key)) { + this.intMapBySegments.put(key, state.next()); + } + this.intMapBySegments.get(key); + } + + @Benchmark + @Threads(THREAD_COUNT) + public void randomGetPutOfIntMapByEcSegment(ThreadState state) { + int key = state.next() & (MAP_CAPACITY - 1); + if (!this.intMapByEcSegments.containsKey(key)) { + this.intMapByEcSegments.put(key, state.next()); + } + this.intMapByEcSegments.get(key); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(MapRandomGetPutThroughputTest.class.getSimpleName()) + .result(BenchmarkConstants.OUTPUT_PATH + OUTPUT_FILE_NAME) + .resultFormat(ResultFormatType.JSON) + .build(); + new Runner(opt).run(); + } +} diff --git a/pom.xml b/pom.xml index 8679c83ddd..719328316c 100644 --- a/pom.xml +++ b/pom.xml @@ -114,6 +114,7 @@ 1.0.0 1.47.0 3.21.7 + 1.36 @@ -322,7 +323,18 @@ commons-text 1.10.0 - + + org.openjdk.jmh + jmh-core + ${jmh.version} + test + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + test +