Commit 46bf6129 authored by Kenton Varda's avatar Kenton Varda

Factor out benchmark common code.

parent 43c59cc3
......@@ -22,103 +22,17 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "benchmark.capnp.h"
#include <limits>
#include "benchmark-common.h"
#include <capnproto/serialize.h>
#include <capnproto/serialize-snappy.h>
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <iostream>
#include <stdlib.h>
#include <stdexcept>
#include <memory>
#include <thread>
#include <algorithm>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include "fast-random.h"
namespace capnproto {
namespace benchmark {
namespace capnp {
template <typename T>
class ProducerConsumerQueue {
public:
ProducerConsumerQueue() {
front = new Node;
back = front;
sem_init(&semaphore, 0, 0);
}
~ProducerConsumerQueue() {
while (front != nullptr) {
Node* oldFront = front;
front = front->next;
delete oldFront;
}
sem_destroy(&semaphore);
}
void post(T t) {
back->next = new Node(t);
back = back->next;
sem_post(&semaphore);
}
T next() {
sem_wait(&semaphore);
Node* oldFront = front;
front = front->next;
delete oldFront;
return front->value;
}
private:
struct Node {
T value;
Node* next;
Node(): next(nullptr) {}
Node(T value): value(value), next(nullptr) {}
};
Node* front; // Last node that has been consumed.
Node* back; // Last node in list.
sem_t semaphore;
};
class OsException: public std::exception {
public:
OsException(int error): error(error) {}
~OsException() noexcept {}
const char* what() const noexcept override {
// TODO: Use strerror_r or whatever for thread-safety. Ugh.
return strerror(error);
}
private:
int error;
};
// =======================================================================================
inline int32_t div(int32_t a, int32_t b) {
if (b == 0) return INT_MAX;
// INT_MIN / -1 => SIGFPE. Who knew?
if (a == INT_MIN && b == -1) return INT_MAX;
return a / b;
}
inline int32_t mod(int32_t a, int32_t b) {
if (b == 0) return INT_MAX;
// INT_MIN % -1 => SIGFPE. Who knew?
if (a == INT_MIN && b == -1) return INT_MAX;
return a % b;
}
// Test case: Expression evaluation
int32_t makeExpression(Expression::Builder exp, uint depth) {
// TODO: Operation_RANGE or something.
......@@ -216,12 +130,6 @@ public:
// 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.
static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud "
};
constexpr size_t WORDS_COUNT = sizeof(WORDS) / sizeof(WORDS[0]);
struct ScoredResult {
double score;
SearchResult::Reader result;
......@@ -473,10 +381,10 @@ struct SnappyCompressed {
// =======================================================================================
template <typename Compression>
struct NoScratch {
struct ScratchSpace {};
template <typename Compression>
class MessageReader: public Compression::MessageReader {
public:
inline MessageReader(int fd, ScratchSpace& scratch)
......@@ -513,7 +421,6 @@ constexpr size_t SCRATCH_SIZE = 128 * 1024;
word scratchSpace[4 * SCRATCH_SIZE];
int scratchCounter = 0;
template <typename Compression>
struct UseScratch {
struct ScratchSpace {
word* words;
......@@ -527,6 +434,7 @@ struct UseScratch {
}
};
template <typename Compression>
class MessageReader: public Compression::MessageReader {
public:
inline MessageReader(int fd, ScratchSpace& scratch)
......@@ -566,21 +474,56 @@ struct UseScratch {
// =======================================================================================
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
CountingOutputStream output(outputFd);
typename ReuseStrategy::ScratchSpace scratch;
struct BenchmarkMethods {
static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
CountingOutputStream output(outputFd);
typename ReuseStrategy::ScratchSpace scratch;
for (; iters > 0; --iters) {
typename TestCase::Expectation expected;
{
typename ReuseStrategy::MessageBuilder builder(scratch);
expected = TestCase::setupRequest(
builder.template initRoot<typename TestCase::Request>());
Compression::write(output, builder);
}
{
typename ReuseStrategy::template MessageReader<Compression> reader(inputFd, scratch);
if (!TestCase::checkResponse(
reader.template getRoot<typename TestCase::Response>(), expected)) {
throw std::logic_error("Incorrect response.");
}
}
}
for (; iters > 0; --iters) {
typename TestCase::Expectation expected;
{
return output.throughput;
}
static uint64_t asyncClientSender(
int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
CountingOutputStream output(outputFd);
typename ReuseStrategy::ScratchSpace scratch;
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder builder(scratch);
expected = TestCase::setupRequest(
builder.template initRoot<typename TestCase::Request>());
expectations->post(TestCase::setupRequest(
builder.template initRoot<typename TestCase::Request>()));
Compression::write(output, builder);
}
{
typename ReuseStrategy::MessageReader reader(inputFd, scratch);
return output.throughput;
}
static void asyncClientReceiver(
int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
typename ReuseStrategy::ScratchSpace scratch;
for (; iters > 0; --iters) {
typename TestCase::Expectation expected = expectations->next();
typename ReuseStrategy::template MessageReader<Compression> reader(inputFd, scratch);
if (!TestCase::checkResponse(
reader.template getRoot<typename TestCase::Response>(), expected)) {
throw std::logic_error("Incorrect response.");
......@@ -588,253 +531,107 @@ uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
}
}
return output.throughput;
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t asyncClientSender(int outputFd,
ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
CountingOutputStream output(outputFd);
typename ReuseStrategy::ScratchSpace scratch;
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder builder(scratch);
expectations->post(TestCase::setupRequest(
builder.template initRoot<typename TestCase::Request>()));
Compression::write(output, builder);
static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
ProducerConsumerQueue<typename TestCase::Expectation> expectations;
std::thread receiverThread(asyncClientReceiver, inputFd, &expectations, iters);
uint64_t throughput = asyncClientSender(outputFd, &expectations, iters);
receiverThread.join();
return throughput;
}
return output.throughput;
}
static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
CountingOutputStream output(outputFd);
typename ReuseStrategy::ScratchSpace builderScratch;
typename ReuseStrategy::ScratchSpace readerScratch;
template <typename TestCase, typename ReuseStrategy, typename Compression>
void asyncClientReceiver(int inputFd,
ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
typename ReuseStrategy::ScratchSpace scratch;
for (; iters > 0; --iters) {
typename TestCase::Expectation expected = expectations->next();
typename ReuseStrategy::MessageReader reader(inputFd, scratch);
if (!TestCase::checkResponse(
reader.template getRoot<typename TestCase::Response>(), expected)) {
throw std::logic_error("Incorrect response.");
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder builder(builderScratch);
typename ReuseStrategy::template MessageReader<Compression> reader(inputFd, readerScratch);
TestCase::handleRequest(reader.template getRoot<typename TestCase::Request>(),
builder.template initRoot<typename TestCase::Response>());
Compression::write(output, builder);
}
}
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
ProducerConsumerQueue<typename TestCase::Expectation> expectations;
std::thread receiverThread(
asyncClientReceiver<TestCase, ReuseStrategy, Compression>, inputFd, &expectations, iters);
uint64_t throughput =
asyncClientSender<TestCase, ReuseStrategy, Compression>(outputFd, &expectations, iters);
receiverThread.join();
return throughput;
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t server(int inputFd, int outputFd, uint64_t iters) {
CountingOutputStream output(outputFd);
typename ReuseStrategy::ScratchSpace builderScratch;
typename ReuseStrategy::ScratchSpace readerScratch;
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder builder(builderScratch);
typename ReuseStrategy::MessageReader reader(inputFd, readerScratch);
TestCase::handleRequest(reader.template getRoot<typename TestCase::Request>(),
builder.template initRoot<typename TestCase::Response>());
Compression::write(output, builder);
return output.throughput;
}
return output.throughput;
}
static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
typename ReuseStrategy::ScratchSpace requestScratch;
typename ReuseStrategy::ScratchSpace responseScratch;
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t passByObject(uint64_t iters, bool countObjectSize) {
typename ReuseStrategy::ScratchSpace requestScratch;
typename ReuseStrategy::ScratchSpace responseScratch;
typename ReuseStrategy::ObjectSizeCounter counter(iters);
typename ReuseStrategy::ObjectSizeCounter counter(iters);
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder requestMessage(requestScratch);
auto request = requestMessage.template initRoot<typename TestCase::Request>();
typename TestCase::Expectation expected = TestCase::setupRequest(request);
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder requestMessage(requestScratch);
auto request = requestMessage.template initRoot<typename TestCase::Request>();
typename TestCase::Expectation expected = TestCase::setupRequest(request);
typename ReuseStrategy::MessageBuilder responseMessage(responseScratch);
auto response = responseMessage.template initRoot<typename TestCase::Response>();
TestCase::handleRequest(request.asReader(), response);
typename ReuseStrategy::MessageBuilder responseMessage(responseScratch);
auto response = responseMessage.template initRoot<typename TestCase::Response>();
TestCase::handleRequest(request.asReader(), response);
if (!TestCase::checkResponse(response.asReader(), expected)) {
throw std::logic_error("Incorrect response.");
}
if (!TestCase::checkResponse(response.asReader(), expected)) {
throw std::logic_error("Incorrect response.");
if (countObjectSize) {
counter.add(requestMessage, responseMessage);
}
}
if (countObjectSize) {
counter.add(requestMessage, responseMessage);
}
return counter.get();
}
return counter.get();
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t passByBytes(uint64_t iters) {
uint64_t throughput = 0;
typename ReuseStrategy::ScratchSpace requestScratch;
typename ReuseStrategy::ScratchSpace responseScratch;
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder requestBuilder(requestScratch);
typename TestCase::Expectation expected = TestCase::setupRequest(
requestBuilder.template initRoot<typename TestCase::Request>());
Array<word> requestBytes = messageToFlatArray(requestBuilder);
throughput += requestBytes.size() * sizeof(word);
FlatArrayMessageReader requestReader(requestBytes.asPtr());
typename ReuseStrategy::MessageBuilder responseBuilder(responseScratch);
TestCase::handleRequest(requestReader.template getRoot<typename TestCase::Request>(),
responseBuilder.template initRoot<typename TestCase::Response>());
Array<word> responseBytes = messageToFlatArray(responseBuilder);
throughput += responseBytes.size() * sizeof(word);
FlatArrayMessageReader responseReader(responseBytes.asPtr());
if (!TestCase::checkResponse(
responseReader.template getRoot<typename TestCase::Response>(), expected)) {
throw std::logic_error("Incorrect response.");
}
}
static uint64_t passByBytes(uint64_t iters) {
uint64_t throughput = 0;
typename ReuseStrategy::ScratchSpace requestScratch;
typename ReuseStrategy::ScratchSpace responseScratch;
return throughput;
}
for (; iters > 0; --iters) {
typename ReuseStrategy::MessageBuilder requestBuilder(requestScratch);
typename TestCase::Expectation expected = TestCase::setupRequest(
requestBuilder.template initRoot<typename TestCase::Request>());
template <typename TestCase, typename ReuseStrategy, typename Compression, typename Func>
uint64_t passByPipe(Func&& clientFunc, uint64_t iters) {
int clientToServer[2];
int serverToClient[2];
if (pipe(clientToServer) < 0) throw OsException(errno);
if (pipe(serverToClient) < 0) throw OsException(errno);
Array<word> requestBytes = messageToFlatArray(requestBuilder);
throughput += requestBytes.size() * sizeof(word);
FlatArrayMessageReader requestReader(requestBytes.asPtr());
typename ReuseStrategy::MessageBuilder responseBuilder(responseScratch);
TestCase::handleRequest(requestReader.template getRoot<typename TestCase::Request>(),
responseBuilder.template initRoot<typename TestCase::Response>());
pid_t child = fork();
if (child == 0) {
// Client.
close(clientToServer[0]);
close(serverToClient[1]);
uint64_t throughput = clientFunc(serverToClient[0], clientToServer[1], iters);
FdOutputStream(clientToServer[1]).write(&throughput, sizeof(throughput));
exit(0);
} else {
// Server.
close(clientToServer[1]);
close(serverToClient[0]);
uint64_t throughput =
server<TestCase, ReuseStrategy, Compression>(clientToServer[0], serverToClient[1], iters);
uint64_t clientThroughput = 0;
FdInputStream(clientToServer[0]).InputStream::read(&clientThroughput, sizeof(clientThroughput));
throughput += clientThroughput;
int status;
if (waitpid(child, &status, 0) != child) {
throw OsException(errno);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
throw std::logic_error("Child exited abnormally.");
}
Array<word> responseBytes = messageToFlatArray(responseBuilder);
throughput += responseBytes.size() * sizeof(word);
FlatArrayMessageReader responseReader(responseBytes.asPtr());
if (!TestCase::checkResponse(
responseReader.template getRoot<typename TestCase::Response>(), expected)) {
throw std::logic_error("Incorrect response.");
}
}
return throughput;
}
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t doBenchmark(const std::string& mode, uint64_t iters) {
if (mode == "client") {
return syncClient<TestCase, ReuseStrategy, Compression>(
STDIN_FILENO, STDOUT_FILENO, iters);
} else if (mode == "server") {
return server<TestCase, ReuseStrategy, Compression>(
STDIN_FILENO, STDOUT_FILENO, iters);
} else if (mode == "object") {
return passByObject<TestCase, ReuseStrategy, Compression>(iters, false);
} else if (mode == "object-size") {
return passByObject<TestCase, ReuseStrategy, Compression>(iters, true);
} else if (mode == "bytes") {
return passByBytes<TestCase, ReuseStrategy, Compression>(iters);
} else if (mode == "pipe") {
return passByPipe<TestCase, ReuseStrategy, Compression>(
syncClient<TestCase, ReuseStrategy, Compression>, iters);
} else if (mode == "pipe-async") {
return passByPipe<TestCase, ReuseStrategy, Compression>(
asyncClient<TestCase, ReuseStrategy, Compression>, iters);
} else {
std::cerr << "Unknown mode: " << mode << std::endl;
exit(1);
}
}
template <typename TestCase, typename Compression>
uint64_t doBenchmark2(const std::string& mode, const std::string& reuse, uint64_t iters) {
if (reuse == "reuse") {
return doBenchmark<TestCase, UseScratch<Compression>, Compression>(mode, iters);
} else if (reuse == "no-reuse") {
return doBenchmark<TestCase, NoScratch<Compression>, Compression>(mode, iters);
} else {
std::cerr << "Unknown reuse mode: " << reuse << std::endl;
exit(1);
}
}
template <typename TestCase>
uint64_t doBenchmark3(const std::string& mode, const std::string& reuse,
const std::string& compression, uint64_t iters) {
if (compression == "none") {
return doBenchmark2<TestCase, Uncompressed>(mode, reuse, iters);
} else if (compression == "snappy") {
return doBenchmark2<TestCase, SnappyCompressed>(mode, reuse, iters);
} else {
std::cerr << "Unknown compression mode: " << compression << std::endl;
exit(1);
}
}
int main(int argc, char* argv[]) {
if (argc != 6) {
std::cerr << "USAGE: " << argv[0]
<< " TEST_CASE MODE REUSE COMPRESSION ITERATION_COUNT" << std::endl;
return 1;
}
uint64_t iters = strtoull(argv[5], nullptr, 0);
};
uint64_t throughput;
struct BenchmarkTypes {
typedef capnp::ExpressionTestCase ExpressionTestCase;
typedef capnp::CatRankTestCase CatRankTestCase;
typedef capnp::CarSalesTestCase CarSalesTestCase;
std::string testcase = argv[1];
if (testcase == "eval") {
throughput = doBenchmark3<ExpressionTestCase>(argv[2], argv[3], argv[4], iters);
} else if (testcase == "catrank") {
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 {
std::cerr << "Unknown test case: " << testcase << std::endl;
return 1;
}
typedef capnp::SnappyCompressed SnappyCompressed;
typedef capnp::Uncompressed Uncompressed;
std::cout << throughput << std::endl;
typedef capnp::UseScratch ReusableResources;
typedef capnp::NoScratch SingleUseResources;
return 0;
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
struct BenchmarkMethods: public capnp::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
};
} // namespace protobuf
} // namespace capnp
} // namespace benchmark
} // namespace capnproto
int main(int argc, char* argv[]) {
return capnproto::benchmark::capnp::main(argc, argv);
return capnproto::benchmark::benchmarkMain<
capnproto::benchmark::capnp::BenchmarkTypes>(argc, argv);
}
// 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_BENCHMARK_COMMON_H_
#define CAPNPROTO_BENCHMARK_BENCHMARK_COMMON_H_
#include <unistd.h>
#include <limits>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <semaphore.h>
#include <algorithm>
#include <stdexcept>
#include <stdio.h>
#include <string.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();
}
inline int32_t div(int32_t a, int32_t b) {
if (b == 0) return std::numeric_limits<int32_t>::max();
// INT_MIN / -1 => SIGFPE. Who knew?
if (a == std::numeric_limits<int32_t>::min() && b == -1) {
return std::numeric_limits<int32_t>::max();
}
return a / b;
}
inline int32_t mod(int32_t a, int32_t b) {
if (b == 0) return std::numeric_limits<int32_t>::max();
// INT_MIN % -1 => SIGFPE. Who knew?
if (a == std::numeric_limits<int32_t>::min() && b == -1) {
return std::numeric_limits<int32_t>::max();
}
return a % b;
}
static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud "
};
constexpr size_t WORDS_COUNT = sizeof(WORDS) / sizeof(WORDS[0]);
template <typename T>
class ProducerConsumerQueue {
public:
ProducerConsumerQueue() {
front = new Node;
back = front;
sem_init(&semaphore, 0, 0);
}
~ProducerConsumerQueue() {
while (front != nullptr) {
Node* oldFront = front;
front = front->next;
delete oldFront;
}
sem_destroy(&semaphore);
}
void post(T t) {
back->next = new Node(t);
back = back->next;
sem_post(&semaphore);
}
T next() {
sem_wait(&semaphore);
Node* oldFront = front;
front = front->next;
delete oldFront;
return front->value;
}
private:
struct Node {
T value;
Node* next;
Node(): next(nullptr) {}
Node(T value): value(value), next(nullptr) {}
};
Node* front; // Last node that has been consumed.
Node* back; // Last node in list.
sem_t semaphore;
};
class OsException: public std::exception {
public:
OsException(int error): error(error) {}
~OsException() noexcept {}
const char* what() const noexcept override {
// TODO: Use strerror_r or whatever for thread-safety. Ugh.
return strerror(error);
}
private:
int error;
};
static void writeAll(int fd, const void* buffer, size_t size) {
const char* pos = reinterpret_cast<const char*>(buffer);
while (size > 0) {
ssize_t n = write(fd, pos, size);
if (n <= 0) {
throw OsException(errno);
}
pos += n;
size -= n;
}
}
static void readAll(int fd, void* buffer, size_t size) {
char* pos = reinterpret_cast<char*>(buffer);
while (size > 0) {
ssize_t n = read(fd, pos, size);
if (n <= 0) {
throw OsException(errno);
}
pos += n;
size -= n;
}
}
template <typename BenchmarkMethods, typename Func>
uint64_t passByPipe(Func&& clientFunc, uint64_t iters) {
int clientToServer[2];
int serverToClient[2];
if (pipe(clientToServer) < 0) throw OsException(errno);
if (pipe(serverToClient) < 0) throw OsException(errno);
pid_t child = fork();
if (child == 0) {
// Client.
close(clientToServer[0]);
close(serverToClient[1]);
uint64_t throughput = clientFunc(serverToClient[0], clientToServer[1], iters);
writeAll(clientToServer[1], &throughput, sizeof(throughput));
exit(0);
} else {
// Server.
close(clientToServer[1]);
close(serverToClient[0]);
uint64_t throughput = BenchmarkMethods::server(clientToServer[0], serverToClient[1], iters);
uint64_t clientThroughput = 0;
readAll(clientToServer[0], &clientThroughput, sizeof(clientThroughput));
throughput += clientThroughput;
int status;
if (waitpid(child, &status, 0) != child) {
throw OsException(errno);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
throw std::logic_error("Child exited abnormally.");
}
return throughput;
}
}
template <typename BenchmarkTypes, typename TestCase, typename Reuse, typename Compression>
uint64_t doBenchmark(const std::string& mode, uint64_t iters) {
typedef typename BenchmarkTypes::template BenchmarkMethods<TestCase, Reuse, Compression>
BenchmarkMethods;
if (mode == "client") {
return BenchmarkMethods::syncClient(STDIN_FILENO, STDOUT_FILENO, iters);
} else if (mode == "server") {
return BenchmarkMethods::server(STDIN_FILENO, STDOUT_FILENO, iters);
} else if (mode == "object") {
return BenchmarkMethods::passByObject(iters, false);
} else if (mode == "object-size") {
return BenchmarkMethods::passByObject(iters, true);
} else if (mode == "bytes") {
return BenchmarkMethods::passByBytes(iters);
} else if (mode == "pipe") {
return passByPipe<BenchmarkMethods>(BenchmarkMethods::syncClient, iters);
} else if (mode == "pipe-async") {
return passByPipe<BenchmarkMethods>(BenchmarkMethods::asyncClient, iters);
} else {
fprintf(stderr, "Unknown mode: %s\n", mode.c_str());
exit(1);
}
}
template <typename BenchmarkTypes, typename TestCase, typename Compression>
uint64_t doBenchmark2(const std::string& mode, const std::string& reuse, uint64_t iters) {
if (reuse == "reuse") {
return doBenchmark<
BenchmarkTypes, TestCase, typename BenchmarkTypes::ReusableResources, Compression>(
mode, iters);
} else if (reuse == "no-reuse") {
return doBenchmark<
BenchmarkTypes, TestCase, typename BenchmarkTypes::SingleUseResources, Compression>(
mode, iters);
} else {
fprintf(stderr, "Unknown reuse mode: %s\n", reuse.c_str());
exit(1);
}
}
template <typename BenchmarkTypes, typename TestCase>
uint64_t doBenchmark3(const std::string& mode, const std::string& reuse,
const std::string& compression, uint64_t iters) {
if (compression == "none") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Uncompressed>(
mode, reuse, iters);
} else if (compression == "snappy") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::SnappyCompressed>(
mode, reuse, iters);
} else {
fprintf(stderr, "Unknown compression mode: %s\n", compression.c_str());
exit(1);
}
}
template <typename BenchmarkTypes>
int benchmarkMain(int argc, char* argv[]) {
if (argc != 6) {
fprintf(stderr, "USAGE: %s TEST_CASE MODE REUSE COMPRESSION ITERATION_COUNT\n", argv[0]);
return 1;
}
uint64_t iters = strtoull(argv[5], nullptr, 0);
uint64_t throughput;
std::string testcase = argv[1];
if (testcase == "eval") {
throughput = doBenchmark3<BenchmarkTypes, typename BenchmarkTypes::ExpressionTestCase>(
argv[2], argv[3], argv[4], iters);
} else if (testcase == "catrank") {
throughput = doBenchmark3<BenchmarkTypes, typename BenchmarkTypes::CatRankTestCase>(
argv[2], argv[3], argv[4], iters);
} else if (testcase == "carsales") {
throughput = doBenchmark3<BenchmarkTypes, typename BenchmarkTypes::CarSalesTestCase>(
argv[2], argv[3], argv[4], iters);
} else {
fprintf(stderr, "Unknown test case: %s\n", testcase.c_str());
return 1;
}
fprintf(stdout, "%llu", (long long unsigned int)throughput);
return 0;
}
} // namespace capnproto
} // namespace benchmark
#endif // CAPNPROTO_BENCHMARK_BENCHMARK_COMMON_H_
......@@ -21,17 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <inttypes.h>
#include <iostream>
#include <string>
#include <stddef.h>
#include <limits.h>
#include <memory>
#include <stdexcept>
#include <algorithm>
#include <string.h>
#include <limits>
#include "fast-random.h"
#include "benchmark-common.h"
namespace capnproto {
namespace benchmark {
......@@ -85,20 +75,6 @@ struct Expression {
};
};
inline int32_t div(int32_t a, int32_t b) {
if (b == 0) return INT_MAX;
// INT_MIN / -1 => SIGFPE. Who knew?
if (a == INT_MIN && b == -1) return INT_MAX;
return a / b;
}
inline int32_t mod(int32_t a, int32_t b) {
if (b == 0) return INT_MAX;
// INT_MIN % -1 => SIGFPE. Who knew?
if (a == INT_MIN && b == -1) return INT_MAX;
return a % b;
}
int32_t makeExpression(Expression* exp, uint depth) {
exp->op = (Operation)(fastRand(OPERATION_RANGE));
......@@ -202,12 +178,6 @@ public:
// 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.
static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud "
};
constexpr size_t WORDS_COUNT = sizeof(WORDS) / sizeof(WORDS[0]);
template <typename T>
struct List {
size_t size;
......@@ -533,70 +503,84 @@ struct ReusableObjects {
// =======================================================================================
template <typename TestCase>
uint64_t passByObject(uint64_t iters) {
uint64_t throughput = 0;
for (; iters > 0; --iters) {
arenaPos = arena;
typename TestCase::Request request;
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
typename TestCase::Response response;
TestCase::handleRequest(request, &response);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
struct BenchmarkMethods {
static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
exit(1);
}
throughput += (arenaPos - arena) * sizeof(arena[0]);
static uint64_t asyncClientSender(
int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
exit(1);
}
return throughput;
}
static void asyncClientReceiver(
int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
exit(1);
}
template <typename TestCase>
uint64_t doBenchmark(const std::string& mode, uint64_t iters) {
if (mode == "object") {
return passByObject<TestCase>(iters);
} else {
std::cerr << "Unknown mode: " << mode << std::endl;
static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
exit(1);
}
}
int main(int argc, char* argv[]) {
if (argc != 6) {
std::cerr << "USAGE: " << argv[0]
<< " TEST_CASE MODE REUSE COMPRESSION ITERATION_COUNT" << std::endl;
return 1;
static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
exit(1);
}
uint64_t iters = strtoull(argv[5], nullptr, 0);
static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
uint64_t throughput = 0;
uint64_t throughput;
for (; iters > 0; --iters) {
arenaPos = arena;
std::string testcase = argv[1];
if (testcase == "eval") {
throughput = doBenchmark<ExpressionTestCase>(argv[2], iters);
} else if (testcase == "catrank") {
throughput = doBenchmark<CatRankTestCase>(argv[2], iters);
} else if (testcase == "carsales") {
throughput = doBenchmark<CarSalesTestCase>(argv[2], iters);
} else {
std::cerr << "Unknown test case: " << testcase << std::endl;
return 1;
typename TestCase::Request request;
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
typename TestCase::Response response;
TestCase::handleRequest(request, &response);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
}
throughput += (arenaPos - arena) * sizeof(arena[0]);
}
return throughput;
}
std::cout << throughput << std::endl;
static uint64_t passByBytes(uint64_t iters) {
fprintf(stderr, "Null benchmark doesn't do I/O.\n");
exit(1);
}
};
return 0;
}
struct BenchmarkTypes {
typedef null::ExpressionTestCase ExpressionTestCase;
typedef null::CatRankTestCase CatRankTestCase;
typedef null::CarSalesTestCase CarSalesTestCase;
typedef void SnappyCompressed;
typedef void Uncompressed;
typedef void ReusableResources;
typedef void SingleUseResources;
template <typename TestCase, typename ReuseStrategy, typename Compression>
struct BenchmarkMethods: public null::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
};
} // namespace null
} // namespace benchmark
} // namespace capnproto
int main(int argc, char* argv[]) {
return capnproto::benchmark::null::main(argc, argv);
return capnproto::benchmark::benchmarkMain<
capnproto::benchmark::null::BenchmarkTypes>(argc, argv);
}
......@@ -22,103 +22,18 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "benchmark.pb.h"
#include "benchmark-common.h"
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <limits>
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <iostream>
#include <stdlib.h>
#include <stdexcept>
#include <memory>
#include <thread>
#include <algorithm>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <snappy/snappy.h>
#include <snappy/snappy-sinksource.h>
#include "fast-random.h"
namespace capnproto {
namespace benchmark {
namespace protobuf {
template <typename T>
class ProducerConsumerQueue {
public:
ProducerConsumerQueue() {
front = new Node;
back = front;
sem_init(&semaphore, 0, 0);
}
~ProducerConsumerQueue() {
while (front != nullptr) {
Node* oldFront = front;
front = front->next;
delete oldFront;
}
sem_destroy(&semaphore);
}
void post(T t) {
back->next = new Node(t);
back = back->next;
sem_post(&semaphore);
}
T next() {
sem_wait(&semaphore);
Node* oldFront = front;
front = front->next;
delete oldFront;
return front->value;
}
private:
struct Node {
T value;
Node* next;
Node(): next(nullptr) {}
Node(T value): value(value), next(nullptr) {}
};
Node* front; // Last node that has been consumed.
Node* back; // Last node in list.
sem_t semaphore;
};
class OsException: public std::exception {
public:
OsException(int error): error(error) {}
~OsException() noexcept {}
const char* what() const noexcept override {
// TODO: Use strerror_r or whatever for thread-safety. Ugh.
return strerror(error);
}
private:
int error;
};
// =======================================================================================
inline int32_t div(int32_t a, int32_t b) {
if (b == 0) return INT_MAX;
// INT_MIN / -1 => SIGFPE. Who knew?
if (a == INT_MIN && b == -1) return INT_MAX;
return a / b;
}
inline int32_t mod(int32_t a, int32_t b) {
if (b == 0) return INT_MAX;
// INT_MIN % -1 => SIGFPE. Who knew?
if (a == INT_MIN && b == -1) return INT_MAX;
return a % b;
}
// Test case: Expression evaluation
int32_t makeExpression(Expression* exp, uint depth) {
exp->set_op((Operation)(fastRand(Operation_MAX + 1)));
......@@ -211,12 +126,6 @@ public:
// 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.
static const char* const WORDS[] = {
"foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
"plugh ", "xyzzy ", "thud "
};
constexpr size_t WORDS_COUNT = sizeof(WORDS) / sizeof(WORDS[0]);
struct ScoredResult {
double score;
const SearchResult* result;
......@@ -506,26 +415,6 @@ struct Uncompressed {
// arrays in some static scratch space. This probably gives protobufs an edge that it doesn't
// deserve.
void writeAll(int fd, const void* buffer, size_t size) {
const char* pos = reinterpret_cast<const char*>(buffer);
while (size > 0) {
ssize_t n = write(fd, pos, size);
GOOGLE_CHECK_GT(n, 0);
pos += n;
size -= n;
}
}
void readAll(int fd, void* buffer, size_t size) {
char* pos = reinterpret_cast<char*>(buffer);
while (size > 0) {
ssize_t n = read(fd, pos, size);
GOOGLE_CHECK_GT(n, 0);
pos += n;
size -= n;
}
}
static char scratch[1 << 20];
static char scratch2[1 << 20];
......@@ -571,304 +460,195 @@ struct SnappyCompressed {
typename ReuseStrategy::template Message<typename TestCase::type>::SingleUse
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
uint64_t throughput = 0;
typename Compression::OutputStream output(outputFd);
typename Compression::InputStream input(inputFd);
REUSABLE(Request) reusableRequest;
REUSABLE(Response) reusableResponse;
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
throughput += Compression::write(request, &output);
Compression::flush(&output);
ReuseStrategy::doneWith(request);
SINGLE_USE(Response) response(reusableResponse);
Compression::read(&input, &response);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
struct BenchmarkMethods {
static uint64_t syncClient(int inputFd, int outputFd, uint64_t iters) {
uint64_t throughput = 0;
typename Compression::OutputStream output(outputFd);
typename Compression::InputStream input(inputFd);
REUSABLE(Request) reusableRequest;
REUSABLE(Response) reusableResponse;
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
throughput += Compression::write(request, &output);
Compression::flush(&output);
ReuseStrategy::doneWith(request);
SINGLE_USE(Response) response(reusableResponse);
Compression::read(&input, &response);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
}
ReuseStrategy::doneWith(response);
}
ReuseStrategy::doneWith(response);
return throughput;
}
return throughput;
}
static uint64_t asyncClientSender(
int outputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
uint64_t throughput = 0;
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t asyncClientSender(int outputFd,
ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
uint64_t throughput = 0;
typename Compression::OutputStream output(outputFd);
REUSABLE(Request) reusableRequest;
typename Compression::OutputStream output(outputFd);
REUSABLE(Request) reusableRequest;
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
expectations->post(TestCase::setupRequest(&request));
throughput += Compression::write(request, &output);
Compression::flush(&output);
ReuseStrategy::doneWith(request);
}
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
expectations->post(TestCase::setupRequest(&request));
throughput += Compression::write(request, &output);
Compression::flush(&output);
ReuseStrategy::doneWith(request);
return throughput;
}
return throughput;
}
static void asyncClientReceiver(
int inputFd, ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
typename Compression::InputStream input(inputFd);
REUSABLE(Response) reusableResponse;
template <typename TestCase, typename ReuseStrategy, typename Compression>
void asyncClientReceiver(int inputFd,
ProducerConsumerQueue<typename TestCase::Expectation>* expectations,
uint64_t iters) {
typename Compression::InputStream input(inputFd);
REUSABLE(Response) reusableResponse;
for (; iters > 0; --iters) {
typename TestCase::Expectation expected = expectations->next();
SINGLE_USE(Response) response(reusableResponse);
Compression::read(&input, &response);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
for (; iters > 0; --iters) {
typename TestCase::Expectation expected = expectations->next();
SINGLE_USE(Response) response(reusableResponse);
Compression::read(&input, &response);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
}
ReuseStrategy::doneWith(response);
}
ReuseStrategy::doneWith(response);
}
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
ProducerConsumerQueue<typename TestCase::Expectation> expectations;
std::thread receiverThread(
asyncClientReceiver<TestCase, ReuseStrategy, Compression>, inputFd, &expectations, iters);
uint64_t throughput =
asyncClientSender<TestCase, ReuseStrategy, Compression>(outputFd, &expectations, iters);
receiverThread.join();
return throughput;
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t server(int inputFd, int outputFd, uint64_t iters) {
uint64_t throughput = 0;
typename Compression::OutputStream output(outputFd);
typename Compression::InputStream input(inputFd);
static uint64_t asyncClient(int inputFd, int outputFd, uint64_t iters) {
ProducerConsumerQueue<typename TestCase::Expectation> expectations;
std::thread receiverThread(asyncClientReceiver, inputFd, &expectations, iters);
uint64_t throughput = asyncClientSender(outputFd, &expectations, iters);
receiverThread.join();
REUSABLE(Request) reusableRequest;
REUSABLE(Response) reusableResponse;
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
Compression::read(&input, &request);
SINGLE_USE(Response) response(reusableResponse);
TestCase::handleRequest(request, &response);
ReuseStrategy::doneWith(request);
throughput += Compression::write(response, &output);
Compression::flush(&output);
ReuseStrategy::doneWith(response);
return throughput;
}
return throughput;
}
static uint64_t server(int inputFd, int outputFd, uint64_t iters) {
uint64_t throughput = 0;
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t passByObject(uint64_t iters, bool countObjectSize) {
uint64_t throughput = 0;
typename Compression::OutputStream output(outputFd);
typename Compression::InputStream input(inputFd);
REUSABLE(Request) reusableRequest;
REUSABLE(Response) reusableResponse;
REUSABLE(Request) reusableRequest;
REUSABLE(Response) reusableResponse;
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
Compression::read(&input, &request);
SINGLE_USE(Response) response(reusableResponse);
TestCase::handleRequest(request, &response);
ReuseStrategy::doneWith(request);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
}
ReuseStrategy::doneWith(response);
SINGLE_USE(Response) response(reusableResponse);
TestCase::handleRequest(request, &response);
ReuseStrategy::doneWith(request);
if (countObjectSize) {
throughput += request.SpaceUsed();
throughput += response.SpaceUsed();
throughput += Compression::write(response, &output);
Compression::flush(&output);
ReuseStrategy::doneWith(response);
}
}
return throughput;
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t passByBytes(uint64_t iters) {
uint64_t throughput = 0;
REUSABLE(Request) reusableClientRequest;
REUSABLE(Request) reusableServerRequest;
REUSABLE(Response) reusableServerResponse;
REUSABLE(Response) reusableClientResponse;
typename ReuseStrategy::ReusableString reusableRequestString, reusableResponseString;
for (; iters > 0; --iters) {
SINGLE_USE(Request) clientRequest(reusableClientRequest);
typename TestCase::Expectation expected = TestCase::setupRequest(&clientRequest);
typename ReuseStrategy::SingleUseString requestString(reusableRequestString);
clientRequest.SerializePartialToString(&requestString);
throughput += requestString.size();
ReuseStrategy::doneWith(clientRequest);
return throughput;
}
SINGLE_USE(Request) serverRequest(reusableServerRequest);
serverRequest.ParsePartialFromString(requestString);
static uint64_t passByObject(uint64_t iters, bool countObjectSize) {
uint64_t throughput = 0;
SINGLE_USE(Response) serverResponse(reusableServerResponse);
TestCase::handleRequest(serverRequest, &serverResponse);
ReuseStrategy::doneWith(serverRequest);
REUSABLE(Request) reusableRequest;
REUSABLE(Response) reusableResponse;
typename ReuseStrategy::SingleUseString responseString(reusableResponseString);
serverResponse.SerializePartialToString(&responseString);
throughput += responseString.size();
ReuseStrategy::doneWith(serverResponse);
for (; iters > 0; --iters) {
SINGLE_USE(Request) request(reusableRequest);
typename TestCase::Expectation expected = TestCase::setupRequest(&request);
SINGLE_USE(Response) clientResponse(reusableClientResponse);
clientResponse.ParsePartialFromString(responseString);
SINGLE_USE(Response) response(reusableResponse);
TestCase::handleRequest(request, &response);
ReuseStrategy::doneWith(request);
if (!TestCase::checkResponse(response, expected)) {
throw std::logic_error("Incorrect response.");
}
ReuseStrategy::doneWith(response);
if (!TestCase::checkResponse(clientResponse, expected)) {
throw std::logic_error("Incorrect response.");
if (countObjectSize) {
throughput += request.SpaceUsed();
throughput += response.SpaceUsed();
}
}
ReuseStrategy::doneWith(clientResponse);
return throughput;
}
return throughput;
}
static uint64_t passByBytes(uint64_t iters) {
uint64_t throughput = 0;
template <typename TestCase, typename ReuseStrategy, typename Compression, typename Func>
uint64_t passByPipe(Func&& clientFunc, uint64_t iters) {
int clientToServer[2];
int serverToClient[2];
if (pipe(clientToServer) < 0) throw OsException(errno);
if (pipe(serverToClient) < 0) throw OsException(errno);
REUSABLE(Request) reusableClientRequest;
REUSABLE(Request) reusableServerRequest;
REUSABLE(Response) reusableServerResponse;
REUSABLE(Response) reusableClientResponse;
typename ReuseStrategy::ReusableString reusableRequestString, reusableResponseString;
pid_t child = fork();
if (child == 0) {
// Client.
close(clientToServer[0]);
close(serverToClient[1]);
for (; iters > 0; --iters) {
SINGLE_USE(Request) clientRequest(reusableClientRequest);
typename TestCase::Expectation expected = TestCase::setupRequest(&clientRequest);
uint64_t throughput = clientFunc(serverToClient[0], clientToServer[1], iters);
writeAll(clientToServer[1], &throughput, sizeof(throughput));
typename ReuseStrategy::SingleUseString requestString(reusableRequestString);
clientRequest.SerializePartialToString(&requestString);
throughput += requestString.size();
ReuseStrategy::doneWith(clientRequest);
exit(0);
} else {
// Server.
close(clientToServer[1]);
close(serverToClient[0]);
SINGLE_USE(Request) serverRequest(reusableServerRequest);
serverRequest.ParsePartialFromString(requestString);
uint64_t throughput =
server<TestCase, ReuseStrategy, Compression>(clientToServer[0], serverToClient[1], iters);
SINGLE_USE(Response) serverResponse(reusableServerResponse);
TestCase::handleRequest(serverRequest, &serverResponse);
ReuseStrategy::doneWith(serverRequest);
uint64_t clientThroughput = 0;
readAll(clientToServer[0], &clientThroughput, sizeof(clientThroughput));
throughput += clientThroughput;
typename ReuseStrategy::SingleUseString responseString(reusableResponseString);
serverResponse.SerializePartialToString(&responseString);
throughput += responseString.size();
ReuseStrategy::doneWith(serverResponse);
int status;
if (waitpid(child, &status, 0) != child) {
throw OsException(errno);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
throw std::logic_error("Child exited abnormally.");
SINGLE_USE(Response) clientResponse(reusableClientResponse);
clientResponse.ParsePartialFromString(responseString);
if (!TestCase::checkResponse(clientResponse, expected)) {
throw std::logic_error("Incorrect response.");
}
ReuseStrategy::doneWith(clientResponse);
}
return throughput;
}
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
uint64_t doBenchmark(const std::string& mode, uint64_t iters) {
if (mode == "client") {
return syncClient<TestCase, ReuseStrategy, Compression>(
STDIN_FILENO, STDOUT_FILENO, iters);
} else if (mode == "server") {
return server<TestCase, ReuseStrategy, Compression>(
STDIN_FILENO, STDOUT_FILENO, iters);
} else if (mode == "object") {
return passByObject<TestCase, ReuseStrategy, Compression>(iters, false);
} else if (mode == "object-size") {
return passByObject<TestCase, ReuseStrategy, Compression>(iters, true);
} else if (mode == "bytes") {
return passByBytes<TestCase, ReuseStrategy, Compression>(iters);
} else if (mode == "pipe") {
return passByPipe<TestCase, ReuseStrategy, Compression>(
syncClient<TestCase, ReuseStrategy, Compression>, iters);
} else if (mode == "pipe-async") {
return passByPipe<TestCase, ReuseStrategy, Compression>(
asyncClient<TestCase, ReuseStrategy, Compression>, iters);
} else {
std::cerr << "Unknown mode: " << mode << std::endl;
exit(1);
}
}
template <typename TestCase, typename Compression>
uint64_t doBenchmark2(const std::string& mode, const std::string& reuse, uint64_t iters) {
if (reuse == "reuse") {
return doBenchmark<TestCase, ReusableMessages, Compression>(mode, iters);
} else if (reuse == "no-reuse") {
return doBenchmark<TestCase, SingleUseMessages, Compression>(mode, iters);
} else {
std::cerr << "Unknown reuse mode: " << reuse << std::endl;
exit(1);
}
}
template <typename TestCase>
uint64_t doBenchmark3(const std::string& mode, const std::string& reuse,
const std::string& compression, uint64_t iters) {
if (compression == "none") {
return doBenchmark2<TestCase, Uncompressed>(mode, reuse, iters);
} else if (compression == "snappy") {
return doBenchmark2<TestCase, SnappyCompressed>(mode, reuse, iters);
} else {
std::cerr << "Unknown compression mode: " << compression << std::endl;
exit(1);
}
}
int main(int argc, char* argv[]) {
if (argc != 6) {
std::cerr << "USAGE: " << argv[0]
<< " TEST_CASE MODE REUSE COMPRESSION ITERATION_COUNT" << std::endl;
return 1;
}
uint64_t iters = strtoull(argv[5], nullptr, 0);
};
uint64_t throughput;
struct BenchmarkTypes {
typedef protobuf::ExpressionTestCase ExpressionTestCase;
typedef protobuf::CatRankTestCase CatRankTestCase;
typedef protobuf::CarSalesTestCase CarSalesTestCase;
std::string testcase = argv[1];
if (testcase == "eval") {
throughput = doBenchmark3<ExpressionTestCase>(argv[2], argv[3], argv[4], iters);
} else if (testcase == "catrank") {
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 {
std::cerr << "Unknown test case: " << testcase << std::endl;
return 1;
}
typedef protobuf::SnappyCompressed SnappyCompressed;
typedef protobuf::Uncompressed Uncompressed;
std::cout << throughput << std::endl;
typedef protobuf::ReusableMessages ReusableResources;
typedef protobuf::SingleUseMessages SingleUseResources;
return 0;
}
template <typename TestCase, typename ReuseStrategy, typename Compression>
struct BenchmarkMethods
: public protobuf::BenchmarkMethods<TestCase, ReuseStrategy, Compression> {};
};
} // namespace protobuf
} // namespace benchmark
} // namespace capnproto
int main(int argc, char* argv[]) {
return capnproto::benchmark::protobuf::main(argc, argv);
return capnproto::benchmark::benchmarkMain<
capnproto::benchmark::protobuf::BenchmarkTypes>(argc, argv);
}
// 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