diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceSelfComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceSelfComparisonTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9e6f8c9ea754079be188132f97313a91740a8dba --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceSelfComparisonTest.java @@ -0,0 +1,23 @@ +package org.briarproject.bramble.db; + +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.api.system.Clock; +import org.junit.Ignore; + +import java.sql.Connection; + +@Ignore +public class H2DatabasePerformanceSelfComparisonTest + extends JdbcDatabasePerformanceComparisonTest { + + @Override + Database<Connection> createDatabase(boolean conditionA, + DatabaseConfig databaseConfig, Clock clock) { + return new H2Database(databaseConfig, clock); + } + + @Override + protected String getTestName() { + return H2DatabasePerformanceSelfComparisonTest.class.getSimpleName(); + } +} diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java index 86d9b11d50675065b079991983e34fecbfa5194e..467bc92317fd6d54a603e6fdec009dc7acc0f718 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2DatabasePerformanceTest.java @@ -5,7 +5,8 @@ import org.briarproject.bramble.api.system.Clock; import org.junit.Ignore; @Ignore -public class H2DatabasePerformanceTest extends JdbcDatabasePerformanceTest { +public class H2DatabasePerformanceTest + extends JdbcSingleDatabasePerformanceTest { @Override protected String getTestName() { diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5e112bb240027e28fdcafc46f9df5909cc1560bf --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2HyperSqlDatabasePerformanceComparisonTest.java @@ -0,0 +1,25 @@ +package org.briarproject.bramble.db; + +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.api.system.Clock; +import org.junit.Ignore; + +import java.sql.Connection; + +@Ignore +public class H2HyperSqlDatabasePerformanceComparisonTest + extends JdbcDatabasePerformanceComparisonTest { + + @Override + Database<Connection> createDatabase(boolean conditionA, + DatabaseConfig databaseConfig, Clock clock) { + if (conditionA) return new H2Database(databaseConfig, clock); + else return new HyperSqlDatabase(databaseConfig, clock); + } + + @Override + protected String getTestName() { + return H2HyperSqlDatabasePerformanceComparisonTest.class + .getSimpleName(); + } +} diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java index a1b48333c30b52a7968d3553a339b68d3cedb188..3e615e9b9e374231d776833ea432e7862078f2a9 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/HyperSqlDatabasePerformanceTest.java @@ -6,7 +6,7 @@ import org.junit.Ignore; @Ignore public class HyperSqlDatabasePerformanceTest - extends JdbcDatabasePerformanceTest { + extends JdbcSingleDatabasePerformanceTest { @Override protected String getTestName() { diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceComparisonTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ea8710bafbe9789c79e66e984ca04c393f343ede --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceComparisonTest.java @@ -0,0 +1,89 @@ +package org.briarproject.bramble.db; + +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.system.SystemClock; +import org.briarproject.bramble.test.TestDatabaseConfig; +import org.briarproject.bramble.test.UTest; + +import java.io.IOException; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; + +import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; +import static org.briarproject.bramble.test.TestUtils.getMean; +import static org.briarproject.bramble.test.TestUtils.getMedian; +import static org.briarproject.bramble.test.TestUtils.getStandardDeviation; +import static org.briarproject.bramble.test.UTest.Z_CRITICAL_0_01; + +public abstract class JdbcDatabasePerformanceComparisonTest + extends JdbcDatabasePerformanceTest { + + /** + * How many blocks of each condition to compare. + */ + private static final int COMPARISON_BLOCKS = 10; + + abstract Database<Connection> createDatabase(boolean conditionA, + DatabaseConfig databaseConfig, Clock clock); + + @Override + protected void benchmark(String name, + BenchmarkTask<Database<Connection>> task) throws Exception { + List<Double> aDurations = new ArrayList<>(); + List<Double> bDurations = new ArrayList<>(); + boolean aFirst = true; + for (int i = 0; i < COMPARISON_BLOCKS; i++) { + // Alternate between running the A and B benchmarks first + if (aFirst) { + aDurations.addAll(benchmark(true, task).durations); + bDurations.addAll(benchmark(false, task).durations); + } else { + bDurations.addAll(benchmark(false, task).durations); + aDurations.addAll(benchmark(true, task).durations); + } + aFirst = !aFirst; + } + // Compare the results using a small P value, which increases our + // chance of getting an inconclusive result, making this a conservative + // test for performance differences + UTest.Result comparison = UTest.test(aDurations, bDurations, + Z_CRITICAL_0_01); + writeResult(name, aDurations, bDurations, comparison); + } + + private SteadyStateResult benchmark(boolean conditionA, + BenchmarkTask<Database<Connection>> task) throws Exception { + deleteTestDirectory(testDir); + Database<Connection> db = openDatabase(conditionA); + populateDatabase(db); + db.close(); + db = openDatabase(conditionA); + // Measure blocks of iterations until we reach a steady state + SteadyStateResult result = measureSteadyState(db, task); + db.close(); + return result; + } + + private Database<Connection> openDatabase(boolean conditionA) + throws DbException { + Database<Connection> db = createDatabase(conditionA, + new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock()); + db.open(); + return db; + } + + private void writeResult(String name, List<Double> aDurations, + List<Double> bDurations, UTest.Result comparison) + throws IOException { + String result = String.format("%s\t%,d\t%,d\t%,d\t%,d\t%,d\t%,d\t%s", + name, (long) getMean(aDurations), (long) getMedian(aDurations), + (long) getStandardDeviation(aDurations), + (long) getMean(bDurations), (long) getMedian(bDurations), + (long) getStandardDeviation(bDurations), + comparison.name()); + writeResult(result); + } +} diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceTest.java index b7580853cc0b764e1ecfabd7797c0287dee8a355..ffb6ed965de6357ef9ef705ea6dde67b2ee48242 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabasePerformanceTest.java @@ -2,7 +2,6 @@ package org.briarproject.bramble.db; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.identity.AuthorId; @@ -13,10 +12,7 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.ValidationManager.State; -import org.briarproject.bramble.api.system.Clock; -import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.test.BrambleTestCase; -import org.briarproject.bramble.test.TestDatabaseConfig; import org.briarproject.bramble.test.UTest; import org.junit.After; import org.junit.Before; @@ -24,6 +20,7 @@ import org.junit.Test; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.util.ArrayList; @@ -39,12 +36,9 @@ import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; -import static org.briarproject.bramble.test.TestUtils.getMean; -import static org.briarproject.bramble.test.TestUtils.getMedian; import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomId; -import static org.briarproject.bramble.test.TestUtils.getStandardDeviation; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.UTest.Result.INCONCLUSIVE; import static org.briarproject.bramble.test.UTest.Z_CRITICAL_0_1; @@ -54,7 +48,7 @@ import static org.junit.Assert.assertTrue; public abstract class JdbcDatabasePerformanceTest extends BrambleTestCase { private static final int ONE_MEGABYTE = 1024 * 1024; - private static final int MAX_SIZE = 100 * ONE_MEGABYTE; + static final int MAX_SIZE = 100 * ONE_MEGABYTE; /** * How many contacts to simulate. @@ -90,12 +84,19 @@ public abstract class JdbcDatabasePerformanceTest extends BrambleTestCase { private static final int OFFERED_MESSAGES_PER_CONTACT = 100; /** - * How many times to run each benchmark while measuring. + * How many benchmark iterations to run in each block. */ - private static final int MEASUREMENT_ITERATIONS = 100; + private static final int ITERATIONS_PER_BLOCK = 10; - private final File testDir = getTestDirectory(); - private final Random random = new Random(); + /** + * How many blocks must be similar before we conclude a steady state has + * been reached. + */ + private static final int STEADY_STATE_BLOCKS = 5; + + protected final File testDir = getTestDirectory(); + private final File resultsFile = new File(getTestName() + ".tsv"); + protected final Random random = new Random(); private LocalAuthor localAuthor; private List<ClientId> clientIds; @@ -108,8 +109,8 @@ public abstract class JdbcDatabasePerformanceTest extends BrambleTestCase { protected abstract String getTestName(); - protected abstract JdbcDatabase createDatabase(DatabaseConfig config, - Clock clock); + protected abstract void benchmark(String name, + BenchmarkTask<Database<Connection>> task) throws Exception; @Before public void setUp() { @@ -522,48 +523,7 @@ public abstract class JdbcDatabasePerformanceTest extends BrambleTestCase { return list.get(random.nextInt(list.size())); } - private void benchmark(String name, - BenchmarkTask<Database<Connection>> task) throws Exception { - deleteTestDirectory(testDir); - Database<Connection> db = openDatabase(); - populateDatabase(db); - db.close(); - db = openDatabase(); - // Measure the first iteration - long start = System.nanoTime(); - task.run(db); - long firstDuration = System.nanoTime() - start; - // Measure blocks of iterations until we reach a steady state - List<Double> oldDurations = measureBlock(db, task); - List<Double> durations = measureBlock(db, task); - int blocks = 2; - while (UTest.test(oldDurations, durations, Z_CRITICAL_0_1) - != INCONCLUSIVE) { - oldDurations = durations; - durations = measureBlock(db, task); - blocks++; - } - db.close(); - String result = String.format("%s\t%d\t%,d\t%,d\t%,d\t%,d", name, - blocks, firstDuration, (long) getMean(durations), - (long) getMedian(durations), - (long) getStandardDeviation(durations)); - System.out.println(result); - File resultsFile = new File(getTestName() + ".tsv"); - PrintWriter out = - new PrintWriter(new FileOutputStream(resultsFile, true), true); - out.println(new Date() + "\t" + result); - out.close(); - } - - private Database<Connection> openDatabase() throws DbException { - Database<Connection> db = createDatabase( - new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock()); - db.open(); - return db; - } - - private void populateDatabase(Database<Connection> db) throws DbException { + void populateDatabase(Database<Connection> db) throws DbException { localAuthor = getLocalAuthor(); clientIds = new ArrayList<>(); contacts = new ArrayList<>(); @@ -640,17 +600,6 @@ public abstract class JdbcDatabasePerformanceTest extends BrambleTestCase { db.commitTransaction(txn); } - private List<Double> measureBlock(Database<Connection> db, - BenchmarkTask<Database<Connection>> task) throws Exception { - List<Double> durations = new ArrayList<>(MEASUREMENT_ITERATIONS); - for (int i = 0; i < MEASUREMENT_ITERATIONS; i++) { - long start = System.nanoTime(); - task.run(db); - durations.add((double) (System.nanoTime() - start)); - } - return durations; - } - private ClientId getClientId() { return new ClientId(getRandomString(CLIENT_ID_LENGTH)); } @@ -664,4 +613,56 @@ public abstract class JdbcDatabasePerformanceTest extends BrambleTestCase { } return meta; } + + long measureOne(Database<Connection> db, + BenchmarkTask<Database<Connection>> task) throws Exception { + long start = System.nanoTime(); + task.run(db); + return System.nanoTime() - start; + } + + private List<Double> measureBlock(Database<Connection> db, + BenchmarkTask<Database<Connection>> task) throws Exception { + List<Double> durations = new ArrayList<>(ITERATIONS_PER_BLOCK); + for (int i = 0; i < ITERATIONS_PER_BLOCK; i++) + durations.add((double) measureOne(db, task)); + return durations; + } + + SteadyStateResult measureSteadyState(Database<Connection> db, + BenchmarkTask<Database<Connection>> task) throws Exception { + List<Double> durations = measureBlock(db, task); + int blocks = 1, steadyBlocks = 1; + while (steadyBlocks < STEADY_STATE_BLOCKS) { + List<Double> prev = durations; + durations = measureBlock(db, task); + // Compare to the previous block with a large P value, which + // decreases our chance of getting an inconclusive result, making + // this a conservative test for steady state + if (UTest.test(prev, durations, Z_CRITICAL_0_1) == INCONCLUSIVE) + steadyBlocks++; + else steadyBlocks = 1; + blocks++; + } + return new SteadyStateResult(blocks, durations); + } + + void writeResult(String result) throws IOException { + System.out.println(result); + PrintWriter out = + new PrintWriter(new FileOutputStream(resultsFile, true), true); + out.println(new Date() + "\t" + result); + out.close(); + } + + static class SteadyStateResult { + + final int blocks; + final List<Double> durations; + + SteadyStateResult(int blocks, List<Double> durations) { + this.blocks = blocks; + this.durations = durations; + } + } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcSingleDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcSingleDatabasePerformanceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2356d9346a884e19e42766185b40f9f6dc62a11a --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcSingleDatabasePerformanceTest.java @@ -0,0 +1,55 @@ +package org.briarproject.bramble.db; + +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.system.SystemClock; +import org.briarproject.bramble.test.TestDatabaseConfig; + +import java.io.IOException; +import java.sql.Connection; +import java.util.List; + +import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; +import static org.briarproject.bramble.test.TestUtils.getMean; +import static org.briarproject.bramble.test.TestUtils.getMedian; +import static org.briarproject.bramble.test.TestUtils.getStandardDeviation; + +public abstract class JdbcSingleDatabasePerformanceTest + extends JdbcDatabasePerformanceTest { + + abstract Database<Connection> createDatabase(DatabaseConfig databaseConfig, + Clock clock); + + @Override + protected void benchmark(String name, + BenchmarkTask<Database<Connection>> task) throws Exception { + deleteTestDirectory(testDir); + Database<Connection> db = openDatabase(); + populateDatabase(db); + db.close(); + db = openDatabase(); + // Measure the first iteration + long firstDuration = measureOne(db, task); + // Measure blocks of iterations until we reach a steady state + SteadyStateResult result = measureSteadyState(db, task); + db.close(); + writeResult(name, result.blocks, firstDuration, result.durations); + } + + private Database<Connection> openDatabase() throws DbException { + Database<Connection> db = createDatabase( + new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock()); + db.open(); + return db; + } + + private void writeResult(String name, int blocks, long firstDuration, + List<Double> durations) throws IOException { + String result = String.format("%s\t%d\t%,d\t%,d\t%,d\t%,d", name, + blocks, firstDuration, (long) getMean(durations), + (long) getMedian(durations), + (long) getStandardDeviation(durations)); + writeResult(result); + } +}