| // 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. |
| |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <sys/wait.h> |
| #include <sys/stat.h> |
| #include <inttypes.h> |
| #include <string> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <iostream> |
| #include <iomanip> |
| |
| using namespace std; |
| |
| namespace capnp { |
| namespace benchmark { |
| namespace runner { |
| |
| struct Times { |
| uint64_t real; |
| uint64_t user; |
| uint64_t sys; |
| |
| uint64_t cpu() { return user + sys; } |
| |
| Times operator-(const Times& other) { |
| Times result; |
| result.real = real - other.real; |
| result.user = user - other.user; |
| result.sys = sys - other.sys; |
| return result; |
| } |
| }; |
| |
| uint64_t asNanosecs(const struct timeval& tv) { |
| return (uint64_t)tv.tv_sec * 1000000000 + (uint64_t)tv.tv_usec * 1000; |
| } |
| |
| Times currentTimes() { |
| Times result; |
| |
| struct rusage self, children; |
| getrusage(RUSAGE_SELF, &self); |
| getrusage(RUSAGE_CHILDREN, &children); |
| |
| struct timeval real; |
| gettimeofday(&real, nullptr); |
| |
| result.real = asNanosecs(real); |
| result.user = asNanosecs(self.ru_utime) + asNanosecs(children.ru_utime); |
| result.sys = asNanosecs(self.ru_stime) + asNanosecs(children.ru_stime); |
| |
| return result; |
| } |
| |
| struct TestResult { |
| uint64_t objectSize; |
| uint64_t messageSize; |
| Times time; |
| }; |
| |
| enum class Product { |
| CAPNPROTO, |
| PROTOBUF, |
| NULLCASE |
| }; |
| |
| enum class TestCase { |
| EVAL, |
| CATRANK, |
| CARSALES |
| }; |
| |
| const char* testCaseName(TestCase testCase) { |
| switch (testCase) { |
| case TestCase::EVAL: |
| return "eval"; |
| case TestCase::CATRANK: |
| return "catrank"; |
| case TestCase::CARSALES: |
| return "carsales"; |
| } |
| // Can't get here. |
| return nullptr; |
| } |
| |
| enum class Mode { |
| OBJECTS, |
| OBJECT_SIZE, |
| BYTES, |
| PIPE_SYNC, |
| PIPE_ASYNC |
| }; |
| |
| enum class Reuse { |
| YES, |
| NO |
| }; |
| |
| enum class Compression { |
| NONE, |
| PACKED, |
| SNAPPY |
| }; |
| |
| TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse, |
| Compression compression, uint64_t iters) { |
| char* argv[6]; |
| |
| string progName; |
| |
| switch (product) { |
| case Product::CAPNPROTO: |
| progName = "capnproto-"; |
| break; |
| case Product::PROTOBUF: |
| progName = "protobuf-"; |
| break; |
| case Product::NULLCASE: |
| progName = "null-"; |
| break; |
| } |
| |
| progName += testCaseName(testCase); |
| argv[0] = strdup(progName.c_str()); |
| |
| switch (mode) { |
| case Mode::OBJECTS: |
| argv[1] = strdup("object"); |
| break; |
| case Mode::OBJECT_SIZE: |
| argv[1] = strdup("object-size"); |
| break; |
| case Mode::BYTES: |
| argv[1] = strdup("bytes"); |
| break; |
| case Mode::PIPE_SYNC: |
| argv[1] = strdup("pipe"); |
| break; |
| case Mode::PIPE_ASYNC: |
| argv[1] = strdup("pipe-async"); |
| break; |
| } |
| |
| switch (reuse) { |
| case Reuse::YES: |
| argv[2] = strdup("reuse"); |
| break; |
| case Reuse::NO: |
| argv[2] = strdup("no-reuse"); |
| break; |
| } |
| |
| switch (compression) { |
| case Compression::NONE: |
| argv[3] = strdup("none"); |
| break; |
| case Compression::PACKED: |
| argv[3] = strdup("packed"); |
| break; |
| case Compression::SNAPPY: |
| argv[3] = strdup("snappy"); |
| break; |
| } |
| |
| char itersStr[64]; |
| sprintf(itersStr, "%llu", (long long unsigned int)iters); |
| argv[4] = itersStr; |
| |
| argv[5] = nullptr; |
| |
| // Make pipe for child to write throughput. |
| int childPipe[2]; |
| if (pipe(childPipe) < 0) { |
| perror("pipe"); |
| exit(1); |
| } |
| |
| // Spawn the child process. |
| struct timeval start, end; |
| gettimeofday(&start, nullptr); |
| pid_t child = fork(); |
| if (child == 0) { |
| close(childPipe[0]); |
| dup2(childPipe[1], STDOUT_FILENO); |
| close(childPipe[1]); |
| execv(argv[0], argv); |
| exit(1); |
| } |
| |
| close(childPipe[1]); |
| for (int i = 0; i < 4; i++) { |
| free(argv[i]); |
| } |
| |
| // Read throughput number written to child's stdout. |
| FILE* input = fdopen(childPipe[0], "r"); |
| long long unsigned int throughput; |
| if (fscanf(input, "%lld", &throughput) != 1) { |
| fprintf(stderr, "Child didn't write throughput to stdout."); |
| } |
| char buffer[1024]; |
| while (fgets(buffer, sizeof(buffer), input) != nullptr) { |
| // Loop until EOF. |
| } |
| fclose(input); |
| |
| // Wait for child exit. |
| int status; |
| struct rusage usage; |
| wait4(child, &status, 0, &usage); |
| gettimeofday(&end, nullptr); |
| |
| // Calculate results. |
| |
| TestResult result; |
| result.objectSize = mode == Mode::OBJECT_SIZE ? throughput : 0; |
| result.messageSize = mode == Mode::OBJECT_SIZE ? 0 : throughput; |
| result.time.real = asNanosecs(end) - asNanosecs(start); |
| result.time.user = asNanosecs(usage.ru_utime); |
| result.time.sys = asNanosecs(usage.ru_stime); |
| |
| return result; |
| } |
| |
| void reportTableHeader() { |
| cout << setw(40) << left << "Test" |
| << setw(10) << right << "obj size" |
| << setw(10) << right << "I/O bytes" |
| << setw(10) << right << "wall ns" |
| << setw(10) << right << "user ns" |
| << setw(10) << right << "sys ns" |
| << endl; |
| cout << setfill('=') << setw(90) << "" << setfill(' ') << endl; |
| } |
| |
| void reportResults(const char* name, uint64_t iters, TestResult results) { |
| cout << setw(40) << left << name |
| << setw(10) << right << (results.objectSize / iters) |
| << setw(10) << right << (results.messageSize / iters) |
| << setw(10) << right << (results.time.real / iters) |
| << setw(10) << right << (results.time.user / iters) |
| << setw(10) << right << (results.time.sys / iters) |
| << endl; |
| } |
| |
| void reportComparisonHeader() { |
| cout << setw(40) << left << "Measure" |
| << setw(15) << right << "Protobuf" |
| << setw(15) << right << "Cap'n Proto" |
| << setw(15) << right << "Improvement" |
| << endl; |
| cout << setfill('=') << setw(85) << "" << setfill(' ') << endl; |
| } |
| |
| void reportOldNewComparisonHeader() { |
| cout << setw(40) << left << "Measure" |
| << setw(15) << right << "Old" |
| << setw(15) << right << "New" |
| << setw(15) << right << "Improvement" |
| << endl; |
| cout << setfill('=') << setw(85) << "" << setfill(' ') << endl; |
| } |
| |
| class Gain { |
| public: |
| Gain(double oldValue, double newValue) |
| : amount(newValue / oldValue) {} |
| |
| void writeTo(std::ostream& os) { |
| if (amount < 2) { |
| double percent = (amount - 1) * 100; |
| os << (int)(percent + 0.5) << "%"; |
| } else { |
| os << fixed << setprecision(2) << amount << "x"; |
| } |
| } |
| |
| private: |
| double amount; |
| }; |
| |
| ostream& operator<<(ostream& os, Gain gain) { |
| gain.writeTo(os); |
| return os; |
| } |
| |
| void reportComparison(const char* name, double base, double protobuf, double capnproto, |
| uint64_t iters) { |
| cout << setw(40) << left << name |
| << setw(14) << right << Gain(base, protobuf) |
| << setw(14) << right << Gain(base, capnproto); |
| |
| // Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf. |
| cout << setw(14) << right << Gain(capnproto - base, protobuf - base) << endl; |
| } |
| |
| void reportComparison(const char* name, const char* unit, double protobuf, double capnproto, |
| uint64_t iters) { |
| cout << setw(40) << left << name |
| << setw(15-strlen(unit)) << fixed << right << setprecision(2) << (protobuf / iters) << unit |
| << setw(15-strlen(unit)) << fixed << right << setprecision(2) << (capnproto / iters) << unit; |
| |
| // Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf. |
| cout << setw(14) << right << Gain(capnproto, protobuf) << endl; |
| } |
| |
| void reportIntComparison(const char* name, const char* unit, uint64_t protobuf, uint64_t capnproto, |
| uint64_t iters) { |
| cout << setw(40) << left << name |
| << setw(15-strlen(unit)) << right << (protobuf / iters) << unit |
| << setw(15-strlen(unit)) << right << (capnproto / iters) << unit; |
| |
| // Since smaller is better, the "improvement" is the "gain" from capnproto to protobuf. |
| cout << setw(14) << right << Gain(capnproto, protobuf) << endl; |
| } |
| |
| size_t fileSize(const std::string& name) { |
| struct stat stats; |
| if (stat(name.c_str(), &stats) < 0) { |
| perror(name.c_str()); |
| exit(1); |
| } |
| |
| return stats.st_size; |
| } |
| |
| int main(int argc, char* argv[]) { |
| char* path = argv[0]; |
| char* slashpos = strrchr(path, '/'); |
| char origDir[1024]; |
| if (getcwd(origDir, sizeof(origDir)) == nullptr) { |
| perror("getcwd"); |
| return 1; |
| } |
| if (slashpos != nullptr) { |
| *slashpos = '\0'; |
| if (chdir(path) < 0) { |
| perror("chdir"); |
| return 1; |
| } |
| *slashpos = '/'; |
| } |
| |
| TestCase testCase = TestCase::CATRANK; |
| Mode mode = Mode::PIPE_SYNC; |
| Compression compression = Compression::NONE; |
| uint64_t iters = 1; |
| const char* oldDir = nullptr; |
| |
| for (int i = 1; i < argc; i++) { |
| string arg = argv[i]; |
| if (isdigit(argv[i][0])) { |
| iters = strtoul(argv[i], nullptr, 0); |
| } else if (arg == "async") { |
| mode = Mode::PIPE_ASYNC; |
| } else if (arg == "inmem") { |
| mode = Mode::BYTES; |
| } else if (arg == "eval") { |
| testCase = TestCase::EVAL; |
| } else if (arg == "carsales") { |
| testCase = TestCase::CARSALES; |
| } else if (arg == "snappy") { |
| compression = Compression::SNAPPY; |
| } else if (arg == "-c") { |
| ++i; |
| if (i == argc) { |
| fprintf(stderr, "-c requires argument.\n"); |
| return 1; |
| } |
| oldDir = argv[i]; |
| } else { |
| fprintf(stderr, "Unknown option: %s\n", argv[i]); |
| return 1; |
| } |
| } |
| |
| // Scale iterations to something reasonable for each case. |
| switch (testCase) { |
| case TestCase::EVAL: |
| iters *= 100000; |
| break; |
| case TestCase::CATRANK: |
| iters *= 1000; |
| break; |
| case TestCase::CARSALES: |
| iters *= 20000; |
| break; |
| } |
| |
| cout << "Running " << iters << " iterations of "; |
| switch (testCase) { |
| case TestCase::EVAL: |
| cout << "calculator"; |
| break; |
| case TestCase::CATRANK: |
| cout << "CatRank"; |
| break; |
| case TestCase::CARSALES: |
| cout << "car sales"; |
| break; |
| } |
| |
| cout << " example case with:" << endl; |
| |
| switch (mode) { |
| case Mode::OBJECTS: |
| case Mode::OBJECT_SIZE: |
| // Can't happen. |
| break; |
| case Mode::BYTES: |
| cout << "* in-memory I/O" << endl; |
| cout << " * with client and server in the same thread" << endl; |
| break; |
| case Mode::PIPE_SYNC: |
| cout << "* pipe I/O" << endl; |
| cout << " * with client and server in separate processes" << endl; |
| cout << " * client waits for each response before sending next request" << endl; |
| break; |
| case Mode::PIPE_ASYNC: |
| cout << "* pipe I/O" << endl; |
| cout << " * with client and server in separate processes" << endl; |
| cout << " * client sends as many simultaneous requests as it can" << endl; |
| break; |
| } |
| switch (compression) { |
| case Compression::NONE: |
| cout << "* no compression" << endl; |
| break; |
| case Compression::PACKED: |
| cout << "* de-zero packing for Cap'n Proto" << endl; |
| cout << "* standard packing for Protobuf" << endl; |
| break; |
| case Compression::SNAPPY: |
| cout << "* Snappy compression" << endl; |
| break; |
| } |
| |
| cout << endl; |
| |
| reportTableHeader(); |
| |
| TestResult nullCase = runTest( |
| Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters); |
| reportResults("Theoretical best pass-by-object", iters, nullCase); |
| |
| TestResult protobufBase = runTest( |
| Product::PROTOBUF, testCase, Mode::OBJECTS, Reuse::YES, compression, iters); |
| protobufBase.objectSize = runTest( |
| Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters).objectSize; |
| reportResults("Protobuf pass-by-object", iters, protobufBase); |
| |
| TestResult capnpBase = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::YES, compression, iters); |
| capnpBase.objectSize = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters).objectSize; |
| reportResults("Cap'n Proto pass-by-object", iters, capnpBase); |
| |
| TestResult nullCaseNoReuse = runTest( |
| Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters); |
| reportResults("Theoretical best w/o object reuse", iters, nullCaseNoReuse); |
| |
| TestResult protobufNoReuse = runTest( |
| Product::PROTOBUF, testCase, Mode::OBJECTS, Reuse::NO, compression, iters); |
| protobufNoReuse.objectSize = runTest( |
| Product::PROTOBUF, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize; |
| reportResults("Protobuf w/o object reuse", iters, protobufNoReuse); |
| |
| TestResult capnpNoReuse = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::NO, compression, iters); |
| capnpNoReuse.objectSize = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize; |
| reportResults("Cap'n Proto w/o object reuse", iters, capnpNoReuse); |
| |
| TestResult protobuf = runTest( |
| Product::PROTOBUF, testCase, mode, Reuse::YES, compression, iters); |
| protobuf.objectSize = protobufBase.objectSize; |
| reportResults("Protobuf I/O", iters, protobuf); |
| |
| TestResult capnp = runTest( |
| Product::CAPNPROTO, testCase, mode, Reuse::YES, compression, iters); |
| capnp.objectSize = capnpBase.objectSize; |
| reportResults("Cap'n Proto I/O", iters, capnp); |
| TestResult capnpPacked = runTest( |
| Product::CAPNPROTO, testCase, mode, Reuse::YES, Compression::PACKED, iters); |
| capnpPacked.objectSize = capnpBase.objectSize; |
| reportResults("Cap'n Proto packed I/O", iters, capnpPacked); |
| |
| size_t protobufBinarySize = fileSize("protobuf-" + std::string(testCaseName(testCase))); |
| size_t capnpBinarySize = fileSize("capnproto-" + std::string(testCaseName(testCase))); |
| size_t protobufCodeSize = fileSize(std::string(testCaseName(testCase)) + ".pb.cc") |
| + fileSize(std::string(testCaseName(testCase)) + ".pb.h"); |
| size_t capnpCodeSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.c++") |
| + fileSize(std::string(testCaseName(testCase)) + ".capnp.h"); |
| size_t protobufObjSize = fileSize(std::string(testCaseName(testCase)) + ".pb.o"); |
| size_t capnpObjSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.o"); |
| |
| TestResult oldNullCase; |
| TestResult oldNullCaseNoReuse; |
| TestResult oldCapnpBase; |
| TestResult oldCapnpNoReuse; |
| TestResult oldCapnp; |
| TestResult oldCapnpPacked; |
| size_t oldCapnpBinarySize = 0; |
| size_t oldCapnpCodeSize = 0; |
| size_t oldCapnpObjSize = 0; |
| if (oldDir != nullptr) { |
| if (chdir(origDir) < 0) { |
| perror("chdir"); |
| return 1; |
| } |
| if (chdir(oldDir) < 0) { |
| perror(oldDir); |
| return 1; |
| } |
| |
| oldNullCase = runTest( |
| Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters); |
| reportResults("Old theoretical best pass-by-object", iters, nullCase); |
| |
| oldCapnpBase = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::YES, compression, iters); |
| oldCapnpBase.objectSize = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::YES, compression, iters) |
| .objectSize; |
| reportResults("Old Cap'n Proto pass-by-object", iters, oldCapnpBase); |
| |
| oldNullCaseNoReuse = runTest( |
| Product::NULLCASE, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters); |
| reportResults("Old theoretical best w/o object reuse", iters, oldNullCaseNoReuse); |
| |
| oldCapnpNoReuse = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECTS, Reuse::NO, compression, iters); |
| oldCapnpNoReuse.objectSize = runTest( |
| Product::CAPNPROTO, testCase, Mode::OBJECT_SIZE, Reuse::NO, compression, iters).objectSize; |
| reportResults("Old Cap'n Proto w/o object reuse", iters, oldCapnpNoReuse); |
| |
| oldCapnp = runTest( |
| Product::CAPNPROTO, testCase, mode, Reuse::YES, compression, iters); |
| oldCapnp.objectSize = oldCapnpBase.objectSize; |
| reportResults("Old Cap'n Proto I/O", iters, oldCapnp); |
| oldCapnpPacked = runTest( |
| Product::CAPNPROTO, testCase, mode, Reuse::YES, Compression::PACKED, iters); |
| oldCapnpPacked.objectSize = oldCapnpBase.objectSize; |
| reportResults("Old Cap'n Proto packed I/O", iters, oldCapnpPacked); |
| |
| oldCapnpBinarySize = fileSize("capnproto-" + std::string(testCaseName(testCase))); |
| oldCapnpCodeSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.c++") |
| + fileSize(std::string(testCaseName(testCase)) + ".capnp.h"); |
| oldCapnpObjSize = fileSize(std::string(testCaseName(testCase)) + ".capnp.o"); |
| } |
| |
| cout << endl; |
| |
| reportComparisonHeader(); |
| reportComparison("memory overhead (vs ideal)", |
| nullCase.objectSize, protobufBase.objectSize, capnpBase.objectSize, iters); |
| reportComparison("memory overhead w/o object reuse", |
| nullCaseNoReuse.objectSize, protobufNoReuse.objectSize, capnpNoReuse.objectSize, iters); |
| reportComparison("object manipulation time (us)", "", |
| ((int64_t)protobufBase.time.user - (int64_t)nullCase.time.user) / 1000.0, |
| ((int64_t)capnpBase.time.user - (int64_t)nullCase.time.user) / 1000.0, iters); |
| reportComparison("object manipulation time w/o reuse (us)", "", |
| ((int64_t)protobufNoReuse.time.user - (int64_t)nullCaseNoReuse.time.user) / 1000.0, |
| ((int64_t)capnpNoReuse.time.user - (int64_t)nullCaseNoReuse.time.user) / 1000.0, iters); |
| reportComparison("I/O time (us)", "", |
| ((int64_t)protobuf.time.user - (int64_t)protobufBase.time.user) / 1000.0, |
| ((int64_t)capnp.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); |
| reportComparison("packed I/O time (us)", "", |
| ((int64_t)protobuf.time.user - (int64_t)protobufBase.time.user) / 1000.0, |
| ((int64_t)capnpPacked.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); |
| |
| reportIntComparison("message size (bytes)", "", protobuf.messageSize, capnp.messageSize, iters); |
| reportIntComparison("packed message size (bytes)", "", |
| protobuf.messageSize, capnpPacked.messageSize, iters); |
| |
| reportComparison("binary size (KiB)", "", |
| protobufBinarySize / 1024.0, capnpBinarySize / 1024.0, 1); |
| reportComparison("generated code size (KiB)", "", |
| protobufCodeSize / 1024.0, capnpCodeSize / 1024.0, 1); |
| reportComparison("generated obj size (KiB)", "", |
| protobufObjSize / 1024.0, capnpObjSize / 1024.0, 1); |
| |
| if (oldDir != nullptr) { |
| cout << endl; |
| reportOldNewComparisonHeader(); |
| |
| reportComparison("memory overhead", |
| oldNullCase.objectSize, oldCapnpBase.objectSize, capnpBase.objectSize, iters); |
| reportComparison("memory overhead w/o object reuse", |
| oldNullCaseNoReuse.objectSize, oldCapnpNoReuse.objectSize, capnpNoReuse.objectSize, iters); |
| reportComparison("object manipulation time (us)", "", |
| ((int64_t)oldCapnpBase.time.user - (int64_t)oldNullCase.time.user) / 1000.0, |
| ((int64_t)capnpBase.time.user - (int64_t)oldNullCase.time.user) / 1000.0, iters); |
| reportComparison("object manipulation time w/o reuse (us)", "", |
| ((int64_t)oldCapnpNoReuse.time.user - (int64_t)oldNullCaseNoReuse.time.user) / 1000.0, |
| ((int64_t)capnpNoReuse.time.user - (int64_t)oldNullCaseNoReuse.time.user) / 1000.0, iters); |
| reportComparison("I/O time (us)", "", |
| ((int64_t)oldCapnp.time.user - (int64_t)oldCapnpBase.time.user) / 1000.0, |
| ((int64_t)capnp.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); |
| reportComparison("packed I/O time (us)", "", |
| ((int64_t)oldCapnpPacked.time.user - (int64_t)oldCapnpBase.time.user) / 1000.0, |
| ((int64_t)capnpPacked.time.user - (int64_t)capnpBase.time.user) / 1000.0, iters); |
| |
| reportIntComparison("message size (bytes)", "", oldCapnp.messageSize, capnp.messageSize, iters); |
| reportIntComparison("packed message size (bytes)", "", |
| oldCapnpPacked.messageSize, capnpPacked.messageSize, iters); |
| |
| reportComparison("binary size (KiB)", "", |
| oldCapnpBinarySize / 1024.0, capnpBinarySize / 1024.0, 1); |
| reportComparison("generated code size (KiB)", "", |
| oldCapnpCodeSize / 1024.0, capnpCodeSize / 1024.0, 1); |
| reportComparison("generated obj size (KiB)", "", |
| oldCapnpObjSize / 1024.0, capnpObjSize / 1024.0, 1); |
| } |
| |
| return 0; |
| } |
| |
| } // namespace runner |
| } // namespace benchmark |
| } // namespace capnp |
| |
| int main(int argc, char* argv[]) { |
| return capnp::benchmark::runner::main(argc, argv); |
| } |