From 11186577ba2d44ea95129248467a8c262e35cdac Mon Sep 17 00:00:00 2001 From: wuchenghui Date: Fri, 1 Sep 2017 15:30:29 +0800 Subject: [PATCH] add test benchmark --- .gitignore | 2 + mace/core/BUILD | 28 ++++- mace/core/macros.h | 21 ++++ mace/core/testing/env_time.h | 28 +++++ mace/core/testing/test_benchmark.cc | 153 ++++++++++++++++++++++++++++ mace/core/testing/test_benchmark.h | 53 ++++++++++ mace/core/testing/test_main.cc | 15 +++ mace/core/types.h | 2 + mace/examples/BUILD | 29 +++++- mace/examples/README.md | 6 +- mace/examples/benchmark_example.cc | 24 +++++ 11 files changed, 349 insertions(+), 12 deletions(-) create mode 100644 mace/core/macros.h create mode 100644 mace/core/testing/env_time.h create mode 100644 mace/core/testing/test_benchmark.cc create mode 100644 mace/core/testing/test_benchmark.h create mode 100644 mace/core/testing/test_main.cc create mode 100644 mace/examples/benchmark_example.cc diff --git a/.gitignore b/.gitignore index ac51a054..a140a563 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bazel-* +.idea/ +cmake-build-debug/ diff --git a/mace/core/BUILD b/mace/core/BUILD index 2da78ebb..f084ef62 100644 --- a/mace/core/BUILD +++ b/mace/core/BUILD @@ -5,16 +5,36 @@ package( default_visibility = ["//visibility:public"], ) - licenses(["notice"]) # Apache 2.0 cc_library( name = "core", - srcs = glob(["*.cc"]), - hdrs = glob(["*.h"]), + srcs = glob([ + "*.cc", + ]), + hdrs = glob([ + "*.h", + ]), + copts = ["-std=c++11"], deps = [ "//mace/proto:cc_proto", ], - copts = ['-std=c++11'], ) +# Main program for tests +cc_library( + name = "test_main", + testonly = 1, + srcs = glob([ + "testing/*.cc", + ]), + hdrs = glob([ + "testing/*.h", + ]), + copts = ["-std=c++11"], + linkopts = ["-lm"], + deps = [ + ":core", + ], + alwayslink = 1, +) diff --git a/mace/core/macros.h b/mace/core/macros.h new file mode 100644 index 00000000..e23699ae --- /dev/null +++ b/mace/core/macros.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +#ifndef MACE_CORE_MACROS_H_ +#define MACE_CORE_MACROS_H_ + +// GCC can be told that a certain branch is not likely to be taken (for +// instance, a CHECK failure), and use that information in static analysis. +// Giving it this information can help it optimize for the common case in +// the absence of better information (ie. -fprofile-arcs). +#if defined(COMPILER_GCC3) +#define MACE_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define MACE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define MACE_PREDICT_FALSE(x) (x) +#define MACE_PREDICT_TRUE(x) (x) +#endif + + +#endif //MACE_CORE_MACROS_H_ diff --git a/mace/core/testing/env_time.h b/mace/core/testing/env_time.h new file mode 100644 index 00000000..6be189a6 --- /dev/null +++ b/mace/core/testing/env_time.h @@ -0,0 +1,28 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +// Only support POSIX environment +#ifndef MACE_TESTING_TIME_H_ +#define MACE_TESTING_TIME_H_ + +#include +#include +#include + +#include "mace/core/types.h" + +namespace mace { + +namespace testing { + +inline int64 NowMicros() { + struct timeval tv; + gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; +} + +} // namespace testing +} // namespace mace + +#endif // MACE_TESTING_TIME_H_ diff --git a/mace/core/testing/test_benchmark.cc b/mace/core/testing/test_benchmark.cc new file mode 100644 index 00000000..778ddb41 --- /dev/null +++ b/mace/core/testing/test_benchmark.cc @@ -0,0 +1,153 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +#include "mace/core/testing/test_benchmark.h" + +#include +#include + +#include +#include +#include "mace/core/logging.h" +#include "mace/core/testing/env_time.h" + +namespace mace { +namespace testing { + +static std::vector* all_benchmarks = nullptr; +static std::string label; +static int64 bytes_processed; +static int64 items_processed; +static int64 accum_time = 0; +static int64 start_time = 0; + +Benchmark::Benchmark(const char* name, void (*fn)(int)) + : name_(name), num_args_(0), fn0_(fn) { + args_.push_back(std::make_pair(-1, -1)); + Register(); +} + +Benchmark::Benchmark(const char* name, void (*fn)(int, int)) + : name_(name), num_args_(1), fn1_(fn) { + Register(); +} + +Benchmark::Benchmark(const char* name, void (*fn)(int, int, int)) + : name_(name), num_args_(2), fn2_(fn) { + Register(); +} + + +// Run all benchmarks +void Benchmark::Run() { + if (!all_benchmarks) return; + + // Compute name width. + int width = 10; + string name; + for (auto b : *all_benchmarks) { + name = b->name_; + for (auto arg : b->args_) { + name.resize(b->name_.size()); + if (arg.first >= 0) { + name += "/" + arg.first; + if (arg.second >= 0) { + name += "/" + arg.second; + } + } + + width = std::max(width, name.size()); + } + } + + printf("%-*s %10s %10s\n", width, "Benchmark", "Time(ns)", "Iterations"); + printf("%s\n", string(width + 22, '-').c_str()); + for (auto b : *all_benchmarks) { + name = b->name_; + for (auto arg : b->args_) { + name.resize(b->name_.size()); + if (arg.first >= 0) { + name += "/" + arg.first; + if (arg.second >= 0) { + name += "/" + arg.second; + } + } + + int iters; + double seconds; + b->Run(arg.first, arg.second, &iters, &seconds); + + char buf[100]; + std::string full_label = label; + if (bytes_processed > 0) { + snprintf(buf, sizeof(buf), " %.1fMB/s", + (bytes_processed * 1e-6) / seconds); + full_label += buf; + } + if (items_processed > 0) { + snprintf(buf, sizeof(buf), " %.1fM items/s", + (items_processed * 1e-6) / seconds); + full_label += buf; + } + printf("%-*s %10.0f %10d\t%s\n", width, name.c_str(), + seconds * 1e9 / iters, iters, full_label.c_str()); + } + } +} + +void Benchmark::Register() { + if (!all_benchmarks) all_benchmarks = new std::vector; + all_benchmarks->push_back(this); +} + +void Benchmark::Run(int arg1, int arg2, int* run_count, double* run_seconds) { + static const int64 kMinIters = 100; + static const int64 kMaxIters = 1000000000; + static const double kMinTime = 0.5; + int64 iters = kMinIters; + while (true) { + accum_time = 0; + start_time = NowMicros(); + bytes_processed = -1; + items_processed = -1; + label.clear(); + if (fn0_) { + (*fn0_)(iters); + } else if (fn1_) { + (*fn1_)(iters, arg1); + } else { + (*fn2_)(iters, arg1, arg2); + } + StopTiming(); + const double seconds = accum_time * 1e-6; + if (seconds >= kMinTime || iters >= kMaxIters) { + *run_count = iters; + *run_seconds = seconds; + return; + } + + // Update number of iterations. Overshoot by 40% in an attempt + // to succeed the next time. + double multiplier = 1.4 * kMinTime / std::max(seconds, 1e-9); + multiplier = std::min(10.0, multiplier); + if (multiplier <= 1.0) multiplier *= 2.0; + iters = std::max(multiplier * iters, iters + 1); + iters = std::min(iters, kMaxIters); + } +} + +void BytesProcessed(int64 n) { bytes_processed = n; } +void ItemsProcessed(int64 n) { items_processed = n; } +void StartTiming() { + if (start_time == 0) start_time = NowMicros(); +} +void StopTiming() { + if (start_time != 0) { + accum_time += (NowMicros() - start_time); + start_time = 0; + } +} + +} // namespace testing +} // namespace mace diff --git a/mace/core/testing/test_benchmark.h b/mace/core/testing/test_benchmark.h new file mode 100644 index 00000000..89639941 --- /dev/null +++ b/mace/core/testing/test_benchmark.h @@ -0,0 +1,53 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +// Simple benchmarking facility. +#ifndef MACE_TEST_BENCHMARK_H_ +#define MACE_TEST_BENCHMARK_H_ + +#include +#include + +#include "mace/core/types.h" + +#define MACE_BENCHMARK_CONCAT(a, b, c) a##b##c +#define BENCHMARK(n) \ + static ::mace::testing::Benchmark* MACE_BENCHMARK_CONCAT(__benchmark_, n, __LINE__) = \ + (new ::mace::testing::Benchmark(#n, (n))) + +namespace mace { +namespace testing { + +class Benchmark { + public: + Benchmark(const char* name, void (*fn)(int)); + Benchmark(const char* name, void (*fn)(int, int)); + Benchmark(const char* name, void (*fn)(int, int, int)); + + static void Run(); + + private: + string name_; + int num_args_; + std::vector> args_; + void (*fn0_)(int) = nullptr; + void (*fn1_)(int, int) = nullptr; + void (*fn2_)(int, int, int) = nullptr; + + void Register(); + void Run(int arg1, int arg2, int* run_count, double* run_seconds); +}; + +void RunBenchmarks(); +void SetLabel(const std::string& label); +void BytesProcessed(int64); +void ItemsProcessed(int64); +void StartTiming(); +void StopTiming(); +void UseRealTime(); + +} // namespace testing +} // namespace mace + +#endif // MACE_TEST_BENCHMARK_H_ diff --git a/mace/core/testing/test_main.cc b/mace/core/testing/test_main.cc new file mode 100644 index 00000000..7c184210 --- /dev/null +++ b/mace/core/testing/test_main.cc @@ -0,0 +1,15 @@ +// +// Copyright (c) 2017 XiaoMi All rights reserved. +// + +#include + +#include "mace/core/testing/test_benchmark.h" + +int main(int argc, char** argv) { + std::cout << "Running main() from test_main.cc\n"; + + mace::testing::Benchmark::Run(); + return 0; +} + diff --git a/mace/core/types.h b/mace/core/types.h index 2e33a568..161be5a7 100644 --- a/mace/core/types.h +++ b/mace/core/types.h @@ -51,6 +51,8 @@ MATCH_TYPE_AND_ENUM(string, DT_STRING); MATCH_TYPE_AND_ENUM(int64, DT_INT64); MATCH_TYPE_AND_ENUM(bool, DT_BOOL); +static const int32 kint32max = ((int32)0x7FFFFFFF); + } // namespace mace #endif // MACE_CORE_TYPES_H_ diff --git a/mace/examples/BUILD b/mace/examples/BUILD index a674593b..bd591c29 100644 --- a/mace/examples/BUILD +++ b/mace/examples/BUILD @@ -5,10 +5,29 @@ cc_binary( name = "helloworld", srcs = [ "helloworld.cc", - ], + ], + copts = ["-std=c++11"], + linkopts = if_android([ + "-pie", + "-llog", + ]), deps = [ - "//mace/ops:ops", - ], - copts = ['-std=c++11'], - linkopts = if_android(["-pie", "-llog"]), + "//mace/core", + "//mace/ops", + ], +) + +cc_test( + name = "benchmark_example", + srcs = ["benchmark_example.cc"], + copts = ["-std=c++11"], + linkopts = if_android([ + "-pie", + "-llog", + ]), + linkstatic = 1, + deps = [ + "//mace/core", + "//mace/core:test_main", + ], ) diff --git a/mace/examples/README.md b/mace/examples/README.md index ec4f29a2..eb9fe27d 100644 --- a/mace/examples/README.md +++ b/mace/examples/README.md @@ -21,9 +21,9 @@ docker run -it --net=host mace-dev /bin/bash * Push and run the example ``` -adb shell "mkdir /data/local/tmp/helloword" -adb shell push bazel-bin/mace/examples/helloworld /data/local/tmp/helloword -adb shell /data/local/tmp/helloword/helloworld +adb shell "mkdir /data/local/tmp" +adb push bazel-bin/mace/examples/helloworld /data/local/tmp/ +adb shell /data/local/tmp/helloworld ``` * Check the logs diff --git a/mace/examples/benchmark_example.cc b/mace/examples/benchmark_example.cc new file mode 100644 index 00000000..c3eb4d03 --- /dev/null +++ b/mace/examples/benchmark_example.cc @@ -0,0 +1,24 @@ + +#include "mace/core/testing/test_benchmark.h" + +static void foo(int iters) { + static const int N = 32; + const int64 tot = static_cast(iters) * N; + mace::testing::ItemsProcessed(tot); + mace::testing::BytesProcessed(tot * (sizeof(float))); + + float* inp = new float[N]; + float* out = new float[N]; + + while (iters--) { + for (int i=0; i < N; i++) { + out[i] = inp[i] * 2.0; + } + } + delete[] inp; + delete[] out; +} + +BENCHMARK(foo); + + -- GitLab