runner.c++ (21363B)
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 #include <sys/types.h> 23 #include <sys/time.h> 24 #include <sys/resource.h> 25 #include <sys/wait.h> 26 #include <sys/stat.h> 27 #include <inttypes.h> 28 #include <string> 29 #include <stdio.h> 30 #include <unistd.h> 31 #include <string.h> 32 #include <iostream> 33 #include <iomanip> 34 35 using namespace std; 36 37 namespace capnp { 38 namespace benchmark { 39 namespace runner { 40 41 struct Times { 42 uint64_t real; 43 uint64_t user; 44 uint64_t sys; 45 46 uint64_t cpu() { return user + sys; } 47 48 Times operator-(const Times& other) { 49 Times result; 50 result.real = real - other.real; 51 result.user = user - other.user; 52 result.sys = sys - other.sys; 53 return result; 54 } 55 }; 56 57 uint64_t asNanosecs(const struct timeval& tv) { 58 return (uint64_t)tv.tv_sec * 1000000000 + (uint64_t)tv.tv_usec * 1000; 59 } 60 61 Times currentTimes() { 62 Times result; 63 64 struct rusage self, children; 65 getrusage(RUSAGE_SELF, &self); 66 getrusage(RUSAGE_CHILDREN, &children); 67 68 struct timeval real; 69 gettimeofday(&real, nullptr); 70 71 result.real = asNanosecs(real); 72 result.user = asNanosecs(self.ru_utime) + asNanosecs(children.ru_utime); 73 result.sys = asNanosecs(self.ru_stime) + asNanosecs(children.ru_stime); 74 75 return result; 76 } 77 78 struct TestResult { 79 uint64_t objectSize; 80 uint64_t messageSize; 81 Times time; 82 }; 83 84 enum class Product { 85 CAPNPROTO, 86 PROTOBUF, 87 NULLCASE 88 }; 89 90 enum class TestCase { 91 EVAL, 92 CATRANK, 93 CARSALES 94 }; 95 96 const char* testCaseName(TestCase testCase) { 97 switch (testCase) { 98 case TestCase::EVAL: 99 return "eval"; 100 case TestCase::CATRANK: 101 return "catrank"; 102 case TestCase::CARSALES: 103 return "carsales"; 104 } 105 // Can't get here. 106 return nullptr; 107 } 108 109 enum class Mode { 110 OBJECTS, 111 OBJECT_SIZE, 112 BYTES, 113 PIPE_SYNC, 114 PIPE_ASYNC 115 }; 116 117 enum class Reuse { 118 YES, 119 NO 120 }; 121 122 enum class Compression { 123 NONE, 124 PACKED, 125 SNAPPY 126 }; 127 128 TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse, 129 Compression compression, uint64_t iters) { 130 char* argv[6]; 131 132 string progName; 133 134 switch (product) { 135 case Product::CAPNPROTO: 136 progName = "capnproto-"; 137 break; 138 case Product::PROTOBUF: 139 progName = "protobuf-"; 140 break; 141 case Product::NULLCASE: 142 progName = "null-"; 143 break; 144 } 145 146 progName += testCaseName(testCase); 147 argv[0] = strdup(progName.c_str()); 148 149 switch (mode) { 150 case Mode::OBJECTS: 151 argv[1] = strdup("object"); 152 break; 153 case Mode::OBJECT_SIZE: 154 argv[1] = strdup("object-size"); 155 break; 156 case Mode::BYTES: 157 argv[1] = strdup("bytes"); 158 break; 159 case Mode::PIPE_SYNC: 160 argv[1] = strdup("pipe"); 161 break; 162 case Mode::PIPE_ASYNC: 163 argv[1] = strdup("pipe-async"); 164 break; 165 } 166 167 switch (reuse) { 168 case Reuse::YES: 169 argv[2] = strdup("reuse"); 170 break; 171 case Reuse::NO: 172 argv[2] = strdup("no-reuse"); 173 break; 174 } 175 176 switch (compression) { 177 case Compression::NONE: 178 argv[3] = strdup("none"); 179 break; 180 case Compression::PACKED: 181 argv[3] = strdup("packed"); 182 break; 183 case Compression::SNAPPY: 184 argv[3] = strdup("snappy"); 185 break; 186 } 187 188 char itersStr[64]; 189 sprintf(itersStr, "%llu", (long long unsigned int)iters); 190 argv[4] = itersStr; 191 192 argv[5] = nullptr; 193 194 // Make pipe for child to write throughput. 195 int childPipe[2]; 196 if (pipe(childPipe) < 0) { 197 perror("pipe"); 198 exit(1); 199 } 200 201 // Spawn the child process. 202 struct timeval start, end; 203 gettimeofday(&start, nullptr); 204 pid_t child = fork(); 205 if (child == 0) { 206 close(childPipe[0]); 207 dup2(childPipe[1], STDOUT_FILENO); 208 close(childPipe[1]); 209 execv(argv[0], argv); 210 exit(1); 211 } 212 213 close(childPipe[1]); 214 for (int i = 0; i < 4; i++) { 215 free(argv[i]); 216 } 217 218 // Read throughput number written to child's stdout. 219 FILE* input = fdopen(childPipe[0], "r"); 220 long long unsigned int throughput; 221 if (fscanf(input, "%lld", &throughput) != 1) { 222 fprintf(stderr, "Child didn't write throughput to stdout."); 223 } 224 char buffer[1024]; 225 while (fgets(buffer, sizeof(buffer), input) != nullptr) { 226 // Loop until EOF. 227 } 228 fclose(input); 229 230 // Wait for child exit. 231 int status; 232 struct rusage usage; 233 wait4(child, &status, 0, &usage); 234 gettimeofday(&end, nullptr); 235 236 // Calculate results. 237 238 TestResult result; 239 result.objectSize = mode == Mode::OBJECT_SIZE ? throughput : 0; 240 result.messageSize = mode == Mode::OBJECT_SIZE ? 0 : throughput; 241 result.time.real = asNanosecs(end) - asNanosecs(start); 242 result.time.user = asNanosecs(usage.ru_utime); 243 result.time.sys = asNanosecs(usage.ru_stime); 244 245 return result; 246 } 247 248 void reportTableHeader() { 249 cout << setw(40) << left << "Test" 250 << setw(10) << right << "obj size" 251 << setw(10) << right << "I/O bytes" 252 << setw(10) << right << "wall ns" 253 << setw(10) << right << "user ns" 254 << setw(10) << right << "sys ns" 255 << endl; 256 cout << setfill('=') << setw(90) << "" << setfill(' ') << endl; 257 } 258 259 void reportResults(const char* name, uint64_t iters, TestResult results) { 260 cout << setw(40) << left << name 261 << setw(10) << right << (results.objectSize / iters) 262 << setw(10) << right << (results.messageSize / iters) 263 << setw(10) << right << (results.time.real / iters) 264 << setw(10) << right << (results.time.user / iters) 265 << setw(10) << right << (results.time.sys / iters) 266 << endl; 267 } 268 269 void reportComparisonHeader() { 270 cout << setw(40) << left << "Measure" 271 << setw(15) << right << "Protobuf" 272 << setw(15) << right << "Cap'n Proto" 273 << setw(15) << right << "Improvement" 274 << endl; 275 cout << setfill('=') << setw(85) << "" << setfill(' ') << endl; 276 } 277 278 void reportOldNewComparisonHeader() { 279 cout << setw(40) << left << "Measure" 280 << setw(15) << right << "Old" 281 << setw(15) << right << "New" 282 << setw(15) << right << "Improvement" 283 << endl; 284 cout << setfill('=') << setw(85) << "" << setfill(' ') << endl; 285 } 286 287 class Gain { 288 public: 289 Gain(double oldValue, double newValue) 290 : amount(newValue / oldValue) {} 291 292 void writeTo(std::ostream& os) { 293 if (amount < 2) { 294 double percent = (amount - 1) * 100; 295 os << (int)(percent + 0.5) << "%"; 296 } else { 297 os << fixed << setprecision(2) << amount << "x"; 298 } 299 } 300 301 private: 302 double amount; 303 }; 304 305 ostream& operator<<(ostream& os, Gain gain) { 306 gain.writeTo(os); 307 return os; 308 } 309 310 void reportComparison(const char* name, double base, double protobuf, double capnproto, 311 uint64_t iters) { 312 cout << setw(40) << left << name 313 << setw(14) << right << Gain(base, protobuf) 314 << setw(14) << right << Gain(base, capnproto); 315 316 // Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf. 317 cout << setw(14) << right << Gain(capnproto - base, protobuf - base) << endl; 318 } 319 320 void reportComparison(const char* name, const char* unit, double protobuf, double capnproto, 321 uint64_t iters) { 322 cout << setw(40) << left << name 323 << setw(15-strlen(unit)) << fixed << right << setprecision(2) << (protobuf / iters) << unit 324 << setw(15-strlen(unit)) << fixed << right << setprecision(2) << (capnproto / iters) << unit; 325 326 // Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf. 327 cout << setw(14) << right << Gain(capnproto, protobuf) << endl; 328 } 329 330 void reportIntComparison(const char* name, const char* unit, uint64_t protobuf, uint64_t capnproto, 331 uint64_t iters) { 332 cout << setw(40) << left << name 333 << setw(15-strlen(unit)) << right << (protobuf / iters) << unit 334 << setw(15-strlen(unit)) << right << (capnproto / iters) << unit; 335 336 // Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf. 337 cout << setw(14) << right << Gain(capnproto, protobuf) << endl; 338 } 339 340 size_t fileSize(const std::string& name) { 341 struct stat stats; 342 if (stat(name.c_str(), &stats) < 0) { 343 perror(name.c_str()); 344 exit(1); 345 } 346 347 return stats.st_size; 348 } 349 350 int main(int argc, char* argv[]) { 351 char* path = argv[0]; 352 char* slashpos = strrchr(path, '/'); 353 char origDir[1024]; 354 if (getcwd(origDir, sizeof(origDir)) == nullptr) { 355 perror("getcwd"); 356 return 1; 357 } 358 if (slashpos != nullptr) { 359 *slashpos = '\0'; 360 if (chdir(path) < 0) { 361 perror("chdir"); 362 return 1; 363 } 364 *slashpos = '/'; 365 } 366 367 TestCase testCase = TestCase::CATRANK; 368 Mode mode = Mode::PIPE_SYNC; 369 Compression compression = Compression::NONE; 370 uint64_t iters = 1; 371 const char* oldDir = nullptr; 372 373 for (int i = 1; i < argc; i++) { 374 string arg = argv[i]; 375 if (isdigit(argv[i][0])) { 376 iters = strtoul(argv[i], nullptr, 0); 377 } else if (arg == "async") { 378 mode = Mode::PIPE_ASYNC; 379 } else if (arg == "inmem") { 380 mode = Mode::BYTES; 381 } else if (arg == "eval") { 382 testCase = TestCase::EVAL; 383 } else if (arg == "carsales") { 384 testCase = TestCase::CARSALES; 385 } else if (arg == "snappy") { 386 compression = Compression::SNAPPY; 387 } else if (arg == "-c") { 388 ++i; 389 if (i == argc) { 390 fprintf(stderr, "-c requires argument.\n"); 391 return 1; 392 } 393 oldDir = argv[i]; 394 } else { 395 fprintf(stderr, "Unknown option: %s\n", argv[i]); 396 return 1; 397 } 398 } 399 400 // Scale iterations to something reasonable for each case. 401 switch (testCase) { 402 case TestCase::EVAL: 403 iters *= 100000; 404 break; 405 case TestCase::CATRANK: 406 iters *= 1000; 407 break; 408 case TestCase::CARSALES: 409 iters *= 20000; 410 break; 411 } 412 413 cout << "Running " << iters << " iterations of "; 414 switch (testCase) { 415 case TestCase::EVAL: 416 cout << "calculator"; 417 break; 418 case TestCase::CATRANK: 419 cout << "CatRank"; 420 break; 421 case TestCase::CARSALES: 422 cout << "car sales"; 423 break; 424 } 425 426 cout << " example case with:" << endl; 427 428 switch (mode) { 429 case Mode::OBJECTS: 430 case Mode::OBJECT_SIZE: 431 // Can't happen. 432 break; 433 case Mode::BYTES: 434 cout << "* in-memory I/O" << endl; 435 cout << " * with client and server in the same thread" << endl; 436 break; 437 case Mode::PIPE_SYNC: 438 cout << "* pipe I/O" << endl; 439 cout << " * with client and server in separate processes" << endl; 440 cout << " * client waits for each response before sending next request" << endl; 441 break; 442 case Mode::PIPE_ASYNC: 443 cout << "* pipe I/O" << endl; 444 cout << " * with client and server in separate processes" << endl; 445 cout << " * client sends as many simultaneous requests as it can" << endl; 446 break; 447 } 448 switch (compression) { 449 case Compression::NONE: 450 cout << "* no compression" << endl; 451 break; 452 case Compression::PACKED: 453 cout << "* de-zero packing for Cap'n Proto" << endl; 454 cout << "* standard packing for Protobuf" << endl; 455 break; 456 case Compression::SNAPPY: 457 cout << "* Snappy compression" << endl; 458 break; 459 } 460 461 cout << endl; 462 463 reportTableHeader(); 464 465 TestResult nullCase = runTest( 466 Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters); 467 reportResults("Theoretical best pass-by-object", iters, nullCase); 468 469 TestResult protobufBase = runTest( 470 Product::PROTOBUF, testCase, Mode::OBJECTS, Reuse::YES, compression, iters); 471 protobufBase.objectSize = runTest( 472 Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters).objectSize; 473 reportResults("Protobuf pass-by-object", iters, protobufBase); 474 475 TestResult capnpBase = runTest( 476 Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::YES, compression, iters); 477 capnpBase.objectSize = runTest( 478 Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters).objectSize; 479 reportResults("Cap'n Proto pass-by-object", iters, capnpBase); 480 481 TestResult nullCaseNoReuse = runTest( 482 Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters); 483 reportResults("Theoretical best w/o object reuse", iters, nullCaseNoReuse); 484 485 TestResult protobufNoReuse = runTest( 486 Product::PROTOBUF, testCase, Mode::OBJECTS, Reuse::NO, compression, iters); 487 protobufNoReuse.objectSize = runTest( 488 Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize; 489 reportResults("Protobuf w/o object reuse", iters, protobufNoReuse); 490 491 TestResult capnpNoReuse = runTest( 492 Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::NO, compression, iters); 493 capnpNoReuse.objectSize = runTest( 494 Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize; 495 reportResults("Cap'n Proto w/o object reuse", iters, capnpNoReuse); 496 497 TestResult protobuf = runTest( 498 Product::PROTOBUF, testCase, mode, Reuse::YES, compression, iters); 499 protobuf.objectSize = protobufBase.objectSize; 500 reportResults("Protobuf I/O", iters, protobuf); 501 502 TestResult capnp = runTest( 503 Product::CAPNPROTO, testCase, mode, Reuse::YES, compression, iters); 504 capnp.objectSize = capnpBase.objectSize; 505 reportResults("Cap'n Proto I/O", iters, capnp); 506 TestResult capnpPacked = runTest( 507 Product::CAPNPROTO, testCase, mode, Reuse::YES, Compression::PACKED, iters); 508 capnpPacked.objectSize = capnpBase.objectSize; 509 reportResults("Cap'n Proto packed I/O", iters, capnpPacked); 510 511 size_t protobufBinarySize = fileSize("protobuf-" + std::string(testCaseName(testCase))); 512 size_t capnpBinarySize = fileSize("capnproto-" + std::string(testCaseName(testCase))); 513 size_t protobufCodeSize = fileSize(std::string(testCaseName(testCase)) + ".pb.cc") 514 + fileSize(std::string(testCaseName(testCase)) + ".pb.h"); 515 size_t capnpCodeSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.c++") 516 + fileSize(std::string(testCaseName(testCase)) + ".capnp.h"); 517 size_t protobufObjSize = fileSize(std::string(testCaseName(testCase)) + ".pb.o"); 518 size_t capnpObjSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.o"); 519 520 TestResult oldNullCase; 521 TestResult oldNullCaseNoReuse; 522 TestResult oldCapnpBase; 523 TestResult oldCapnpNoReuse; 524 TestResult oldCapnp; 525 TestResult oldCapnpPacked; 526 size_t oldCapnpBinarySize = 0; 527 size_t oldCapnpCodeSize = 0; 528 size_t oldCapnpObjSize = 0; 529 if (oldDir != nullptr) { 530 if (chdir(origDir) < 0) { 531 perror("chdir"); 532 return 1; 533 } 534 if (chdir(oldDir) < 0) { 535 perror(oldDir); 536 return 1; 537 } 538 539 oldNullCase = runTest( 540 Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters); 541 reportResults("Old theoretical best pass-by-object", iters, nullCase); 542 543 oldCapnpBase = runTest( 544 Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::YES, compression, iters); 545 oldCapnpBase.objectSize = runTest( 546 Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters) 547 .objectSize; 548 reportResults("Old Cap'n Proto pass-by-object", iters, oldCapnpBase); 549 550 oldNullCaseNoReuse = runTest( 551 Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters); 552 reportResults("Old theoretical best w/o object reuse", iters, oldNullCaseNoReuse); 553 554 oldCapnpNoReuse = runTest( 555 Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::NO, compression, iters); 556 oldCapnpNoReuse.objectSize = runTest( 557 Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize; 558 reportResults("Old Cap'n Proto w/o object reuse", iters, oldCapnpNoReuse); 559 560 oldCapnp = runTest( 561 Product::CAPNPROTO, testCase, mode, Reuse::YES, compression, iters); 562 oldCapnp.objectSize = oldCapnpBase.objectSize; 563 reportResults("Old Cap'n Proto I/O", iters, oldCapnp); 564 oldCapnpPacked = runTest( 565 Product::CAPNPROTO, testCase, mode, Reuse::YES, Compression::PACKED, iters); 566 oldCapnpPacked.objectSize = oldCapnpBase.objectSize; 567 reportResults("Old Cap'n Proto packed I/O", iters, oldCapnpPacked); 568 569 oldCapnpBinarySize = fileSize("capnproto-" + std::string(testCaseName(testCase))); 570 oldCapnpCodeSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.c++") 571 + fileSize(std::string(testCaseName(testCase)) + ".capnp.h"); 572 oldCapnpObjSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.o"); 573 } 574 575 cout << endl; 576 577 reportComparisonHeader(); 578 reportComparison("memory overhead (vs ideal)", 579 nullCase.objectSize, protobufBase.objectSize, capnpBase.objectSize, iters); 580 reportComparison("memory overhead w/o object reuse", 581 nullCaseNoReuse.objectSize, protobufNoReuse.objectSize, capnpNoReuse.objectSize, iters); 582 reportComparison("object manipulation time (us)", "", 583 ((int64_t)protobufBase.time.user - (int64_t)nullCase.time.user) / 1000.0, 584 ((int64_t)capnpBase.time.user - (int64_t)nullCase.time.user) / 1000.0, iters); 585 reportComparison("object manipulation time w/o reuse (us)", "", 586 ((int64_t)protobufNoReuse.time.user - (int64_t)nullCaseNoReuse.time.user) / 1000.0, 587 ((int64_t)capnpNoReuse.time.user - (int64_t)nullCaseNoReuse.time.user) / 1000.0, iters); 588 reportComparison("I/O time (us)", "", 589 ((int64_t)protobuf.time.user - (int64_t)protobufBase.time.user) / 1000.0, 590 ((int64_t)capnp.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); 591 reportComparison("packed I/O time (us)", "", 592 ((int64_t)protobuf.time.user - (int64_t)protobufBase.time.user) / 1000.0, 593 ((int64_t)capnpPacked.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); 594 595 reportIntComparison("message size (bytes)", "", protobuf.messageSize, capnp.messageSize, iters); 596 reportIntComparison("packed message size (bytes)", "", 597 protobuf.messageSize, capnpPacked.messageSize, iters); 598 599 reportComparison("binary size (KiB)", "", 600 protobufBinarySize / 1024.0, capnpBinarySize / 1024.0, 1); 601 reportComparison("generated code size (KiB)", "", 602 protobufCodeSize / 1024.0, capnpCodeSize / 1024.0, 1); 603 reportComparison("generated obj size (KiB)", "", 604 protobufObjSize / 1024.0, capnpObjSize / 1024.0, 1); 605 606 if (oldDir != nullptr) { 607 cout << endl; 608 reportOldNewComparisonHeader(); 609 610 reportComparison("memory overhead", 611 oldNullCase.objectSize, oldCapnpBase.objectSize, capnpBase.objectSize, iters); 612 reportComparison("memory overhead w/o object reuse", 613 oldNullCaseNoReuse.objectSize, oldCapnpNoReuse.objectSize, capnpNoReuse.objectSize, iters); 614 reportComparison("object manipulation time (us)", "", 615 ((int64_t)oldCapnpBase.time.user - (int64_t)oldNullCase.time.user) / 1000.0, 616 ((int64_t)capnpBase.time.user - (int64_t)oldNullCase.time.user) / 1000.0, iters); 617 reportComparison("object manipulation time w/o reuse (us)", "", 618 ((int64_t)oldCapnpNoReuse.time.user - (int64_t)oldNullCaseNoReuse.time.user) / 1000.0, 619 ((int64_t)capnpNoReuse.time.user - (int64_t)oldNullCaseNoReuse.time.user) / 1000.0, iters); 620 reportComparison("I/O time (us)", "", 621 ((int64_t)oldCapnp.time.user - (int64_t)oldCapnpBase.time.user) / 1000.0, 622 ((int64_t)capnp.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); 623 reportComparison("packed I/O time (us)", "", 624 ((int64_t)oldCapnpPacked.time.user - (int64_t)oldCapnpBase.time.user) / 1000.0, 625 ((int64_t)capnpPacked.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); 626 627 reportIntComparison("message size (bytes)", "", oldCapnp.messageSize, capnp.messageSize, iters); 628 reportIntComparison("packed message size (bytes)", "", 629 oldCapnpPacked.messageSize, capnpPacked.messageSize, iters); 630 631 reportComparison("binary size (KiB)", "", 632 oldCapnpBinarySize / 1024.0, capnpBinarySize / 1024.0, 1); 633 reportComparison("generated code size (KiB)", "", 634 oldCapnpCodeSize / 1024.0, capnpCodeSize / 1024.0, 1); 635 reportComparison("generated obj size (KiB)", "", 636 oldCapnpObjSize / 1024.0, capnpObjSize / 1024.0, 1); 637 } 638 639 return 0; 640 } 641 642 } // namespace runner 643 } // namespace benchmark 644 } // namespace capnp 645 646 int main(int argc, char* argv[]) { 647 return capnp::benchmark::runner::main(argc, argv); 648 }