Commit 43c59cc3 authored by Kenton Varda's avatar Kenton Varda

Benchmark case: car sales

parent f8b5d71d
...@@ -10,10 +10,10 @@ continuous: ...@@ -10,10 +10,10 @@ continuous:
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -g -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315 CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -g -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315
continuous-opt: continuous-opt:
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -DNDEBUG -Wall' LIBS='-lz -lprofiler -pthread' ekam -j6 -c -n :51315 CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -DNDEBUG -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315
continuous-prof: continuous-opt3:
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -DNDEBUG -Wall -pg' LIBS='-pg -lz -pthread' ekam -j6 -c -n :51315 CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O3 -DNDEBUG -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315
clean: clean:
rm -rf bin lib tmp rm -rf bin lib tmp
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "benchmark.capnp.h" #include "benchmark.capnp.h"
#include <limits>
#include <capnproto/serialize.h> #include <capnproto/serialize.h>
#include <capnproto/serialize-snappy.h> #include <capnproto/serialize-snappy.h>
#include <unistd.h> #include <unistd.h>
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <semaphore.h> #include <semaphore.h>
#include "fast-random.h"
namespace capnproto { namespace capnproto {
namespace benchmark { namespace benchmark {
...@@ -118,23 +120,23 @@ inline int32_t mod(int32_t a, int32_t b) { ...@@ -118,23 +120,23 @@ inline int32_t mod(int32_t a, int32_t b) {
return a % b; return a % b;
} }
int32_t makeExpression(Expression::Builder exp, int depth) { int32_t makeExpression(Expression::Builder exp, uint depth) {
// TODO: Operation_MAX or something. // TODO: Operation_RANGE or something.
exp.setOp((Operation)(rand() % (int)Operation::MODULUS + 1)); exp.setOp((Operation)(fastRand((int)Operation::MODULUS + 1)));
uint32_t left, right; uint32_t left, right;
if (rand() % 8 < depth) { if (fastRand(8) < depth) {
exp.setLeftIsValue(true); exp.setLeftIsValue(true);
left = rand() % 128 + 1; left = fastRand(128) + 1;
exp.setLeftValue(left); exp.setLeftValue(left);
} else { } else {
left = makeExpression(exp.initLeftExpression(), depth + 1); left = makeExpression(exp.initLeftExpression(), depth + 1);
} }
if (rand() % 8 < depth) { if (fastRand(8) < depth) {
exp.setRightIsValue(true); exp.setRightIsValue(true);
right = rand() % 128 + 1; right = fastRand(128) + 1;
exp.setRightValue(right); exp.setRightValue(right);
} else { } else {
right = makeExpression(exp.initRightExpression(), depth + 1); right = makeExpression(exp.initRightExpression(), depth + 1);
...@@ -214,7 +216,7 @@ public: ...@@ -214,7 +216,7 @@ public:
// The promotion multiplier is large enough that all the results mentioning "cat" but not "dog" // The promotion multiplier is large enough that all the results mentioning "cat" but not "dog"
// should end up at the front ofthe list, which is how we verify the result. // should end up at the front ofthe list, which is how we verify the result.
static const char* WORDS[] = { static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ", "foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud " "plugh ", "xyzzy ", "thud "
}; };
...@@ -237,7 +239,7 @@ public: ...@@ -237,7 +239,7 @@ public:
typedef int Expectation; typedef int Expectation;
static int setupRequest(SearchResultList::Builder request) { static int setupRequest(SearchResultList::Builder request) {
int count = rand() % 1000; int count = fastRand(1000);
int goodCount = 0; int goodCount = 0;
auto list = request.initResults(count); auto list = request.initResults(count);
...@@ -245,7 +247,7 @@ public: ...@@ -245,7 +247,7 @@ public:
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
SearchResult::Builder result = list[i]; SearchResult::Builder result = list[i];
result.setScore(1000 - i); result.setScore(1000 - i);
int urlSize = rand() % 100; int urlSize = fastRand(100);
static const char URL_PREFIX[] = "http://example.com/"; static const char URL_PREFIX[] = "http://example.com/";
auto url = result.initUrl(urlSize + sizeof(URL_PREFIX)); auto url = result.initUrl(urlSize + sizeof(URL_PREFIX));
...@@ -253,28 +255,28 @@ public: ...@@ -253,28 +255,28 @@ public:
strcpy(url.data(), URL_PREFIX); strcpy(url.data(), URL_PREFIX);
char* pos = url.data() + strlen(URL_PREFIX); char* pos = url.data() + strlen(URL_PREFIX);
for (int j = 0; j < urlSize; j++) { for (int j = 0; j < urlSize; j++) {
*pos++ = 'a' + rand() % 26; *pos++ = 'a' + fastRand(26);
} }
bool isCat = rand() % 8 == 0; bool isCat = fastRand(8) == 0;
bool isDog = rand() % 8 == 0; bool isDog = fastRand(8) == 0;
goodCount += isCat && !isDog; goodCount += isCat && !isDog;
static std::string snippet; static std::string snippet;
snippet.clear(); snippet.clear();
snippet.push_back(' '); snippet.push_back(' ');
int prefix = rand() % 20; int prefix = fastRand(20);
for (int j = 0; j < prefix; j++) { for (int j = 0; j < prefix; j++) {
snippet.append(WORDS[rand() % WORDS_COUNT]); snippet.append(WORDS[fastRand(WORDS_COUNT)]);
} }
if (isCat) snippet.append("cat "); if (isCat) snippet.append("cat ");
if (isDog) snippet.append("dog "); if (isDog) snippet.append("dog ");
int suffix = rand() % 20; int suffix = fastRand(20);
for (int j = 0; j < suffix; j++) { for (int j = 0; j < suffix; j++) {
snippet.append(WORDS[rand() % WORDS_COUNT]); snippet.append(WORDS[fastRand(WORDS_COUNT)]);
} }
result.setSnippet(snippet); result.setSnippet(snippet);
...@@ -324,6 +326,112 @@ public: ...@@ -324,6 +326,112 @@ public:
} }
}; };
// =======================================================================================
// Test case: Car Sales
//
// We have a parking lot full of cars and we want to know how much they are worth.
template <typename ReaderOrBuilder>
uint64_t carValue(ReaderOrBuilder car) {
// Do not think too hard about realism.
uint64_t result = 0;
result += car.getSeats() * 200;
result += car.getDoors() * 350;
for (auto wheel: car.getWheels()) {
result += wheel.getDiameter() * wheel.getDiameter();
result += wheel.getSnowTires() ? 100 : 0;
}
result += car.getLength() * car.getWidth() * car.getHeight() / 50;
auto engine = car.getEngine();
result += engine.getHorsepower() * 40;
if (engine.getUsesElectric()) {
if (engine.getUsesGas()) {
// hybrid
result += 5000;
} else {
result += 3000;
}
}
result += car.getHasPowerWindows() ? 100 : 0;
result += car.getHasPowerSteering() ? 200 : 0;
result += car.getHasCruiseControl() ? 400 : 0;
result += car.getHasNavSystem() ? 2000 : 0;
result += car.getCupHolders() * 25;
return result;
}
void randomCar(Car::Builder car) {
// Do not think too hard about realism.
static const char* const MAKES[] = { "Toyota", "GM", "Ford", "Honda", "Tesla" };
static const char* const MODELS[] = { "Camry", "Prius", "Volt", "Accord", "Leaf", "Model S" };
car.setMake(MAKES[fastRand(sizeof(MAKES) / sizeof(MAKES[0]))]);
car.setModel(MODELS[fastRand(sizeof(MODELS) / sizeof(MODELS[0]))]);
// TODO: Color_RANGE or something.
car.setColor((Color)fastRand((uint)Color::SILVER + 1));
car.setSeats(2 + fastRand(6));
car.setDoors(2 + fastRand(3));
for (auto wheel: car.initWheels(4)) {
wheel.setDiameter(25 + fastRand(15));
wheel.setAirPressure(30 + fastRandDouble(20));
wheel.setSnowTires(fastRand(16) == 0);
}
car.setLength(170 + fastRand(150));
car.setWidth(48 + fastRand(36));
car.setHeight(54 + fastRand(48));
car.setWeight(car.getLength() * car.getWidth() * car.getHeight() / 200);
auto engine = car.initEngine();
engine.setHorsepower(100 * fastRand(400));
engine.setCylinders(4 + 2 * fastRand(3));
engine.setCc(800 + fastRand(10000));
car.setFuelCapacity(10.0 + fastRandDouble(30.0));
car.setFuelLevel(fastRandDouble(car.getFuelCapacity()));
car.setHasPowerWindows(fastRand(2));
car.setHasPowerSteering(fastRand(2));
car.setHasCruiseControl(fastRand(2));
car.setCupHolders(fastRand(12));
car.setHasNavSystem(fastRand(2));
}
class CarSalesTestCase {
public:
typedef ParkingLot Request;
typedef TotalValue Response;
typedef uint64_t Expectation;
static uint64_t setupRequest(ParkingLot::Builder request) {
uint64_t result = 0;
for (auto car: request.initCars(fastRand(200))) {
randomCar(car);
result += carValue(car);
}
return result;
}
static void handleRequest(ParkingLot::Reader request, TotalValue::Builder response) {
uint64_t result = 0;
for (auto car: request.getCars()) {
result += carValue(car);
}
response.setAmount(result);
}
static inline bool checkResponse(TotalValue::Reader response, uint64_t expected) {
return response.getAmount() == expected;
}
};
// ======================================================================================= // =======================================================================================
class CountingOutputStream: public FdOutputStream { class CountingOutputStream: public FdOutputStream {
...@@ -703,7 +811,6 @@ int main(int argc, char* argv[]) { ...@@ -703,7 +811,6 @@ int main(int argc, char* argv[]) {
} }
uint64_t iters = strtoull(argv[5], nullptr, 0); uint64_t iters = strtoull(argv[5], nullptr, 0);
srand(123);
uint64_t throughput; uint64_t throughput;
...@@ -712,6 +819,8 @@ int main(int argc, char* argv[]) { ...@@ -712,6 +819,8 @@ int main(int argc, char* argv[]) {
throughput = doBenchmark3<ExpressionTestCase>(argv[2], argv[3], argv[4], iters); throughput = doBenchmark3<ExpressionTestCase>(argv[2], argv[3], argv[4], iters);
} else if (testcase == "catrank") { } else if (testcase == "catrank") {
throughput = doBenchmark3<CatRankTestCase>(argv[2], argv[3], argv[4], iters); throughput = doBenchmark3<CatRankTestCase>(argv[2], argv[3], argv[4], iters);
} else if (testcase == "carsales") {
throughput = doBenchmark3<CarSalesTestCase>(argv[2], argv[3], argv[4], iters);
} else { } else {
std::cerr << "Unknown test case: " << testcase << std::endl; std::cerr << "Unknown test case: " << testcase << std::endl;
return 1; return 1;
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include <stdexcept> #include <stdexcept>
#include <algorithm> #include <algorithm>
#include <string.h> #include <string.h>
#include <limits>
#include "fast-random.h"
namespace capnproto { namespace capnproto {
namespace benchmark { namespace benchmark {
...@@ -64,7 +66,7 @@ enum class Operation { ...@@ -64,7 +66,7 @@ enum class Operation {
DIVIDE, DIVIDE,
MODULUS MODULUS
}; };
uint Operation_MAX = static_cast<uint>(Operation::MODULUS) + 1; uint OPERATION_RANGE = static_cast<uint>(Operation::MODULUS) + 1;
struct Expression { struct Expression {
Operation op; Operation op;
...@@ -97,14 +99,14 @@ inline int32_t mod(int32_t a, int32_t b) { ...@@ -97,14 +99,14 @@ inline int32_t mod(int32_t a, int32_t b) {
return a % b; return a % b;
} }
int32_t makeExpression(Expression* exp, int depth) { int32_t makeExpression(Expression* exp, uint depth) {
exp->op = (Operation)(rand() % Operation_MAX); exp->op = (Operation)(fastRand(OPERATION_RANGE));
int32_t left, right; int32_t left, right;
if (rand() % 8 < depth) { if (fastRand(8) < depth) {
exp->leftIsValue = true; exp->leftIsValue = true;
left = rand() % 128 + 1; left = fastRand(128) + 1;
exp->leftValue = left; exp->leftValue = left;
} else { } else {
exp->leftIsValue = false; exp->leftIsValue = false;
...@@ -112,9 +114,9 @@ int32_t makeExpression(Expression* exp, int depth) { ...@@ -112,9 +114,9 @@ int32_t makeExpression(Expression* exp, int depth) {
left = makeExpression(exp->leftExpression, depth + 1); left = makeExpression(exp->leftExpression, depth + 1);
} }
if (rand() % 8 < depth) { if (fastRand(8) < depth) {
exp->rightIsValue = true; exp->rightIsValue = true;
right = rand() % 128 + 1; right = fastRand(128) + 1;
exp->rightValue = right; exp->rightValue = right;
} else { } else {
exp->rightIsValue = false; exp->rightIsValue = false;
...@@ -200,7 +202,7 @@ public: ...@@ -200,7 +202,7 @@ public:
// The promotion multiplier is large enough that all the results mentioning "cat" but not "dog" // The promotion multiplier is large enough that all the results mentioning "cat" but not "dog"
// should end up at the front ofthe list, which is how we verify the result. // should end up at the front ofthe list, which is how we verify the result.
static const char* WORDS[] = { static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ", "foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud " "plugh ", "xyzzy ", "thud "
}; };
...@@ -213,6 +215,12 @@ struct List { ...@@ -213,6 +215,12 @@ struct List {
inline T* begin() const { return items; } inline T* begin() const { return items; }
inline T* end() const { return items + size; } inline T* end() const { return items + size; }
inline List<T>& init(size_t size) {
this->size = size;
items = allocate<T>(size);
return *this;
}
}; };
struct SearchResult { struct SearchResult {
...@@ -238,11 +246,10 @@ public: ...@@ -238,11 +246,10 @@ public:
typedef int Expectation; typedef int Expectation;
static int setupRequest(List<SearchResult>* request) { static int setupRequest(List<SearchResult>* request) {
int count = rand() % 1000; int count = fastRand(1000);
int goodCount = 0; int goodCount = 0;
request->size = count; request->init(count);
request->items = allocate<SearchResult>(count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
SearchResult& result = request->items[i]; SearchResult& result = request->items[i];
result.score = 1000 - i; result.score = 1000 - i;
...@@ -251,9 +258,9 @@ public: ...@@ -251,9 +258,9 @@ public:
strcpy(pos, "http://example.com/"); strcpy(pos, "http://example.com/");
pos += strlen("http://example.com/"); pos += strlen("http://example.com/");
int urlSize = rand() % 100; int urlSize = fastRand(100);
for (int j = 0; j < urlSize; j++) { for (int j = 0; j < urlSize; j++) {
*pos++ = 'a' + rand() % 26; *pos++ = 'a' + fastRand(26);
} }
*pos++ = '\0'; *pos++ = '\0';
...@@ -262,8 +269,8 @@ public: ...@@ -262,8 +269,8 @@ public:
throw std::bad_alloc(); throw std::bad_alloc();
} }
bool isCat = rand() % 8 == 0; bool isCat = fastRand(8) == 0;
bool isDog = rand() % 8 == 0; bool isDog = fastRand(8) == 0;
goodCount += isCat && !isDog; goodCount += isCat && !isDog;
pos = reinterpret_cast<char*>(arenaPos); pos = reinterpret_cast<char*>(arenaPos);
...@@ -271,9 +278,9 @@ public: ...@@ -271,9 +278,9 @@ public:
*pos++ = ' '; *pos++ = ' ';
int prefix = rand() % 20; int prefix = fastRand(20);
for (int j = 0; j < prefix; j++) { for (int j = 0; j < prefix; j++) {
const char* word = WORDS[rand() % WORDS_COUNT]; const char* word = WORDS[fastRand(WORDS_COUNT)];
size_t len = strlen(word); size_t len = strlen(word);
memcpy(pos, word, len); memcpy(pos, word, len);
pos += len; pos += len;
...@@ -288,9 +295,9 @@ public: ...@@ -288,9 +295,9 @@ public:
pos += 4; pos += 4;
} }
int suffix = rand() % 20; int suffix = fastRand(20);
for (int j = 0; j < suffix; j++) { for (int j = 0; j < suffix; j++) {
const char* word = WORDS[rand() % WORDS_COUNT]; const char* word = WORDS[fastRand(WORDS_COUNT)];
size_t len = strlen(word); size_t len = strlen(word);
memcpy(pos, word, len); memcpy(pos, word, len);
pos += len; pos += len;
...@@ -324,8 +331,7 @@ public: ...@@ -324,8 +331,7 @@ public:
std::sort(scoredResults.begin(), scoredResults.end()); std::sort(scoredResults.begin(), scoredResults.end());
response->size = scoredResults.size(); response->init(scoredResults.size());
response->items = allocate<SearchResult>(scoredResults.size());
SearchResult* dst = response->items; SearchResult* dst = response->items;
for (auto& result: scoredResults) { for (auto& result: scoredResults) {
dst->url = copyString(result.result->url); dst->url = copyString(result.result->url);
...@@ -350,6 +356,157 @@ public: ...@@ -350,6 +356,157 @@ public:
} }
}; };
// =======================================================================================
// Test case: Car Sales
//
// We have a parking lot full of cars and we want to know how much they are worth.
enum class Color {
BLACK,
WHITE,
RED,
GREEN,
BLUE,
CYAN,
MAGENTA,
YELLOW,
SILVER
};
constexpr uint COLOR_RANGE = static_cast<uint>(Color::SILVER) + 1;
struct Wheel {
uint16_t diameter;
float airPressure;
bool snowTires;
};
struct Engine {
uint16_t horsepower;
uint8_t cylinders;
uint32_t cc;
bool usesGas;
bool usesElectric;
};
struct Car {
const char* make;
const char* model;
Color color;
uint8_t seats;
uint8_t doors;
List<Wheel> wheels;
uint16_t length;
uint16_t width;
uint16_t height;
uint32_t weight;
Engine engine;
float fuelCapacity;
float fuelLevel;
bool hasPowerWindows;
bool hasPowerSteering;
bool hasCruiseControl;
uint8_t cupHolders;
bool hasNavSystem;
};
uint64_t carValue(const Car& car) {
// Do not think too hard about realism.
uint64_t result = 0;
result += car.seats * 200;
result += car.doors * 350;
for (auto wheel: car.wheels) {
result += wheel.diameter * wheel.diameter;
result += wheel.snowTires ? 100 : 0;
}
result += car.length * car.width * car.height / 50;
auto engine = car.engine;
result += engine.horsepower * 40;
if (engine.usesElectric) {
if (engine.usesGas) {
// hybrid
result += 5000;
} else {
result += 3000;
}
}
result += car.hasPowerWindows ? 100 : 0;
result += car.hasPowerSteering ? 200 : 0;
result += car.hasCruiseControl ? 400 : 0;
result += car.hasNavSystem ? 2000 : 0;
result += car.cupHolders * 25;
return result;
}
void randomCar(Car* car) {
// Do not think too hard about realism.
static const char* const MAKES[] = { "Toyota", "GM", "Ford", "Honda", "Tesla" };
static const char* const MODELS[] = { "Camry", "Prius", "Volt", "Accord", "Leaf", "Model S" };
car->make = copyString(MAKES[fastRand(sizeof(MAKES) / sizeof(MAKES[0]))]);
car->model = copyString(MODELS[fastRand(sizeof(MODELS) / sizeof(MODELS[0]))]);
car->color = (Color)fastRand(COLOR_RANGE);
car->seats = 2 + fastRand(6);
car->doors = 2 + fastRand(3);
for (auto& wheel: car->wheels.init(4)) {
wheel.diameter = 25 + fastRand(15);
wheel.airPressure = 30 + fastRandDouble(20);
wheel.snowTires = fastRand(16) == 0;
}
car->length = 170 + fastRand(150);
car->width = 48 + fastRand(36);
car->height = 54 + fastRand(48);
car->weight = car->length * car->width * car->height / 200;
car->engine.horsepower = 100 * fastRand(400);
car->engine.cylinders = 4 + 2 * fastRand(3);
car->engine.cc = 800 + fastRand(10000);
car->fuelCapacity = 10.0 + fastRandDouble(30.0);
car->fuelLevel = fastRandDouble(car->fuelCapacity);
car->hasPowerWindows = fastRand(2);
car->hasPowerSteering = fastRand(2);
car->hasCruiseControl = fastRand(2);
car->cupHolders = fastRand(12);
car->hasNavSystem = fastRand(2);
}
class CarSalesTestCase {
public:
typedef List<Car> Request;
typedef uint64_t Response;
typedef uint64_t Expectation;
static uint64_t setupRequest(List<Car>* request) {
uint64_t result = 0;
for (auto& car: request->init(fastRand(200))) {
randomCar(&car);
result += carValue(car);
}
return result;
}
static void handleRequest(const List<Car>& request, uint64_t* response) {
*response = 0;
for (auto& car: request) {
*response += carValue(car);
}
}
static inline bool checkResponse(uint64_t response, uint64_t expected) {
return response == expected;
}
};
// ======================================================================================= // =======================================================================================
struct SingleUseObjects { struct SingleUseObjects {
...@@ -416,7 +573,6 @@ int main(int argc, char* argv[]) { ...@@ -416,7 +573,6 @@ int main(int argc, char* argv[]) {
} }
uint64_t iters = strtoull(argv[5], nullptr, 0); uint64_t iters = strtoull(argv[5], nullptr, 0);
srand(123);
uint64_t throughput; uint64_t throughput;
...@@ -425,6 +581,8 @@ int main(int argc, char* argv[]) { ...@@ -425,6 +581,8 @@ int main(int argc, char* argv[]) {
throughput = doBenchmark<ExpressionTestCase>(argv[2], iters); throughput = doBenchmark<ExpressionTestCase>(argv[2], iters);
} else if (testcase == "catrank") { } else if (testcase == "catrank") {
throughput = doBenchmark<CatRankTestCase>(argv[2], iters); throughput = doBenchmark<CatRankTestCase>(argv[2], iters);
} else if (testcase == "carsales") {
throughput = doBenchmark<CarSalesTestCase>(argv[2], iters);
} else { } else {
std::cerr << "Unknown test case: " << testcase << std::endl; std::cerr << "Unknown test case: " << testcase << std::endl;
return 1; return 1;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "benchmark.pb.h" #include "benchmark.pb.h"
#include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/io/zero_copy_stream_impl.h>
#include <limits>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
#include <semaphore.h> #include <semaphore.h>
#include <snappy/snappy.h> #include <snappy/snappy.h>
#include <snappy/snappy-sinksource.h> #include <snappy/snappy-sinksource.h>
#include "fast-random.h"
namespace capnproto { namespace capnproto {
namespace benchmark { namespace benchmark {
...@@ -118,20 +120,20 @@ inline int32_t mod(int32_t a, int32_t b) { ...@@ -118,20 +120,20 @@ inline int32_t mod(int32_t a, int32_t b) {
return a % b; return a % b;
} }
int32_t makeExpression(Expression* exp, int depth) { int32_t makeExpression(Expression* exp, uint depth) {
exp->set_op((Operation)(rand() % Operation_MAX + 1)); exp->set_op((Operation)(fastRand(Operation_MAX + 1)));
int32_t left, right; int32_t left, right;
if (rand() % 8 < depth) { if (fastRand(8) < depth) {
left = rand() % 128 + 1; left = fastRand(128) + 1;
exp->set_left_value(left); exp->set_left_value(left);
} else { } else {
left = makeExpression(exp->mutable_left_expression(), depth + 1); left = makeExpression(exp->mutable_left_expression(), depth + 1);
} }
if (rand() % 8 < depth) { if (fastRand(8) < depth) {
right = rand() % 128 + 1; right = fastRand(128) + 1;
exp->set_right_value(right); exp->set_right_value(right);
} else { } else {
right = makeExpression(exp->mutable_right_expression(), depth + 1); right = makeExpression(exp->mutable_right_expression(), depth + 1);
...@@ -209,7 +211,7 @@ public: ...@@ -209,7 +211,7 @@ public:
// The promotion multiplier is large enough that all the results mentioning "cat" but not "dog" // The promotion multiplier is large enough that all the results mentioning "cat" but not "dog"
// should end up at the front ofthe list, which is how we verify the result. // should end up at the front ofthe list, which is how we verify the result.
static const char* WORDS[] = { static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ", "foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud " "plugh ", "xyzzy ", "thud "
}; };
...@@ -232,7 +234,7 @@ public: ...@@ -232,7 +234,7 @@ public:
typedef int Expectation; typedef int Expectation;
static int setupRequest(SearchResultList* request) { static int setupRequest(SearchResultList* request) {
int count = rand() % 1000; int count = fastRand(1000);
int goodCount = 0; int goodCount = 0;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
...@@ -240,29 +242,29 @@ public: ...@@ -240,29 +242,29 @@ public:
result->set_score(1000 - i); result->set_score(1000 - i);
result->set_url("http://example.com/"); result->set_url("http://example.com/");
std::string* url = result->mutable_url(); std::string* url = result->mutable_url();
int urlSize = rand() % 100; int urlSize = fastRand(100);
for (int j = 0; j < urlSize; j++) { for (int j = 0; j < urlSize; j++) {
url->push_back('a' + rand() % 26); url->push_back('a' + fastRand(26));
} }
bool isCat = rand() % 8 == 0; bool isCat = fastRand(8) == 0;
bool isDog = rand() % 8 == 0; bool isDog = fastRand(8) == 0;
goodCount += isCat && !isDog; goodCount += isCat && !isDog;
std::string* snippet = result->mutable_snippet(); std::string* snippet = result->mutable_snippet();
snippet->push_back(' '); snippet->push_back(' ');
int prefix = rand() % 20; int prefix = fastRand(20);
for (int j = 0; j < prefix; j++) { for (int j = 0; j < prefix; j++) {
snippet->append(WORDS[rand() % WORDS_COUNT]); snippet->append(WORDS[fastRand(WORDS_COUNT)]);
} }
if (isCat) snippet->append("cat "); if (isCat) snippet->append("cat ");
if (isDog) snippet->append("dog "); if (isDog) snippet->append("dog ");
int suffix = rand() % 20; int suffix = fastRand(20);
for (int j = 0; j < suffix; j++) { for (int j = 0; j < suffix; j++) {
snippet->append(WORDS[rand() % WORDS_COUNT]); snippet->append(WORDS[fastRand(WORDS_COUNT)]);
} }
} }
...@@ -307,6 +309,113 @@ public: ...@@ -307,6 +309,113 @@ public:
} }
}; };
// =======================================================================================
// Test case: Car Sales
//
// We have a parking lot full of cars and we want to know how much they are worth.
uint64_t carValue(const Car& car) {
// Do not think too hard about realism.
uint64_t result = 0;
result += car.seats() * 200;
result += car.doors() * 350;
for (auto& wheel: car.wheel()) {
result += wheel.diameter() * wheel.diameter();
result += wheel.snow_tires() ? 100 : 0;
}
result += car.length() * car.width() * car.height() / 50;
const Engine& engine = car.engine();
result += engine.horsepower() * 40;
if (engine.uses_electric()) {
if (engine.uses_gas()) {
// hybrid
result += 5000;
} else {
result += 3000;
}
}
result += car.has_power_windows() ? 100 : 0;
result += car.has_power_steering() ? 200 : 0;
result += car.has_cruise_control() ? 400 : 0;
result += car.has_nav_system() ? 2000 : 0;
result += car.cup_holders() * 25;
return result;
}
void randomCar(Car* car) {
// Do not think too hard about realism.
static const char* const MAKES[] = { "Toyota", "GM", "Ford", "Honda", "Tesla" };
static const char* const MODELS[] = { "Camry", "Prius", "Volt", "Accord", "Leaf", "Model S" };
car->set_make(MAKES[fastRand(sizeof(MAKES) / sizeof(MAKES[0]))]);
car->set_model(MODELS[fastRand(sizeof(MODELS) / sizeof(MODELS[0]))]);
car->set_color((Color)fastRand(Color_MAX));
car->set_seats(2 + fastRand(6));
car->set_doors(2 + fastRand(3));
for (uint i = 0; i < 4; i++) {
Wheel* wheel = car->add_wheel();
wheel->set_diameter(25 + fastRand(15));
wheel->set_air_pressure(30 + fastRandDouble(20));
wheel->set_snow_tires(fastRand(16) == 0);
}
car->set_length(170 + fastRand(150));
car->set_width(48 + fastRand(36));
car->set_height(54 + fastRand(48));
car->set_weight(car->length() * car->width() * car->height() / 200);
Engine* engine = car->mutable_engine();
engine->set_horsepower(100 * fastRand(400));
engine->set_cylinders(4 + 2 * fastRand(3));
engine->set_cc(800 + fastRand(10000));
car->set_fuel_capacity(10.0 + fastRandDouble(30.0));
car->set_fuel_level(fastRandDouble(car->fuel_capacity()));
car->set_has_power_windows(fastRand(2));
car->set_has_power_steering(fastRand(2));
car->set_has_cruise_control(fastRand(2));
car->set_cup_holders(fastRand(12));
car->set_has_nav_system(fastRand(2));
}
class CarSalesTestCase {
public:
typedef ParkingLot Request;
typedef TotalValue Response;
typedef uint64_t Expectation;
static uint64_t setupRequest(ParkingLot* request) {
uint count = fastRand(200);
uint64_t result = 0;
for (uint i = 0; i < count; i++) {
Car* car = request->add_car();
randomCar(car);
result += carValue(*car);
}
return result;
}
static void handleRequest(const ParkingLot& request, TotalValue* response) {
uint64_t result = 0;
for (auto& car: request.car()) {
result += carValue(car);
}
response->set_amount(result);
}
static inline bool checkResponse(const TotalValue& response, uint64_t expected) {
return response.amount() == expected;
}
};
// ======================================================================================= // =======================================================================================
struct SingleUseMessages { struct SingleUseMessages {
...@@ -736,7 +845,6 @@ int main(int argc, char* argv[]) { ...@@ -736,7 +845,6 @@ int main(int argc, char* argv[]) {
} }
uint64_t iters = strtoull(argv[5], nullptr, 0); uint64_t iters = strtoull(argv[5], nullptr, 0);
srand(123);
uint64_t throughput; uint64_t throughput;
...@@ -745,6 +853,8 @@ int main(int argc, char* argv[]) { ...@@ -745,6 +853,8 @@ int main(int argc, char* argv[]) {
throughput = doBenchmark3<ExpressionTestCase>(argv[2], argv[3], argv[4], iters); throughput = doBenchmark3<ExpressionTestCase>(argv[2], argv[3], argv[4], iters);
} else if (testcase == "catrank") { } else if (testcase == "catrank") {
throughput = doBenchmark3<CatRankTestCase>(argv[2], argv[3], argv[4], iters); throughput = doBenchmark3<CatRankTestCase>(argv[2], argv[3], argv[4], iters);
} else if (testcase == "carsales") {
throughput = doBenchmark3<CarSalesTestCase>(argv[2], argv[3], argv[4], iters);
} else { } else {
std::cerr << "Unknown test case: " << testcase << std::endl; std::cerr << "Unknown test case: " << testcase << std::endl;
return 1; return 1;
......
...@@ -90,7 +90,8 @@ enum class Product { ...@@ -90,7 +90,8 @@ enum class Product {
enum class TestCase { enum class TestCase {
EVAL, EVAL,
CATRANK CATRANK,
CARSALES
}; };
enum class Mode { enum class Mode {
...@@ -134,6 +135,9 @@ TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse, ...@@ -134,6 +135,9 @@ TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse,
case TestCase::CATRANK: case TestCase::CATRANK:
argv[1] = strdup("catrank"); argv[1] = strdup("catrank");
break; break;
case TestCase::CARSALES:
argv[1] = strdup("carsales");
break;
} }
switch (mode) { switch (mode) {
...@@ -342,6 +346,8 @@ int main(int argc, char* argv[]) { ...@@ -342,6 +346,8 @@ int main(int argc, char* argv[]) {
mode = Mode::BYTES; mode = Mode::BYTES;
} else if (arg == "eval") { } else if (arg == "eval") {
testCase = TestCase::EVAL; testCase = TestCase::EVAL;
} else if (arg == "carsales") {
testCase = TestCase::CARSALES;
} else if (arg == "no-reuse") { } else if (arg == "no-reuse") {
reuse = Reuse::NO; reuse = Reuse::NO;
} else if (arg == "snappy") { } else if (arg == "snappy") {
...@@ -356,6 +362,9 @@ int main(int argc, char* argv[]) { ...@@ -356,6 +362,9 @@ int main(int argc, char* argv[]) {
case TestCase::CATRANK: case TestCase::CATRANK:
iters *= 1000; iters *= 1000;
break; break;
case TestCase::CARSALES:
iters *= 20000;
break;
} }
cout << "Running " << iters << " iterations of "; cout << "Running " << iters << " iterations of ";
...@@ -366,6 +375,9 @@ int main(int argc, char* argv[]) { ...@@ -366,6 +375,9 @@ int main(int argc, char* argv[]) {
case TestCase::CATRANK: case TestCase::CATRANK:
cout << "CatRank"; cout << "CatRank";
break; break;
case TestCase::CARSALES:
cout << "car sales";
break;
} }
cout << " example case with:" << endl; cout << " example case with:" << endl;
...@@ -404,7 +416,6 @@ int main(int argc, char* argv[]) { ...@@ -404,7 +416,6 @@ int main(int argc, char* argv[]) {
break; break;
} }
cout << endl;
reportTableHeader(); reportTableHeader();
TestResult nullCase = runTest( TestResult nullCase = runTest(
...@@ -417,19 +428,19 @@ int main(int argc, char* argv[]) { ...@@ -417,19 +428,19 @@ int main(int argc, char* argv[]) {
Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, reuse, compression, iters).throughput; Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, reuse, compression, iters).throughput;
reportResults("Protobuf pass-by-object", iters, protobufBase); reportResults("Protobuf pass-by-object", iters, protobufBase);
TestResult protobuf = runTest(
Product::PROTOBUF, testCase, mode, reuse, compression, iters);
reportResults("Protobuf end-to-end", iters, protobuf);
TestResult capnpBase = runTest( TestResult capnpBase = runTest(
Product::CAPNPROTO, testCase, Mode::OBJECTS, reuse, compression, iters); Product::CAPNPROTO, testCase, Mode::OBJECTS, reuse, compression, iters);
capnpBase.throughput = runTest( capnpBase.throughput = runTest(
Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, reuse, compression, iters).throughput; Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, reuse, compression, iters).throughput;
reportResults("Cap'n Proto pass-by-object", iters, capnpBase); reportResults("Cap'n Proto pass-by-object", iters, capnpBase);
TestResult protobuf = runTest(
Product::PROTOBUF, testCase, mode, reuse, compression, iters);
reportResults("Protobuf pass-by-I/O", iters, protobuf);
TestResult capnp = runTest( TestResult capnp = runTest(
Product::CAPNPROTO, testCase, mode, reuse, compression, iters); Product::CAPNPROTO, testCase, mode, reuse, compression, iters);
reportResults("Cap'n Proto end-to-end", iters, capnp); reportResults("Cap'n Proto pass-by-I/O", iters, capnp);
cout << endl; cout << endl;
...@@ -438,7 +449,7 @@ int main(int argc, char* argv[]) { ...@@ -438,7 +449,7 @@ int main(int argc, char* argv[]) {
nullCase.throughput, protobufBase.throughput, capnpBase.throughput, iters); nullCase.throughput, protobufBase.throughput, capnpBase.throughput, iters);
reportComparison("object manipulation", reportComparison("object manipulation",
nullCase.time.cpu(), protobufBase.time.cpu(), capnpBase.time.cpu(), iters); nullCase.time.cpu(), protobufBase.time.cpu(), capnpBase.time.cpu(), iters);
reportComparison("I/O overhead", "us", reportComparison("I/O", "us",
(protobuf.time.cpu() - protobufBase.time.cpu()) / 1000.0, (protobuf.time.cpu() - protobufBase.time.cpu()) / 1000.0,
(capnp.time.cpu() - capnpBase.time.cpu()) / 1000.0, iters); (capnp.time.cpu() - capnpBase.time.cpu()) / 1000.0, iters);
......
...@@ -58,3 +58,60 @@ struct SearchResult { ...@@ -58,3 +58,60 @@ struct SearchResult {
score@1: Float64; score@1: Float64;
snippet@2: Text; snippet@2: Text;
} }
# ========================================================================================
struct ParkingLot {
cars@0: List(Car);
}
struct TotalValue {
amount@0: UInt64;
}
struct Car {
make@0: Text;
model@1: Text;
color@2: Color;
seats@3: UInt8;
doors@4: UInt8;
wheels@5: List(Wheel);
length@6: UInt16;
width@7: UInt16;
height@8: UInt16;
weight@9: UInt32;
engine@10: Engine;
fuelCapacity@11: Float32;
fuelLevel@12: Float32;
hasPowerWindows@13: Bool;
hasPowerSteering@14: Bool;
hasCruiseControl@15: Bool;
cupHolders@16: UInt8;
hasNavSystem@17: Bool;
}
enum Color {
black = 0;
white = 1;
red = 2;
green = 3;
blue = 4;
cyan = 5;
magenta = 6;
yellow = 7;
silver = 8;
}
struct Wheel {
diameter@0: UInt16;
airPressure@1: Float32;
snowTires@2: Bool;
}
struct Engine {
horsepower@0: UInt16;
cylinders@1: UInt8;
cc@2: UInt32;
usesGas@3: Bool;
usesElectric@4: Bool;
}
...@@ -56,3 +56,60 @@ message SearchResult { ...@@ -56,3 +56,60 @@ message SearchResult {
optional double score = 2; optional double score = 2;
optional string snippet = 3; optional string snippet = 3;
} }
// =======================================================================================
message ParkingLot {
repeated Car car = 1;
}
message TotalValue {
required uint64 amount = 1;
}
message Car {
optional string make = 1;
optional string model = 2;
optional Color color = 3;
optional uint32 seats = 4;
optional uint32 doors = 5;
repeated Wheel wheel = 6;
optional uint32 length = 7;
optional uint32 width = 8;
optional uint32 height = 9;
optional uint32 weight = 10;
optional Engine engine = 11;
optional float fuel_capacity = 12;
optional float fuel_level = 13;
optional bool has_power_windows = 14;
optional bool has_power_steering = 15;
optional bool has_cruise_control = 16;
optional uint32 cup_holders = 17;
optional bool has_nav_system = 18;
}
enum Color {
BLACK = 0;
WHITE = 1;
RED = 2;
GREEN = 3;
BLUE = 4;
CYAN = 5;
MAGENTA = 6;
YELLOW = 7;
SILVER = 8;
}
message Wheel {
optional uint32 diameter = 1;
optional float air_pressure = 2;
optional bool snow_tires = 3;
}
message Engine {
optional uint32 horsepower = 1;
optional uint32 cylinders = 2;
optional uint32 cc = 3;
optional bool uses_gas = 4;
optional bool uses_electric = 5;
}
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNPROTO_BENCHMARK_FAST_RANDOM_H_
#define CAPNPROTO_BENCHMARK_FAST_RANDOM_H_
namespace capnproto {
namespace benchmark {
static inline uint32_t nextFastRand() {
static constexpr uint32_t A = 1664525;
static constexpr uint32_t C = 1013904223;
static uint32_t state = C;
state = A * state + C;
return state;
}
static inline uint32_t fastRand(uint32_t range) {
return nextFastRand() % range;
}
static inline double fastRandDouble(double range) {
return nextFastRand() * range / std::numeric_limits<uint32_t>::max();
}
} // namespace capnproto
} // namespace benchmark
#endif // CAPNPROTO_BENCHMARK_FAST_RANDOM_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment