capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

common.h (8846B)


      1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #pragma once
     23 
     24 #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
     25 #pragma GCC system_header
     26 #endif
     27 
     28 #include <unistd.h>
     29 #include <limits>
     30 #include <errno.h>
     31 #include <sys/types.h>
     32 #include <sys/time.h>
     33 #include <sys/resource.h>
     34 #include <sys/wait.h>
     35 #include <stdlib.h>
     36 #include <semaphore.h>
     37 #include <algorithm>
     38 #include <stdexcept>
     39 #include <stdio.h>
     40 #include <string.h>
     41 #include <string>
     42 #include <vector>
     43 
     44 namespace capnp {
     45 namespace benchmark {
     46 
     47 // Use a 128-bit Xorshift algorithm.
     48 static inline uint32_t nextFastRand() {
     49   // These values are arbitrary. Any seed other than all zeroes is OK.
     50   static uint32_t x = 0x1d2acd47;
     51   static uint32_t y = 0x58ca3e14;
     52   static uint32_t z = 0xf563f232;
     53   static uint32_t w = 0x0bc76199;
     54 
     55   uint32_t tmp = x ^ (x << 11);
     56   x = y;
     57   y = z;
     58   z = w;
     59   w = w ^ (w >> 19) ^ tmp ^ (tmp >> 8);
     60   return w;
     61 }
     62 
     63 static inline uint32_t fastRand(uint32_t range) {
     64   return nextFastRand() % range;
     65 }
     66 
     67 static inline double fastRandDouble(double range) {
     68   return nextFastRand() * range / std::numeric_limits<uint32_t>::max();
     69 }
     70 
     71 inline int32_t div(int32_t a, int32_t b) {
     72   if (b == 0) return std::numeric_limits<int32_t>::max();
     73   // INT_MIN / -1 => SIGFPE.  Who knew?
     74   if (a == std::numeric_limits<int32_t>::min() && b == -1) {
     75     return std::numeric_limits<int32_t>::max();
     76   }
     77   return a / b;
     78 }
     79 
     80 inline int32_t mod(int32_t a, int32_t b) {
     81   if (b == 0) return std::numeric_limits<int32_t>::max();
     82   // INT_MIN % -1 => SIGFPE.  Who knew?
     83   if (a == std::numeric_limits<int32_t>::min() && b == -1) {
     84     return std::numeric_limits<int32_t>::max();
     85   }
     86   return a % b;
     87 }
     88 
     89 static const char* const WORDS[] = {
     90     "foo ", "bar ", "baz ", "qux ", "quux ", "corge ", "grault ", "garply ", "waldo ", "fred ",
     91     "plugh ", "xyzzy ", "thud "
     92 };
     93 constexpr size_t WORDS_COUNT = sizeof(WORDS) / sizeof(WORDS[0]);
     94 
     95 template <typename T>
     96 class ProducerConsumerQueue {
     97 public:
     98   ProducerConsumerQueue() {
     99     front = new Node;
    100     back = front;
    101     sem_init(&semaphore, 0, 0);
    102   }
    103 
    104   ~ProducerConsumerQueue() noexcept(false) {
    105     while (front != nullptr) {
    106       Node* oldFront = front;
    107       front = front->next;
    108       delete oldFront;
    109     }
    110     sem_destroy(&semaphore);
    111   }
    112 
    113   void post(T t) {
    114     back->next = new Node(t);
    115     back = back->next;
    116     sem_post(&semaphore);
    117   }
    118 
    119   T next() {
    120     sem_wait(&semaphore);
    121     Node* oldFront = front;
    122     front = front->next;
    123     delete oldFront;
    124     return front->value;
    125   }
    126 
    127 private:
    128   struct Node {
    129     T value;
    130     Node* next;
    131 
    132     Node(): next(nullptr) {}
    133     Node(T value): value(value), next(nullptr) {}
    134   };
    135 
    136   Node* front;  // Last node that has been consumed.
    137   Node* back;   // Last node in list.
    138   sem_t semaphore;
    139 };
    140 
    141 // TODO(cleanup):  Use SYSCALL(), get rid of this exception class.
    142 class OsException: public std::exception {
    143 public:
    144   OsException(int error): error(error) {}
    145   ~OsException() noexcept {}
    146 
    147   const char* what() const noexcept override {
    148     return strerror(error);
    149   }
    150 
    151 private:
    152   int error;
    153 };
    154 
    155 static void writeAll(int fd, const void* buffer, size_t size) {
    156   const char* pos = reinterpret_cast<const char*>(buffer);
    157   while (size > 0) {
    158     ssize_t n = write(fd, pos, size);
    159     if (n <= 0) {
    160       throw OsException(errno);
    161     }
    162     pos += n;
    163     size -= n;
    164   }
    165 }
    166 
    167 static void readAll(int fd, void* buffer, size_t size) {
    168   char* pos = reinterpret_cast<char*>(buffer);
    169   while (size > 0) {
    170     ssize_t n = read(fd, pos, size);
    171     if (n <= 0) {
    172       throw OsException(errno);
    173     }
    174     pos += n;
    175     size -= n;
    176   }
    177 }
    178 
    179 template <typename BenchmarkMethods, typename Func>
    180 uint64_t passByPipe(Func&& clientFunc, uint64_t iters) {
    181   int clientToServer[2];
    182   int serverToClient[2];
    183   if (pipe(clientToServer) < 0) throw OsException(errno);
    184   if (pipe(serverToClient) < 0) throw OsException(errno);
    185 
    186   pid_t child = fork();
    187   if (child == 0) {
    188     // Client.
    189     close(clientToServer[0]);
    190     close(serverToClient[1]);
    191 
    192     uint64_t throughput = clientFunc(serverToClient[0], clientToServer[1], iters);
    193     writeAll(clientToServer[1], &throughput, sizeof(throughput));
    194 
    195     exit(0);
    196   } else {
    197     // Server.
    198     close(clientToServer[1]);
    199     close(serverToClient[0]);
    200 
    201     uint64_t throughput = BenchmarkMethods::server(clientToServer[0], serverToClient[1], iters);
    202 
    203     uint64_t clientThroughput = 0;
    204     readAll(clientToServer[0], &clientThroughput, sizeof(clientThroughput));
    205     throughput += clientThroughput;
    206 
    207     int status;
    208     if (waitpid(child, &status, 0) != child) {
    209       throw OsException(errno);
    210     }
    211     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    212       throw std::logic_error("Child exited abnormally.");
    213     }
    214 
    215     return throughput;
    216   }
    217 }
    218 
    219 template <typename BenchmarkTypes, typename TestCase, typename Reuse, typename Compression>
    220 uint64_t doBenchmark(const std::string& mode, uint64_t iters) {
    221   typedef typename BenchmarkTypes::template BenchmarkMethods<TestCase, Reuse, Compression>
    222       BenchmarkMethods;
    223   if (mode == "client") {
    224     return BenchmarkMethods::syncClient(STDIN_FILENO, STDOUT_FILENO, iters);
    225   } else if (mode == "server") {
    226     return BenchmarkMethods::server(STDIN_FILENO, STDOUT_FILENO, iters);
    227   } else if (mode == "object") {
    228     return BenchmarkMethods::passByObject(iters, false);
    229   } else if (mode == "object-size") {
    230     return BenchmarkMethods::passByObject(iters, true);
    231   } else if (mode == "bytes") {
    232     return BenchmarkMethods::passByBytes(iters);
    233   } else if (mode == "pipe") {
    234     return passByPipe<BenchmarkMethods>(BenchmarkMethods::syncClient, iters);
    235   } else if (mode == "pipe-async") {
    236     return passByPipe<BenchmarkMethods>(BenchmarkMethods::asyncClient, iters);
    237   } else {
    238     fprintf(stderr, "Unknown mode: %s\n", mode.c_str());
    239     exit(1);
    240   }
    241 }
    242 
    243 template <typename BenchmarkTypes, typename TestCase, typename Compression>
    244 uint64_t doBenchmark2(const std::string& mode, const std::string& reuse, uint64_t iters) {
    245   if (reuse == "reuse") {
    246     return doBenchmark<
    247         BenchmarkTypes, TestCase, typename BenchmarkTypes::ReusableResources, Compression>(
    248             mode, iters);
    249   } else if (reuse == "no-reuse") {
    250     return doBenchmark<
    251         BenchmarkTypes, TestCase, typename BenchmarkTypes::SingleUseResources, Compression>(
    252             mode, iters);
    253   } else {
    254     fprintf(stderr, "Unknown reuse mode: %s\n", reuse.c_str());
    255     exit(1);
    256   }
    257 }
    258 
    259 template <typename BenchmarkTypes, typename TestCase>
    260 uint64_t doBenchmark3(const std::string& mode, const std::string& reuse,
    261                       const std::string& compression, uint64_t iters) {
    262   if (compression == "none") {
    263     return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Uncompressed>(
    264         mode, reuse, iters);
    265   } else if (compression == "packed") {
    266     return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Packed>(
    267         mode, reuse, iters);
    268 #if HAVE_SNAPPY
    269   } else if (compression == "snappy") {
    270     return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::SnappyCompressed>(
    271         mode, reuse, iters);
    272 #endif  // HAVE_SNAPPY
    273   } else {
    274     fprintf(stderr, "Unknown compression mode: %s\n", compression.c_str());
    275     exit(1);
    276   }
    277 }
    278 
    279 template <typename BenchmarkTypes, typename TestCase>
    280 int benchmarkMain(int argc, char* argv[]) {
    281   if (argc != 5) {
    282     fprintf(stderr, "USAGE:  %s MODE REUSE COMPRESSION ITERATION_COUNT\n", argv[0]);
    283     return 1;
    284   }
    285 
    286   uint64_t iters = strtoull(argv[4], nullptr, 0);
    287   uint64_t throughput = doBenchmark3<BenchmarkTypes, TestCase>(argv[1], argv[2], argv[3], iters);
    288   fprintf(stdout, "%llu\n", (long long unsigned int)throughput);
    289 
    290   return 0;
    291 }
    292 
    293 }  // namespace capnp
    294 }  // namespace benchmark