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