/*
* Copyright (C) 2012 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.common.collect;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.common.base.Optional;
import com.google.common.primitives.Ints;
import java.util.List;
import java.util.Random;
/**
* Benchmarks for the {@code TreeTraverser} and optimized {@code BinaryTreeTraverser} operations on
* binary trees.
*
* @author Louis Wasserman
*/
public class BinaryTreeTraverserBenchmark {
private static class BinaryNode {
final int x;
final Optional<BinaryNode> left;
final Optional<BinaryNode> right;
BinaryNode(int x, Optional<BinaryNode> left, Optional<BinaryNode> right) {
this.x = x;
this.left = left;
this.right = right;
}
}
enum Topology {
BALANCED {
@Override
Optional<BinaryNode> createTree(int size, Random rng) {
if (size == 0) {
return Optional.absent();
} else {
int leftChildSize = (size - 1) / 2;
int rightChildSize = size - 1 - leftChildSize;
return Optional.of(new BinaryNode(
rng.nextInt(), createTree(leftChildSize, rng), createTree(rightChildSize, rng)));
}
}
},
ALL_LEFT {
@Override
Optional<BinaryNode> createTree(int size, Random rng) {
Optional<BinaryNode> root = Optional.absent();
for (int i = 0; i < size; i++) {
root = Optional.of(new BinaryNode(rng.nextInt(), root, Optional.<BinaryNode>absent()));
}
return root;
}
},
ALL_RIGHT {
@Override
Optional<BinaryNode> createTree(int size, Random rng) {
Optional<BinaryNode> root = Optional.absent();
for (int i = 0; i < size; i++) {
root = Optional.of(new BinaryNode(rng.nextInt(), Optional.<BinaryNode>absent(), root));
}
return root;
}
},
RANDOM {
/**
* Generates a tree with topology selected uniformly at random from the topologies of binary
* trees of the specified size.
*/
@Override
Optional<BinaryNode> createTree(int size, Random rng) {
int[] keys = new int[size];
for (int i = 0; i < size; i++) {
keys[i] = rng.nextInt();
}
return createTreap(Ints.asList(keys));
}
// See http://en.wikipedia.org/wiki/Treap for details on the algorithm.
private Optional<BinaryNode> createTreap(List<Integer> keys) {
if (keys.isEmpty()) {
return Optional.absent();
}
int minIndex = 0;
for (int i = 1; i < keys.size(); i++) {
if (keys.get(i) < keys.get(minIndex)) {
minIndex = i;
}
}
Optional<BinaryNode> leftChild = createTreap(keys.subList(0, minIndex));
Optional<BinaryNode> rightChild = createTreap(keys.subList(minIndex + 1, keys.size()));
return Optional.of(new BinaryNode(keys.get(minIndex), leftChild, rightChild));
}
};
abstract Optional<BinaryNode> createTree(int size, Random rng);
}
private static final BinaryTreeTraverser<BinaryNode> BINARY_VIEWER =
new BinaryTreeTraverser<BinaryNode>() {
@Override
public Optional<BinaryNode> leftChild(BinaryNode node) {
return node.left;
}
@Override
public Optional<BinaryNode> rightChild(BinaryNode node) {
return node.right;
}
};
private static final TreeTraverser<BinaryNode> VIEWER = new TreeTraverser<BinaryNode>() {
@Override
public Iterable<BinaryNode> children(BinaryNode root) {
return BINARY_VIEWER.children(root);
}
};
enum Traversal {
PRE_ORDER {
@Override
<T> Iterable<T> view(T root, TreeTraverser<T> viewer) {
return viewer.preOrderTraversal(root);
}
},
POST_ORDER {
@Override
<T> Iterable<T> view(T root, TreeTraverser<T> viewer) {
return viewer.postOrderTraversal(root);
}
},
BREADTH_FIRST {
@Override
<T> Iterable<T> view(T root, TreeTraverser<T> viewer) {
return viewer.breadthFirstTraversal(root);
}
};
abstract <T> Iterable<T> view(T root, TreeTraverser<T> viewer);
}
private Iterable<BinaryNode> view;
@Param
Topology topology;
@Param({"1", "100", "10000", "1000000"})
int size;
@Param
Traversal traversal;
@Param
boolean useBinaryTraverser;
@Param({"1234"})
SpecialRandom rng;
@BeforeExperiment
void setUp() {
this.view = traversal.view(
topology.createTree(size, rng).get(),
useBinaryTraverser ? BINARY_VIEWER : VIEWER);
}
@Benchmark int traversal(int reps) {
int tmp = 0;
for (int i = 0; i < reps; i++) {
for (BinaryNode node : view) {
tmp += node.x;
}
}
return tmp;
}
}