blob: a42c722e476fcea071f7deb41df76627f20b6b7f [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
#pragma GCC system_header
#endif
#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>
#include <string>
#include <vector>
namespace capnp {
namespace benchmark {
// Use a 128-bit Xorshift algorithm.
static inline uint32_t nextFastRand() {
// These values are arbitrary. Any seed other than all zeroes is OK.
static uint32_t x = 0x1d2acd47;
static uint32_t y = 0x58ca3e14;
static uint32_t z = 0xf563f232;
static uint32_t w = 0x0bc76199;
uint32_t tmp = x ^ (x << 11);
x = y;
y = z;
z = w;
w = w ^ (w >> 19) ^ tmp ^ (tmp >> 8);
return w;
}
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() noexcept(false) {
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;
};
// TODO(cleanup): Use SYSCALL(), get rid of this exception class.
class OsException: public std::exception {
public:
OsException(int error): error(error) {}
~OsException() noexcept {}
const char* what() const noexcept override {
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 == "packed") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Packed>(
mode, reuse, iters);
#if HAVE_SNAPPY
} else if (compression == "snappy") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::SnappyCompressed>(
mode, reuse, iters);
#endif // HAVE_SNAPPY
} else {
fprintf(stderr, "Unknown compression mode: %s\n", compression.c_str());
exit(1);
}
}
template <typename BenchmarkTypes, typename TestCase>
int benchmarkMain(int argc, char* argv[]) {
if (argc != 5) {
fprintf(stderr, "USAGE: %s MODE REUSE COMPRESSION ITERATION_COUNT\n", argv[0]);
return 1;
}
uint64_t iters = strtoull(argv[4], nullptr, 0);
uint64_t throughput = doBenchmark3<BenchmarkTypes, TestCase>(argv[1], argv[2], argv[3], iters);
fprintf(stdout, "%llu\n", (long long unsigned int)throughput);
return 0;
}
} // namespace capnp
} // namespace benchmark