Skip to content

Commit 2489120

Browse files
authored
GH-5353: Make write benchmarks more realistic. (#5354)
2 parents 7879f3c + 541a967 commit 2489120

10 files changed

Lines changed: 578 additions & 230 deletions

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/BenchmarkBaseFoaf.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2022 Eclipse RDF4J contributors.
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
33
*
44
* All rights reserved. This program and the accompanying materials
55
* are made available under the terms of the Eclipse Distribution License v1.0
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.sail.lmdb.benchmark;
12+
13+
import java.math.BigDecimal;
14+
import java.math.BigInteger;
15+
import java.time.LocalDate;
16+
import java.time.LocalDateTime;
17+
import java.util.Arrays;
18+
import java.util.List;
19+
import java.util.Random;
20+
import java.util.UUID;
21+
import java.util.function.Supplier;
22+
23+
import org.eclipse.rdf4j.model.Literal;
24+
import org.eclipse.rdf4j.model.ValueFactory;
25+
import org.eclipse.rdf4j.model.vocabulary.XSD;
26+
27+
/**
28+
* A utility class for generating random RDF literals using a variety of data types, including numeric types (integer,
29+
* float, double, etc.), boolean, string, and date/time types.
30+
* <p>
31+
* This class is primarily useful for testing and demonstration purposes where randomized literal values are needed.
32+
*/
33+
public class RandomLiteralGenerator {
34+
35+
/**
36+
* The {@link ValueFactory} used to create RDF literals.
37+
*/
38+
private final ValueFactory vf;
39+
40+
/**
41+
* The {@link Random} instance used to generate random values.
42+
*/
43+
private final Random random;
44+
45+
/**
46+
* A list of suppliers, each of which produces a different type of RDF literal.
47+
*/
48+
private List<Supplier<Literal>> literalSuppliers;
49+
50+
/**
51+
* Constructs a new {@code RandomLiteralGenerator} with the specified {@link ValueFactory} and {@link Random}
52+
* instances.
53+
*
54+
* @param vf the value factory used to create RDF literals
55+
* @param random the random generator used to generate random values
56+
*/
57+
public RandomLiteralGenerator(ValueFactory vf, Random random) {
58+
this.vf = vf;
59+
this.random = random;
60+
init();
61+
}
62+
63+
/**
64+
* Initializes the list of literal suppliers with a variety of data types. Includes decimals, doubles, floats,
65+
* integers, booleans, strings, unsigned values, and date/time literals.
66+
*/
67+
private void init() {
68+
literalSuppliers = Arrays.asList(
69+
// Decimal
70+
() -> vf.createLiteral(BigDecimal.valueOf(random.nextDouble() * 100000 - 50000)),
71+
// Double
72+
() -> vf.createLiteral(random.nextBoolean() ? Double.NaN : random.nextDouble() * 1000),
73+
() -> vf.createLiteral(random.nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY),
74+
() -> vf.createLiteral((double) random.nextInt(1000)),
75+
// Float
76+
() -> vf.createLiteral(random.nextBoolean() ? Float.NaN : random.nextFloat() * 100),
77+
() -> vf.createLiteral(random.nextBoolean() ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY),
78+
// Integer
79+
() -> vf.createLiteral(BigInteger.valueOf(random.nextInt(1_000_000) - 500_000)),
80+
() -> vf.createLiteral(random.nextInt(1_000_000) - 500_000),
81+
// Long
82+
() -> vf.createLiteral(random.nextLong()),
83+
// Short
84+
() -> vf.createLiteral((short) (random.nextInt(Short.MAX_VALUE - Short.MIN_VALUE) + Short.MIN_VALUE)),
85+
// Byte
86+
() -> vf.createLiteral((byte) (random.nextInt(Byte.MAX_VALUE - Byte.MIN_VALUE) + Byte.MIN_VALUE)),
87+
// Unsigned Int types
88+
() -> vf.createLiteral(String.valueOf(random.nextInt(1 << 16)), XSD.UNSIGNED_SHORT),
89+
() -> vf.createLiteral(String.valueOf(random.nextInt(1 << 8)), XSD.UNSIGNED_BYTE),
90+
() -> vf.createLiteral(String.valueOf(random.nextInt(100000)), XSD.UNSIGNED_INT),
91+
// Positive/Negative Integer
92+
() -> vf.createLiteral(String.valueOf(1 + random.nextInt(1_000_000)), XSD.POSITIVE_INTEGER),
93+
() -> vf.createLiteral("-" + (1 + random.nextInt(1_000_000)), XSD.NEGATIVE_INTEGER),
94+
// Non-negative/Non-positive
95+
() -> vf.createLiteral(String.valueOf(random.nextInt(1_000_000)), XSD.NON_NEGATIVE_INTEGER),
96+
() -> vf.createLiteral("-" + random.nextInt(1_000_000), XSD.NON_POSITIVE_INTEGER),
97+
// String
98+
() -> vf.createLiteral(UUID.randomUUID().toString().substring(0, 8), XSD.STRING),
99+
() -> vf.createLiteral("testString" + random.nextInt(100), XSD.STRING),
100+
// Boolean
101+
() -> vf.createLiteral(random.nextBoolean()),
102+
// Date and DateTime
103+
() -> vf.createLiteral(
104+
LocalDate.of(1970 + random.nextInt(100), 1 + random.nextInt(12), 1 + random.nextInt(28))),
105+
() -> vf.createLiteral(
106+
LocalDateTime.of(1970 + random.nextInt(100), 1 + random.nextInt(12), 1 + random.nextInt(28),
107+
random.nextInt(24), random.nextInt(60), random.nextInt(60)))
108+
);
109+
}
110+
111+
/**
112+
* Generates a random RDF literal.
113+
*
114+
* @return a randomly selected RDF literal
115+
*/
116+
public Literal createRandomLiteral() {
117+
return literalSuppliers.get(random.nextInt(literalSuppliers.size())).get();
118+
}
119+
}

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmark.java

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2021 Eclipse RDF4J contributors.
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
33
*
44
* All rights reserved. This program and the accompanying materials
55
* are made available under the terms of the Eclipse Distribution License v1.0
@@ -13,12 +13,15 @@
1313

1414
import java.io.File;
1515
import java.io.IOException;
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
import java.util.Random;
1619
import java.util.concurrent.TimeUnit;
1720

1821
import org.apache.commons.io.FileUtils;
1922
import org.assertj.core.util.Files;
2023
import org.eclipse.rdf4j.common.transaction.IsolationLevels;
21-
import org.eclipse.rdf4j.model.vocabulary.RDFS;
24+
import org.eclipse.rdf4j.model.IRI;
2225
import org.eclipse.rdf4j.repository.sail.SailRepository;
2326
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
2427
import org.eclipse.rdf4j.sail.lmdb.LmdbStore;
@@ -46,20 +49,23 @@
4649
@Warmup(iterations = 2)
4750
@BenchmarkMode({ Mode.Throughput })
4851
@Fork(value = 1, jvmArgs = { "-Xms2G", "-Xmx2G", "-XX:+UseG1GC" })
49-
//@Fork(value = 1, jvmArgs = {"-Xms8G", "-Xmx8G", "-XX:+UseG1GC", "-XX:+UnlockCommercialFeatures", "-XX:StartFlightRecording=delay=60s,duration=120s,filename=recording.jfr,settings=profile", "-XX:FlightRecorderOptions=samplethreads=true,stackdepth=1024", "-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints"})
50-
@Measurement(iterations = 5)
52+
@Measurement(iterations = 3)
5153
@OutputTimeUnit(TimeUnit.SECONDS)
5254
public class TransactionsPerSecondBenchmark {
5355

54-
private SailRepository repository;
55-
private File file;
56-
5756
SailRepositoryConnection connection;
57+
RandomLiteralGenerator literalGenerator;
58+
Random random;
5859
int i;
60+
List<IRI> resources;
61+
List<IRI> predicates;
62+
protected SailRepository repository;
63+
protected File file;
64+
protected boolean forceSync = false;
5965

6066
public static void main(String[] args) throws RunnerException {
6167
Options opt = new OptionsBuilder()
62-
.include("TransactionsPerSecondBenchmark") // adapt to control which benchmark tests to run
68+
.include("TransactionsPerSecondBenchmark\\.") // adapt to control which benchmarks to run
6369
.forks(1)
6470
.build();
6571

@@ -75,12 +81,29 @@ public void beforeClass() {
7581
i = 0;
7682
file = Files.newTemporaryFolder();
7783

78-
LmdbStore sail = new LmdbStore(file, ConfigUtil.createConfig());
84+
LmdbStore sail = new LmdbStore(file, ConfigUtil.createConfig().setForceSync(forceSync));
7985
repository = new SailRepository(sail);
8086
connection = repository.getConnection();
87+
random = new Random(1337);
88+
literalGenerator = new RandomLiteralGenerator(connection.getValueFactory(), random);
89+
resources = new ArrayList<>();
90+
for (int i = 0; i < 10; i++) {
91+
resources.add(connection.getValueFactory().createIRI("some:resource-" + i));
92+
}
93+
predicates = new ArrayList<>();
94+
for (int i = 0; i < 10; i++) {
95+
predicates.add(connection.getValueFactory().createIRI("some:predicate-" + i));
96+
}
8197

8298
System.gc();
99+
}
100+
101+
IRI randomResource() {
102+
return resources.get(random.nextInt(resources.size()));
103+
}
83104

105+
IRI randomPredicate() {
106+
return predicates.get(random.nextInt(predicates.size()));
84107
}
85108

86109
@TearDown(Level.Iteration)
@@ -97,22 +120,22 @@ public void afterClass() throws IOException {
97120
@Benchmark
98121
public void transactions() {
99122
connection.begin();
100-
connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++));
123+
connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral());
101124
connection.commit();
102125
}
103126

104127
@Benchmark
105128
public void transactionsLevelNone() {
106129
connection.begin(IsolationLevels.NONE);
107-
connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++));
130+
connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral());
108131
connection.commit();
109132
}
110133

111134
@Benchmark
112135
public void mediumTransactionsLevelNone() {
113136
connection.begin(IsolationLevels.NONE);
114137
for (int k = 0; k < 10; k++) {
115-
connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k));
138+
connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral());
116139
}
117140
connection.commit();
118141
}
@@ -121,7 +144,7 @@ public void mediumTransactionsLevelNone() {
121144
public void largerTransaction() {
122145
connection.begin();
123146
for (int k = 0; k < 10000; k++) {
124-
connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k));
147+
connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral());
125148
}
126149
connection.commit();
127150
}
@@ -130,7 +153,7 @@ public void largerTransaction() {
130153
public void largerTransactionLevelNone() {
131154
connection.begin(IsolationLevels.NONE);
132155
for (int k = 0; k < 10000; k++) {
133-
connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k));
156+
connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral());
134157
}
135158
connection.commit();
136159
}
@@ -139,7 +162,7 @@ public void largerTransactionLevelNone() {
139162
public void veryLargerTransactionLevelNone() {
140163
connection.begin(IsolationLevels.NONE);
141164
for (int k = 0; k < 1000000; k++) {
142-
connection.add(RDFS.RESOURCE, RDFS.LABEL, connection.getValueFactory().createLiteral(i++ + "_" + k));
165+
connection.add(randomResource(), randomPredicate(), literalGenerator.createRandomLiteral());
143166
}
144167
connection.commit();
145168
}

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/TransactionsPerSecondBenchmarkFoaf.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2022 Eclipse RDF4J contributors.
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
33
*
44
* All rights reserved. This program and the accompanying materials
55
* are made available under the terms of the Eclipse Distribution License v1.0
@@ -33,19 +33,19 @@
3333
import org.openjdk.jmh.runner.options.OptionsBuilder;
3434

3535
/**
36-
* Benchmarks insertion performance with extended FOAF data.
36+
* Benchmarks insertion performance with synthetic FOAF data.
3737
*/
3838
@State(Scope.Benchmark)
3939
@Warmup(iterations = 2)
4040
@BenchmarkMode({ Mode.Throughput })
4141
@Fork(value = 1, jvmArgs = { "-Xms2G", "-Xmx2G", "-XX:+UseG1GC" })
42-
@Measurement(iterations = 5)
42+
@Measurement(iterations = 3)
4343
@OutputTimeUnit(TimeUnit.SECONDS)
4444
public class TransactionsPerSecondBenchmarkFoaf extends BenchmarkBaseFoaf {
4545

4646
public static void main(String[] args) throws RunnerException {
4747
Options opt = new OptionsBuilder()
48-
.include("TransactionsPerSecondBenchmarkFoaf") // adapt to control which benchmark tests to run
48+
.include("TransactionsPerSecondBenchmarkFoaf\\.") // adapt to control which benchmark tests to run
4949
.forks(1)
5050
.build();
5151

0 commit comments

Comments
 (0)