From 0c82a879e1e0f90f2b4ee55bd2536317537f748b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 6 Jan 2021 13:59:18 +0100 Subject: [PATCH 1/2] Add simple CRUD performance benchmark --- analysis_options.yaml | 4 +- benchmark/.gitignore | 12 +++++ benchmark/bin/main.dart | 57 ++++++++++++++++++++ benchmark/lib/benchmark.dart | 84 ++++++++++++++++++++++++++++++ benchmark/lib/model.dart | 20 +++++++ benchmark/lib/objectbox-model.json | 51 ++++++++++++++++++ benchmark/pubspec.yaml | 18 +++++++ 7 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 benchmark/.gitignore create mode 100644 benchmark/bin/main.dart create mode 100644 benchmark/lib/benchmark.dart create mode 100644 benchmark/lib/model.dart create mode 100644 benchmark/lib/objectbox-model.json create mode 100644 benchmark/pubspec.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml index e71865266..945037083 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -5,7 +5,9 @@ linter: # additional rules used by pub.dev non_constant_identifier_names: true +# exclude standalone packages: analyzer: exclude: - example/** - - generator/** # needs to be checked separately because its a separate package \ No newline at end of file + - generator/** + - benchmark/** \ No newline at end of file diff --git a/benchmark/.gitignore b/benchmark/.gitignore new file mode 100644 index 000000000..befc3b811 --- /dev/null +++ b/benchmark/.gitignore @@ -0,0 +1,12 @@ +# Files and directories created by pub +.dart_tool/ +.packages + +# Conventional directory for build outputs +build/ + +# Directory created by dartdoc +doc/api/ + +# ObjectBox DB files +**.mdb \ No newline at end of file diff --git a/benchmark/bin/main.dart b/benchmark/bin/main.dart new file mode 100644 index 000000000..dd21d4ff6 --- /dev/null +++ b/benchmark/bin/main.dart @@ -0,0 +1,57 @@ +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:glob/glob.dart'; +import 'package:objectbox_benchmark/benchmark.dart'; + +void main(List arguments) { + exitCode = 0; // presume success + + final argDb = 'db'; + final argCount = 'count'; + final argRuns = 'runs'; + + final parser = ArgParser() + ..addOption(argDb, defaultsTo: 'benchmark-db', help: 'database directory') + ..addOption(argCount, defaultsTo: '10000', help: 'number of objects') + ..addOption(argRuns, + defaultsTo: '30', help: 'number of times the tests should be executed'); + + final args = parser.parse(arguments); + final dbDir = Directory(args[argDb]); + final count = int.parse(args[argCount]); + final runs = int.parse(args[argRuns]); + + print('running $runs times with $count objects in $dbDir'); + + if (dbDir.existsSync()) { + print('deleting existing DB files in $dbDir'); + final dbFilesGlob = Glob(dbDir.path + '/*.mdb'); + for (var dbFile in dbFilesGlob.listSync()) { + dbFile.deleteSync(); + } + } + + final bench = Executor(dbDir); + + final inserts = bench.prepareData(count); + + for (var i = 0; i < runs; i++) { + bench.putMany(inserts); + final items = bench.readAll(); + bench.changeValues(items); + bench.updateAll(items); + bench.removeAll(); + + print('${i + 1}/$runs finished'); + } + + bench.close(); + bench.printTimes([ + 'putMany', + 'readAll', + 'updateAll', + 'removeAll', + ]); +} + diff --git a/benchmark/lib/benchmark.dart b/benchmark/lib/benchmark.dart new file mode 100644 index 000000000..251d98c8d --- /dev/null +++ b/benchmark/lib/benchmark.dart @@ -0,0 +1,84 @@ +import 'dart:io'; + +import 'package:objectbox/objectbox.dart'; + +import 'model.dart'; +import 'objectbox.g.dart'; + +class Executor { + final Store store; + + /*late final*/ + Box box; + + /// list of runtimes indexed by function name + final times = >{}; + + Executor(Directory dbDir) + : store = Store(getObjectBoxModel(), directory: dbDir.path) { + box = Box(store); + } + + void close() => store.close(); + + R _track(String fnName, R Function() fn) { + final watch = Stopwatch(); + + watch.start(); + final result = fn(); + watch.stop(); + + times[fnName] ??= []; + times[fnName].add(watch.elapsed); + return result; + } + + void _print(List varArgs) { + print(varArgs.join('\t')); + } + + void printTimes([List functions]) { + functions ??= times.keys.toList(); + + // print the whole data as a table + _print(['Function', 'Runs', 'Average ms', 'All times']); + for (final fun in functions) { + final fnTimes = times[fun]; + + final sum = fnTimes.map((d) => d.inMicroseconds).reduce((v, e) => v + e); + final avg = sum.toDouble() / fnTimes.length.toDouble() / 1000; + final timesCols = fnTimes.map((d) => d.inMicroseconds.toDouble() / 1000); + _print([fun, fnTimes.length, avg, ...timesCols]); + } + } + + List prepareData(int count) { + return _track('prepareData', () { + final result = []; + for (var i = 0; i < count; i++) { + result.add(TestEntity.full('Entity #$i', i, i, i.toDouble())); + } + return result; + }); + } + + void putMany(List items) { + _track('putMany', () => box.putMany(items)); + } + + void updateAll(List items) { + _track('updateAll', () => box.putMany(items)); + } + + List readAll() { + return _track('readAll', () => box.getAll()); + } + + void removeAll() { + _track('removeAll', () => box.removeAll()); + } + + void changeValues(List items) { + _track('changeValues', () => items.forEach((item) => item.tLong *= 2)); + } +} diff --git a/benchmark/lib/model.dart b/benchmark/lib/model.dart new file mode 100644 index 000000000..d8f54b499 --- /dev/null +++ b/benchmark/lib/model.dart @@ -0,0 +1,20 @@ +import 'package:objectbox/objectbox.dart'; + +@Entity() +class TestEntity { + @Id() + int /*?*/ id; + + String /*?*/ tString; + + @Property(type: PropertyType.int) + int /*?*/ tInt; // 32-bit + + int /*?*/ tLong; // 64-bit + + double /*?*/ tDouble; + + TestEntity(); + + TestEntity.full(this.tString, this.tInt, this.tLong, this.tDouble); +} diff --git a/benchmark/lib/objectbox-model.json b/benchmark/lib/objectbox-model.json new file mode 100644 index 000000000..0e63ce558 --- /dev/null +++ b/benchmark/lib/objectbox-model.json @@ -0,0 +1,51 @@ +{ + "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", + "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", + "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", + "entities": [ + { + "id": "1:1824294286181366916", + "lastPropertyId": "5:5062831200094422416", + "name": "TestEntity", + "properties": [ + { + "id": "1:725236837183566145", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:3060128101942237916", + "name": "tString", + "type": 9 + }, + { + "id": "3:3092165041934597703", + "name": "tInt", + "type": 5 + }, + { + "id": "4:4958684909838795382", + "name": "tLong", + "type": 6 + }, + { + "id": "5:5062831200094422416", + "name": "tDouble", + "type": 8 + } + ] + } + ], + "lastEntityId": "1:1824294286181366916", + "lastIndexId": "0:0", + "lastRelationId": "0:0", + "lastSequenceId": "0:0", + "modelVersion": 5, + "modelVersionParserMinimum": 5, + "retiredEntityUids": [], + "retiredIndexUids": [], + "retiredPropertyUids": [], + "retiredRelationUids": [], + "version": 1 +} \ No newline at end of file diff --git a/benchmark/pubspec.yaml b/benchmark/pubspec.yaml new file mode 100644 index 000000000..619927066 --- /dev/null +++ b/benchmark/pubspec.yaml @@ -0,0 +1,18 @@ +name: objectbox_benchmark +description: Simple ObjectBox-Dart performance benchmark + +environment: + sdk: ">=2.6.0 <3.0.0" + +dependencies: + objectbox: any + +dev_dependencies: + objectbox_generator: any + build_runner: ^1.0.0 + +dependency_overrides: + objectbox: + path: ../ + objectbox_generator: + path: ../generator From 948dcca75d9e4ac27293da3ca176e8d1dc2c91f3 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 12 Jan 2021 18:30:17 +0100 Subject: [PATCH 2/2] benchmark - make `dart run` work --- benchmark/bin/{main.dart => benchmark.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename benchmark/bin/{main.dart => benchmark.dart} (100%) diff --git a/benchmark/bin/main.dart b/benchmark/bin/benchmark.dart similarity index 100% rename from benchmark/bin/main.dart rename to benchmark/bin/benchmark.dart