diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/BaseConcurrentBenchmark.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/BaseConcurrentBenchmark.java new file mode 100644 index 00000000000..11baf7aae51 --- /dev/null +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/BaseConcurrentBenchmark.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.memory.benchmark; + +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.openjdk.jmh.annotations.Level; +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 BaseConcurrentBenchmark { + + ExecutorService executorService; + + @Setup(Level.Trial) + public void setup() throws Exception { + executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + } + + @TearDown(Level.Trial) + public void tearDown() throws Exception { + executorService.shutdownNow(); + } + + void threads(int threadCount, Runnable runnable) throws InterruptedException { + + CountDownLatch latch = new CountDownLatch(1); + CountDownLatch latchDone = new CountDownLatch(threadCount); + + for (int i = 0; i < threadCount; i++) { + executorService.submit(() -> { + try { + latch.await(); + runnable.run(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + latchDone.countDown(); + } + }); + } + + latch.countDown(); + latchDone.await(); + + } + + static InputStream getResourceAsStream(String filename) { + return BaseConcurrentBenchmark.class.getClassLoader().getResourceAsStream(filename); + } + +} diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/LoadingBenchmark.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/LoadingBenchmark.java index 06af16ef504..5bee1632385 100644 --- a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/LoadingBenchmark.java +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/LoadingBenchmark.java @@ -60,8 +60,7 @@ public class LoadingBenchmark { public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("LoadingBenchmark.load") // adapt to run other benchmark tests - // .addProfiler("stack", "lines=20;period=1;top=20") + .include("LoadingBenchmark.*") // adapt to run other benchmark tests .forks(1) .build(); diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/MemValueFactoryConcurrentBenchmark.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/MemValueFactoryConcurrentBenchmark.java new file mode 100644 index 00000000000..ad69ceb34aa --- /dev/null +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/MemValueFactoryConcurrentBenchmark.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2021 Eclipse RDF4J contributors. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.memory.benchmark; + +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Literal; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.eclipse.rdf4j.sail.memory.model.MemIRI; +import org.eclipse.rdf4j.sail.memory.model.MemValue; +import org.eclipse.rdf4j.sail.memory.model.MemValueFactory; +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.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +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; + +import com.google.common.collect.Lists; + +/** + * @author HÃ¥vard M. Ottestad + */ +@State(Scope.Benchmark) +@Warmup(iterations = 5) +@BenchmarkMode({ Mode.AverageTime }) +@Fork(value = 1, jvmArgs = { "-Xms1G", "-Xmx1G", }) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class MemValueFactoryConcurrentBenchmark extends BaseConcurrentBenchmark { + + public static final int BUCKET_SIZE = 10000; + private SailRepository repository; + private List> values; + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include("MemValueFactoryConcurrentBenchmark.*") // adapt to run other benchmark tests + .forks(1) + .build(); + + new Runner(opt).run(); + } + + @Setup(Level.Trial) + public void setup() throws Exception { + super.setup(); + repository = new SailRepository(new MemoryStore()); + + try (SailRepositoryConnection connection = repository.getConnection()) { + connection.begin(IsolationLevels.NONE); + try (InputStream resourceAsStream = getResourceAsStream("benchmarkFiles/datagovbe-valid.ttl")) { + connection.add(resourceAsStream, RDFFormat.TURTLE); + } + connection.commit(); + + List collect = connection.getStatements(null, null, null) + .stream() + .flatMap(s -> Stream.of(s.getSubject(), s.getPredicate(), s.getObject())) + .distinct() + .map(v -> { + if (v.isIRI()) { + return Values.iri(v.stringValue()); + } else if (v.isBNode()) { + return Values.bnode(v.stringValue()); + } else if (v.isLiteral()) { + Literal literal = (Literal) v; + if (literal.getLanguage().isPresent()) { + return Values.literal(literal.stringValue(), literal.getLanguage().get()); + } else { + return Values.literal(literal.stringValue(), literal.getDatatype()); + } + } + throw new IllegalStateException("Could not map '" + v + "'"); + }) + .collect(Collectors.toList()); + + Collections.shuffle(collect, new Random(4583295)); + + values = Lists.partition(collect, BUCKET_SIZE); + + } + + } + + @TearDown(Level.Trial) + public void tearDown() throws Exception { + super.tearDown(); + repository.shutDown(); + } + + @Benchmark + public void onlyReads(Blackhole blackhole) throws Exception { + + MemValueFactory valueFactory = (MemValueFactory) repository.getValueFactory(); + + Random random = new Random(48593); + + threads(100, () -> { + + List values = this.values.get(random.nextInt(this.values.size())); + + for (Value value : values) { + MemValue memValue = valueFactory.getMemValue(value); + blackhole.consume(memValue); + } + + }); + + } + + @Benchmark + public void readHeavy(Blackhole blackhole) throws Exception { + + MemoryStore memoryStore = new MemoryStore(); + memoryStore.init(); + MemValueFactory valueFactory = (MemValueFactory) memoryStore.getValueFactory(); + + Random random = new Random(48593); + + threads(100, () -> { + Random r = new Random(random.nextInt()); + for (int i = 0; i < BUCKET_SIZE; i++) { + MemIRI orCreateMemURI = valueFactory + .getOrCreateMemURI(Values.iri("http://example.com", "" + r.nextInt(BUCKET_SIZE / 10))); + blackhole.consume(orCreateMemURI); + } + }); + + } + + @Benchmark + public void onlyWrites(Blackhole blackhole) throws Exception { + + MemoryStore memoryStore = new MemoryStore(); + memoryStore.init(); + MemValueFactory valueFactory = (MemValueFactory) memoryStore.getValueFactory(); + + AtomicInteger atomicInteger = new AtomicInteger(); + + threads(100, () -> { + int base = atomicInteger.incrementAndGet(); + for (int i = 0; i < BUCKET_SIZE; i++) { + IRI iri = valueFactory.createIRI("http://example.com", base + "-" + i); + blackhole.consume(iri); + } + }); + + } + +} diff --git a/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/model/WeakObjectRegistryTest.java b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/model/WeakObjectRegistryTest.java new file mode 100644 index 00000000000..213a1b5b0ba --- /dev/null +++ b/core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/model/WeakObjectRegistryTest.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse RDF4J contributors. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + ******************************************************************************/ + +package org.eclipse.rdf4j.sail.memory.model; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.util.Values; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +public class WeakObjectRegistryTest { + + @Test + @Timeout(5) + public void testGC() throws InterruptedException { + + WeakObjectRegistry objects = new WeakObjectRegistry<>(); + + IRI iri = Values.iri("http://example.com/1"); + + objects.add(iri); + + assertTrue(objects.contains(iri)); + + iri = null; + + while (objects.contains(Values.iri("http://example.com/1"))) { + System.gc(); + Thread.sleep(10); + } + + } + +}