Skip to content

Commit

Permalink
Merge pull request #19 from Workiva/web-benchmarks
Browse files Browse the repository at this point in the history
Web benchmarks
  • Loading branch information
travissanderson-wf authored Dec 13, 2018
2 parents c3094cf + b82cc95 commit 21757f9
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 56 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
.pub
packages
pubspec.lock
doc
build

# coverage
/coverage/
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ A recursive RTree library written in Dart.

"R-trees are tree data structures used for spatial access methods, i.e., for indexing multi-dimensional information such as geographical coordinates, rectangles or polygons." - http://en.wikipedia.org/wiki/R-tree

## Benchmarks

Run the benchmarks in the command line (Dart VM) using:
```
dart benchmark/benchmarks.dart
```

You can also run them in a browser using dart2js using `pub serve benchmark` or `pub build benchmark` and then serving them with your http server of choice. Click the run button and observe the output in the browser console.

## API

- *RTree* ( [ Number **branch_factor** ] )
Expand Down
109 changes: 68 additions & 41 deletions benchmark/benchmarks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,33 @@ import 'dart:math';

import 'package:benchmark_harness/benchmark_harness.dart';

import '../lib/r_tree.dart';
import 'package:r_tree/r_tree.dart';

final int BRANCH_FACTOR = 16;

main() {
RTreeBenchmark.main();
print('Running benchmark...');
var collector = new ScoreCollector();
new InsertBenchmark(collector).report();
new RemoveBenchmark(collector).report();
new SearchBenchmark1(collector).report();
new SearchBenchmark2(collector).report();
new SearchBenchmark1(collector, iterateAll: true).report();
new SearchBenchmark2(collector, iterateAll: true).report();

var output = '\nName\tResult (microseconds)\n';
collector.collected.forEach((String name, double value) {
output += '$name\t$value\n';
});

print(output);
}

class RTreeBenchmark {
static void main() {
InsertBenchmark.main();
RemoveBenchmark.main();
SearchBenchmark1.main();
SearchBenchmark2.main();
}
}

class InsertBenchmark extends BenchmarkBase {
InsertBenchmark() : super("Insert 5000 items with random rectangles.");
class InsertBenchmark extends RTreeBenchmarkBase {
InsertBenchmark(ScoreCollector collector) : super("Insert 5k", collector);

RTree<String> tree;

static void main() {
new InsertBenchmark().report();
}

void run() {
Random rand = new Random();
for (int i = 0; i < 5000; i++) {
Expand All @@ -47,16 +48,12 @@ class InsertBenchmark extends BenchmarkBase {
void teardown() {}
}

class RemoveBenchmark extends BenchmarkBase {
RemoveBenchmark() : super("Remove 5000 items from a tree of 10000 items.");
class RemoveBenchmark extends RTreeBenchmarkBase {
RemoveBenchmark(ScoreCollector collector) : super("Remove 5k", collector);

RTree<String> tree;
List<List<RTreeDatum>> items = [];

static void main() {
new RemoveBenchmark().report();
}

void run() {
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 50; j++) {
Expand All @@ -83,21 +80,22 @@ class RemoveBenchmark extends BenchmarkBase {
void teardown() {}
}

class SearchBenchmark1 extends BenchmarkBase {
SearchBenchmark1()
: super(
"Search 5000 items. (500 rectangles, 10 items each) Find all 10 items for each of the 500 rectangles.");
class SearchBenchmark1 extends RTreeBenchmarkBase {
final bool iterateAll;
SearchBenchmark1(ScoreCollector collector, {this.iterateAll: false})
: super("Search${iterateAll ? '/Iterate' : ''} 5k", collector);

RTree<String> tree;

static void main() {
new SearchBenchmark1().report();
}

void run() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 50; j++) {
tree.search(new Rectangle(i, j, 1, 1));
var results = tree.search(new Rectangle(i, j, 1, 1));
if (iterateAll) {
for (var result in results) {
// nothing to do here, just iterating over every result once
}
}
}
}
}
Expand Down Expand Up @@ -125,21 +123,23 @@ class SearchBenchmark1 extends BenchmarkBase {
void teardown() {}
}

class SearchBenchmark2 extends BenchmarkBase {
SearchBenchmark2()
: super(
"Search 30000 items. (10000 rectangles. 3 items each) Find all 3 items for 5000 of the rectangles.");
class SearchBenchmark2 extends RTreeBenchmarkBase {
final bool iterateAll;

RTree<String> tree;
SearchBenchmark2(ScoreCollector collector, {this.iterateAll: false})
: super("Search${iterateAll ? '/Iterate' : ''} 30k", collector);

static void main() {
new SearchBenchmark2().report();
}
RTree<String> tree;

void run() {
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 50; j++) {
tree.search(new Rectangle(i, j, 1, 1));
var results = tree.search(new Rectangle(i, j, 1, 1));
if (iterateAll) {
for (var result in results) {
// nothing to do here, just iterating over every result once
}
}
}
}
}
Expand All @@ -159,3 +159,30 @@ class SearchBenchmark2 extends BenchmarkBase {

void teardown() {}
}

class RTreeBenchmarkBase extends BenchmarkBase {
final int iterations;

RTreeBenchmarkBase(String name, ScoreCollector collector, {this.iterations: 100})
: super(name, emitter: collector);

@override
void exercise() {
for (int i = 0; i < iterations; i++) {
run();
}
}
}

class ScoreCollector extends ScoreEmitter {
Map<String, double> collected = {};

@override
void emit(String testName, double value) {
if (collected.containsKey(testName)) {
throw new StateError('Already collected results for $testName');
}

collected[testName] = value;
}
}
13 changes: 13 additions & 0 deletions benchmark/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>r_tree benchmarks</title>

<script type="application/dart" src="web_benchmarks.dart"></script>
<script src="./packages/browser/dart.js"></script>
</head>
<body>
<button id="runButton">Run benchmark (outputs to console)</button>
</body>
</html>
9 changes: 9 additions & 0 deletions benchmark/web_benchmarks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'dart:html';

import 'benchmarks.dart' as benchmarks;

main() {
querySelector('#runButton').onClick.listen((_) {
benchmarks.main();
});
}
3 changes: 3 additions & 0 deletions lib/r_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
* limitations under the License.
*/

/// A recursive RTree library written in Dart.
///
/// "R-trees are tree data structures used for spatial access methods, i.e., for indexing multi-dimensional information such as geographical coordinates, rectangles or polygons." - http://en.wikipedia.org/wiki/R-tree
library r_tree;

import 'dart:math';
Expand Down
12 changes: 6 additions & 6 deletions lib/src/r_tree/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ abstract class Node<E> extends RTreeContributor {

_minimumBoundingRect = null;

children.forEach((RTreeContributor child) {
for (var child in children) {
include(child);
});
}
}

Node<E> splitIfNecessary() => size > branchFactor ? _split() : null;
Expand All @@ -94,7 +94,7 @@ abstract class Node<E> extends RTreeContributor {
}

_reassignRemainingChildren(List<RTreeContributor> remainingChildren, Node<E> splitNode) {
remainingChildren.forEach((RTreeContributor child) {
for (var child in remainingChildren) {
num thisExpansionCost = expansionCost(child);
num splitExpansionCost = splitNode.expansionCost(child);

Expand All @@ -107,7 +107,7 @@ abstract class Node<E> extends RTreeContributor {
} else {
splitNode.addChild(child);
}
});
}
}

_Seeds _pickSeeds() {
Expand All @@ -119,12 +119,12 @@ abstract class Node<E> extends RTreeContributor {
RTreeContributor topmost = children.elementAt(0);
RTreeContributor bottommost = children.elementAt(0);

children.forEach((RTreeContributor child) {
for (var child in children) {
if (child.rect.right < leftmost.rect.right) leftmost = child;
if (child.rect.left > rightmost.rect.left) rightmost = child;
if (child.rect.top > bottommost.rect.top) bottommost = child;
if (child.rect.bottom < topmost.rect.bottom) topmost = child;
});
}

RTreeContributor a, b, c, d;
if (_horizontalDifference(leftmost, rightmost) > _verticalDifference(topmost, bottommost)) {
Expand Down
16 changes: 8 additions & 8 deletions lib/src/r_tree/non_leaf_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ class NonLeafNode<E> extends Node<E> {
Iterable<RTreeDatum<E>> search(Rectangle searchRect) {
List<RTreeDatum<E>> overlappingLeafs = [];

_childNodes.forEach((Node<E> childNode) {
for (var childNode in _childNodes) {
if (childNode.overlaps(searchRect)) {
overlappingLeafs.addAll(childNode.search(searchRect));
}
});
}

return overlappingLeafs;
}
Expand All @@ -54,19 +54,19 @@ class NonLeafNode<E> extends Node<E> {
remove(RTreeDatum<E> item) {
List<Node<E>> childrenToRemove = [];

_childNodes.forEach((Node<E> childNode) {
for (var childNode in _childNodes) {
if (childNode.overlaps(item.rect)) {
childNode.remove(item);

if (childNode.size == 0) {
childrenToRemove.add(childNode);
}
}
});
}

childrenToRemove.forEach((Node<E> child) {
for (var child in childrenToRemove) {
removeChild(child);
});
}
}

addChild(Node<E> child) {
Expand All @@ -93,13 +93,13 @@ class NonLeafNode<E> extends Node<E> {
num tentativeCost;
Node<E> bestNode;

_childNodes.forEach((Node<E> child) {
for (var child in _childNodes) {
tentativeCost = child.expansionCost(item);
if (tentativeCost < bestCost) {
bestCost = tentativeCost;
bestNode = child;
}
});
}

return bestNode;
}
Expand Down
10 changes: 9 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ environment:
sdk: ">=1.24.2 <2.0.0"

dev_dependencies:
browser: any
benchmark_harness: any
coverage: ^0.7.9
dart_style: 1.0.7
dart_dev: ^1.7.6
test: ^0.12.0
dartdoc: ^0.14.1
test: ^0.12.0

transformers:
- $dart2js:
minify: false
sourceMaps: true
commandLineOptions: [--trust-type-annotations]

0 comments on commit 21757f9

Please sign in to comment.