benchmark_main.cpp revision c0eed72cbfe29d7d5f7daea9d019982465c566f0
17be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes/* 27be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * Copyright (C) 2012 The Android Open Source Project 37be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * 47be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * Licensed under the Apache License, Version 2.0 (the "License"); 57be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * you may not use this file except in compliance with the License. 67be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * You may obtain a copy of the License at 77be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * 87be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * http://www.apache.org/licenses/LICENSE-2.0 97be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * 107be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * Unless required by applicable law or agreed to in writing, software 117be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS, 127be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * See the License for the specific language governing permissions and 147be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes * limitations under the License. 157be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes */ 167be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 177be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include "benchmark.h" 187be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 197be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <regex.h> 207be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <stdio.h> 217be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <stdlib.h> 227be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 237be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <string> 247be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes#include <map> 257be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 26282e232e2a32cca8a288e81edddfd95f450cfc79Serban Constantinescu#include <inttypes.h> 27282e232e2a32cca8a288e81edddfd95f450cfc79Serban Constantinescu 281728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic int64_t g_bytes_processed; 291728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic int64_t g_benchmark_total_time_ns; 301728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic int64_t g_benchmark_start_time_ns; 317be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 327be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughestypedef std::map<std::string, ::testing::Benchmark*> BenchmarkMap; 337be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughestypedef BenchmarkMap::iterator BenchmarkMapIt; 341728b2396591853345507a063ed6075dfd251706Elliott Hughesstatic BenchmarkMap g_benchmarks; 35c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughesstatic size_t g_name_column_width = 20; 367be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 377be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesstatic int Round(int n) { 387be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes int base = 1; 397be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes while (base*10 < n) { 407be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes base *= 10; 417be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 427be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (n < 2*base) { 437be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return 2*base; 447be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 457be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (n < 5*base) { 467be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return 5*base; 477be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 487be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return 10*base; 497be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 507be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 517be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesstatic int64_t NanoTime() { 527be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes struct timespec t; 537be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes t.tv_sec = t.tv_nsec = 0; 547be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes clock_gettime(CLOCK_MONOTONIC, &t); 557be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec; 567be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 577be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 587be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesnamespace testing { 597be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 607be369d4c60e9df2316fdb6c73181a40020abef2Elliott HughesBenchmark* Benchmark::Arg(int arg) { 617be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes args_.push_back(arg); 627be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return this; 637be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 647be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 659edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughesconst char* Benchmark::Name() { 669edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes return name_; 679edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes} 689edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes 697be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesbool Benchmark::ShouldRun(int argc, char* argv[]) { 707be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (argc == 1) { 717be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return true; // With no arguments, we run all benchmarks. 727be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 737be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes // Otherwise, we interpret each argument as a regular expression and 747be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes // see if any of our benchmarks match. 757be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes for (int i = 1; i < argc; i++) { 767be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes regex_t re; 777be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (regcomp(&re, argv[i], 0) != 0) { 787be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]); 797be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes exit(EXIT_FAILURE); 807be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 817be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes int match = regexec(&re, name_, 0, NULL, 0); 827be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes regfree(&re); 837be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (match != REG_NOMATCH) { 847be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return true; 857be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 867be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 877be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return false; 887be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 897be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 907be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) { 917be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes name_ = name; 927be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fn_ = fn; 937be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fn_range_ = fn_range; 947be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 957be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (fn_ == NULL && fn_range_ == NULL) { 967be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fprintf(stderr, "%s: missing function\n", name_); 977be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes exit(EXIT_FAILURE); 987be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 997be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1001728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmarks.insert(std::make_pair(name, this)); 1017be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1027be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1037be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::Run() { 1049edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes if (fn_ != NULL) { 1059edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes RunWithArg(0); 1069edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes } else { 1079edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes if (args_.empty()) { 1089edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes fprintf(stderr, "%s: no args!\n", name_); 1099edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes exit(EXIT_FAILURE); 1109edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes } 1119edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes for (size_t i = 0; i < args_.size(); ++i) { 1129edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes RunWithArg(args_[i]); 1139edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes } 1147be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1157be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1167be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1177be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::RunRepeatedlyWithArg(int iterations, int arg) { 1181728b2396591853345507a063ed6075dfd251706Elliott Hughes g_bytes_processed = 0; 1191728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmark_total_time_ns = 0; 1201728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmark_start_time_ns = NanoTime(); 1217be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (fn_ != NULL) { 1227be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fn_(iterations); 1237be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } else { 1247be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fn_range_(iterations, arg); 1257be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1261728b2396591853345507a063ed6075dfd251706Elliott Hughes if (g_benchmark_start_time_ns != 0) { 1271728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns; 1287be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1297be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1307be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1317be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid Benchmark::RunWithArg(int arg) { 1327be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes // run once in case it's expensive 1337be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes int iterations = 1; 1347be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes RunRepeatedlyWithArg(iterations, arg); 1351728b2396591853345507a063ed6075dfd251706Elliott Hughes while (g_benchmark_total_time_ns < 1e9 && iterations < 1e9) { 1367be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes int last = iterations; 1371728b2396591853345507a063ed6075dfd251706Elliott Hughes if (g_benchmark_total_time_ns/iterations == 0) { 1387be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes iterations = 1e9; 1397be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } else { 1401728b2396591853345507a063ed6075dfd251706Elliott Hughes iterations = 1e9 / (g_benchmark_total_time_ns/iterations); 1417be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1427be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last)); 1437be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes iterations = Round(iterations); 1447be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes RunRepeatedlyWithArg(iterations, arg); 1457be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1467be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1477be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes char throughput[100]; 1487be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes throughput[0] = '\0'; 1491728b2396591853345507a063ed6075dfd251706Elliott Hughes if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) { 1501728b2396591853345507a063ed6075dfd251706Elliott Hughes double mib_processed = static_cast<double>(g_bytes_processed)/1e6; 1511728b2396591853345507a063ed6075dfd251706Elliott Hughes double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9; 1527be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds); 1537be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1547be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1557be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes char full_name[100]; 1567be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (fn_range_ != NULL) { 1577be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (arg >= (1<<20)) { 1587be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes snprintf(full_name, sizeof(full_name), "%s/%dM", name_, arg/(1<<20)); 1597be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } else if (arg >= (1<<10)) { 1607be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes snprintf(full_name, sizeof(full_name), "%s/%dK", name_, arg/(1<<10)); 1617be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } else { 1627be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes snprintf(full_name, sizeof(full_name), "%s/%d", name_, arg); 1637be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1647be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } else { 1657be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes snprintf(full_name, sizeof(full_name), "%s", name_); 1667be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1677be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 168c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes printf("%-*s %10d %10" PRId64 "%s\n", g_name_column_width, full_name, 1691728b2396591853345507a063ed6075dfd251706Elliott Hughes iterations, g_benchmark_total_time_ns/iterations, throughput); 1707be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes fflush(stdout); 1717be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1727be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1737be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} // namespace testing 1747be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1757be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid SetBenchmarkBytesProcessed(int64_t x) { 1761728b2396591853345507a063ed6075dfd251706Elliott Hughes g_bytes_processed = x; 1777be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1787be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1797be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid StopBenchmarkTiming() { 1801728b2396591853345507a063ed6075dfd251706Elliott Hughes if (g_benchmark_start_time_ns != 0) { 1811728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns; 1827be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1831728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmark_start_time_ns = 0; 1847be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1857be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1867be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesvoid StartBenchmarkTiming() { 1871728b2396591853345507a063ed6075dfd251706Elliott Hughes if (g_benchmark_start_time_ns == 0) { 1881728b2396591853345507a063ed6075dfd251706Elliott Hughes g_benchmark_start_time_ns = NanoTime(); 1897be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1907be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 1917be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 1927be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughesint main(int argc, char* argv[]) { 1931728b2396591853345507a063ed6075dfd251706Elliott Hughes if (g_benchmarks.empty()) { 1949edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes fprintf(stderr, "No benchmarks registered!\n"); 1957be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes exit(EXIT_FAILURE); 1967be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 1977be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes 198c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) { 199c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes g_name_column_width = std::max(g_name_column_width, strlen(it->second->Name())); 200c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes } 201c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes 2029edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes bool need_header = true; 2031728b2396591853345507a063ed6075dfd251706Elliott Hughes for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) { 2047be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes ::testing::Benchmark* b = it->second; 2057be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes if (b->ShouldRun(argc, argv)) { 2069edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes if (need_header) { 207c0eed72cbfe29d7d5f7daea9d019982465c566f0Elliott Hughes printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op"); 2089edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes fflush(stdout); 2099edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes need_header = false; 2109edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes } 2117be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes b->Run(); 2127be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 2137be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes } 2149edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes 2159edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes if (need_header) { 2169edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes fprintf(stderr, "No matching benchmarks!\n"); 2179edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes fprintf(stderr, "Available benchmarks:\n"); 2181728b2396591853345507a063ed6075dfd251706Elliott Hughes for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) { 2199edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes fprintf(stderr, " %s\n", it->second->Name()); 2209edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes } 2219edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes exit(EXIT_FAILURE); 2229edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes } 2239edb3e004b487e08cbbb54f2af18b15241550513Elliott Hughes 2247be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes return 0; 2257be369d4c60e9df2316fdb6c73181a40020abef2Elliott Hughes} 226