├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.rst ├── examples ├── full.cpp └── simple.cpp ├── include └── cpm │ ├── bootstrap_tabs_theme.hpp │ ├── bootstrap_theme.hpp │ ├── compat.hpp │ ├── compiler.hpp │ ├── config.hpp │ ├── cpm.hpp │ ├── cpm_support.hpp │ ├── data.hpp │ ├── duration.hpp │ ├── io.hpp │ ├── json.hpp │ ├── policy.hpp │ ├── random.hpp │ ├── rapidjson.hpp │ └── raw_theme.hpp ├── samples.sh ├── sonar-project.properties └── src ├── cpm.cpp └── dark_unica.inc.js /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | results 3 | reports 4 | debug 5 | release 6 | release_debug 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/rapidjson"] 2 | path = lib/rapidjson 3 | url = https://github.com/miloyip/rapidjson.git 4 | [submodule "make-utils"] 5 | path = make-utils 6 | url = https://github.com/wichtounet/make-utils.git 7 | branch = master 8 | [submodule "lib/cxxopts"] 9 | path = lib/cxxopts 10 | url = https://github.com/jarro2783/cxxopts.git 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Baptiste Wicht 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: release 2 | 3 | .PHONY: default release debug all clean 4 | 5 | include make-utils/flags.mk 6 | include make-utils/cpp-utils.mk 7 | 8 | # Use C++20 9 | $(eval $(call use_cpp20)) 10 | 11 | CXX_FLAGS += -Ilib/rapidjson/include -Ilib/cxxopts/src/ 12 | 13 | # Make sure warnings are not ignored 14 | CXX_FLAGS += -Werror -pedantic 15 | 16 | # Disable documentation warnings for dependencies 17 | ifneq (,$(findstring clang,$(CXX))) 18 | CXX_FLAGS += -Wno-documentation 19 | endif 20 | 21 | # Use the correct stdlib 22 | ifneq (,$(findstring clang,$(CXX))) 23 | CXX_FLAGS += -stdlib=libc++ 24 | endif 25 | 26 | # Generate the default executable 27 | $(eval $(call auto_folder_compile,src)) 28 | $(eval $(call auto_add_executable,cpm)) 29 | 30 | $(eval $(call folder_compile,examples)) 31 | $(eval $(call add_executable,simple,examples/simple.cpp)) 32 | $(eval $(call add_executable,full,examples/full.cpp)) 33 | 34 | PREFIX = $(DESTDIR)/usr/local 35 | BINDIR = $(PREFIX)/bin 36 | INCDIR = $(PREFIX)/include 37 | 38 | release: release/bin/cpm 39 | release_debug: release_debug/bin/cpm 40 | debug: debug/bin/cpm 41 | 42 | install: release 43 | install -D release/bin/cpm $(BINDIR)/cpm 44 | @mkdir -p $(INCDIR)/cpm 45 | install -D -m 0644 include/cpm/*.hpp $(INCDIR)/cpm 46 | 47 | install-strip: release 48 | install -D -s release/bin/cpm $(BINDIR)/cpm 49 | @mkdir -p $(INCDIR)/cpm 50 | install -D -m 0644 include/cpm/*.hpp $(INCDIR)/cpm 51 | 52 | examples: debug/bin/simple debug/bin/full 53 | 54 | release_examples: release_debug/bin/simple release_debug/bin/full 55 | 56 | all: release release_debug debug 57 | 58 | clean: base_clean 59 | 60 | include make-utils/cpp-utils-finalize.mk 61 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Continuous Perfomance Monitor (CPM) 2 | ################################### 3 | 4 | Simple tool to monitor the performance of a program in the long run. 5 | 6 | Build 7 | +++++ 8 | 9 | The benchmark helper is header-only, but the report generator needs to be built. You simple need to use make for building and installing it: 10 | 11 | .. code:: bash 12 | 13 | make 14 | sudo make install 15 | 16 | You need a recent compiler to build cpm. It has been tested with clang++-3.4 and greater and g++-4.9 and greater. 17 | -------------------------------------------------------------------------------- /examples/full.cpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #define CPM_BENCHMARK "Tests Benchmarks" 9 | #define CPM_NO_RANDOMIZATION 10 | #define CPM_AUTO_STEPS 11 | #define CPM_STEP_ESTIMATION_MIN 0.05 12 | #define CPM_RUNTIME_TARGET 0.25 13 | #define CPM_SECTION_FLOPS 14 | 15 | #include "cpm/cpm.hpp" 16 | 17 | #include 18 | 19 | constexpr const double factor = 1.1; 20 | 21 | constexpr std::chrono::nanoseconds operator ""_ns(unsigned long long us){ 22 | return std::chrono::nanoseconds(us); 23 | } 24 | 25 | struct test { std::size_t d; }; 26 | void randomize(test&){} 27 | void random_init(test&){} 28 | 29 | #define CPM_WARMUP 10 30 | #define CPM_REPEAT 50 31 | 32 | CPM_BENCH() { 33 | CPM_SIMPLE("simple_a [tag1]", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 1_ns ); }); 34 | CPM_SIMPLE("simple_b", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }); 35 | CPM_SIMPLE("simple_c", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }, [](std::size_t d) { return 2 * d; }); 36 | } 37 | 38 | CPM_DIRECT_BENCH_SIMPLE("simple_direct_a", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }) 39 | 40 | CPM_DIRECT_BENCH_SIMPLE("simple_direct_b", 41 | [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }, 42 | [](std::size_t d){ return 2 * d; }) 43 | 44 | CPM_BENCH() { 45 | CPM_SIMPLE_P( 46 | NARY_POLICY(VALUES_POLICY(1,2,3,4,5,6), VALUES_POLICY(2,4,8,16,32,64)), 47 | "simple_a_n", [](auto d1, auto /*d2*/){ std::this_thread::sleep_for((factor * d1) * 1_ns ); }); 48 | CPM_SIMPLE_P( 49 | NARY_POLICY(VALUES_POLICY(1,2,3,4,5,6), VALUES_POLICY(2,4,8,16,32,64)), 50 | "simple_a_n_f", 51 | [](auto d1, auto /*d2*/){ std::this_thread::sleep_for((factor * d1) * 1_ns ); }, 52 | [](auto d1, auto d2){ return d1 * d2; }); 53 | CPM_SIMPLE_P( 54 | NARY_POLICY(VALUES_POLICY(1,2,3,4,5,6)), 55 | "simple_b_n", 56 | [](auto d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }); 57 | CPM_SIMPLE_P( 58 | VALUES_POLICY(1,2,3,4,5,6), 59 | "simple_c_n", 60 | [](auto d){ std::this_thread::sleep_for((factor * 3 * d) * 2_ns ); }); 61 | CPM_SIMPLE_P( 62 | VALUES_POLICY(1,2,3,4,5,6), 63 | "simple_c_n", 64 | [](auto d){ std::this_thread::sleep_for((factor * 3 * d) * 2_ns ); }, 65 | [](auto d){ return d / 2; }); 66 | } 67 | 68 | CPM_BENCH() { 69 | test a{3}; 70 | test b{5}; 71 | CPM_GLOBAL("global_a", [&a](std::size_t d){ std::this_thread::sleep_for((factor * d * a.d) * 1_ns ); }, a); 72 | CPM_GLOBAL("global_b", [&b](std::size_t d){ std::this_thread::sleep_for((factor * d * b.d) * 1_ns ); }, b); 73 | CPM_GLOBAL_F("global_c", 74 | [&b](std::size_t d){ std::this_thread::sleep_for((factor * d * b.d) * 1_ns ); }, 75 | [](std::size_t d){ return d * d; }, b); 76 | 77 | CPM_GLOBAL_P(VALUES_POLICY(1,2,3), "global_a", [&a](std::size_t d){ std::this_thread::sleep_for((factor * d * a.d) * 1_ns ); }, a); 78 | CPM_GLOBAL_FP(VALUES_POLICY(1,2,3), "global_a", [&a](std::size_t d){ std::this_thread::sleep_for((factor * d * a.d) * 1_ns ); }, 79 | [](std::size_t d){ return 2 * d; }, a, b); 80 | } 81 | 82 | CPM_BENCH() { 83 | CPM_TWO_PASS("2p_a", 84 | [](std::size_t d){ return std::make_tuple(test{d}); }, 85 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * (d + d2.d)) * 1_ns ); } 86 | ); 87 | 88 | CPM_TWO_PASS("2p_b", 89 | [](std::size_t d){ return std::make_tuple(test{d}); }, 90 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 2 * (d + d2.d)) * 1_ns ); } 91 | ); 92 | 93 | CPM_TWO_PASS("2p_c", 94 | [](std::size_t d){ return std::make_tuple(test{d}); }, 95 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 2 * (d + d2.d)) * 1_ns ); }, 96 | [](std::size_t d){ return 2 * d; } 97 | ); 98 | 99 | CPM_TWO_PASS_NS_P( 100 | NARY_POLICY(STD_STOP_POLICY, STD_STOP_POLICY), 101 | "2p_b_n", 102 | [](auto d1, auto /*d2*/){ return std::make_tuple(test{d1}); }, 103 | [](test& d2){ std::this_thread::sleep_for((factor * 2 * (d2.d + d2.d + d2.d)) * 1_ns ); } 104 | ); 105 | 106 | CPM_TWO_PASS_NS_P( 107 | NARY_POLICY(STD_STOP_POLICY, STD_STOP_POLICY), 108 | "2p_b_n_f", 109 | [](auto d1, auto /*d2*/){ return std::make_tuple(test{d1}); }, 110 | [](test& d2){ std::this_thread::sleep_for((factor * 2 * (d2.d + d2.d + d2.d)) * 1_ns ); }, 111 | [](std::size_t d1, std::size_t d2){ return 2 * d1 + d2; } 112 | ); 113 | } 114 | 115 | CPM_DIRECT_BENCH_TWO_PASS("2p_d", 116 | [](std::size_t d){ return std::make_tuple(test{d}); }, 117 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); } 118 | ) 119 | 120 | CPM_DIRECT_BENCH_TWO_PASS("2p_e", 121 | [](std::size_t d){ return std::make_tuple(test{d}); }, 122 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); }, 123 | [](std::size_t d1){ return 2 * d1 + d1 / 2; } 124 | ) 125 | 126 | CPM_DIRECT_BENCH_TWO_PASS_NS_P( 127 | NARY_POLICY(VALUES_POLICY(1,2), VALUES_POLICY(3,4)), 128 | "2p_c_n", 129 | [](auto d1, auto /*d2*/){ return std::make_tuple(test{d1}); }, 130 | [](test& d2){ std::this_thread::sleep_for((factor * 2 * (d2.d + d2.d + d2.d)) * 1_ns ); } 131 | ) 132 | 133 | CPM_DIRECT_BENCH_TWO_PASS_NS_P( 134 | NARY_POLICY(VALUES_POLICY(1,2), VALUES_POLICY(3,4)), 135 | "2p_c_n_f", 136 | [](auto d1, auto /*d2*/){ return std::make_tuple(test{d1}); }, 137 | [](test& d2){ std::this_thread::sleep_for((factor * 2 * (d2.d + d2.d + d2.d)) * 1_ns ); }, 138 | [](std::size_t d1, std::size_t d2){ return 2 * d1 + d2; } 139 | ) 140 | 141 | CPM_SECTION("mmul") 142 | CPM_SIMPLE("std", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 9_ns ); }); 143 | CPM_SIMPLE("fast", [](std::size_t d){ std::this_thread::sleep_for((factor * (d / 3)) * 1_ns ); }); 144 | CPM_SIMPLE("common", [](std::size_t d){ std::this_thread::sleep_for((factor * (d / 2)) * 3_ns ); }); 145 | } 146 | 147 | CPM_SECTION("conv") 148 | CPM_TWO_PASS("std", 149 | [](std::size_t d){ return std::make_tuple(test{d}); }, 150 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 5 * (d + d2.d)) * 1_ns ); } 151 | ); 152 | 153 | CPM_TWO_PASS("fast", 154 | [](std::size_t d){ return std::make_tuple(test{d}); }, 155 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); } 156 | ); 157 | } 158 | 159 | CPM_DIRECT_SECTION_TWO_PASS("conv2", 160 | CPM_SECTION_INIT([](std::size_t d){ return std::make_tuple(test{d}); }), 161 | CPM_SECTION_FUNCTOR("std", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 5 * (d + d2.d)) * 1_ns ); }), 162 | CPM_SECTION_FUNCTOR("fast", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); }) 163 | ) 164 | 165 | CPM_DIRECT_SECTION_TWO_PASS_F("conv2", 166 | FLOPS([](auto d){ return 2 * d; }), 167 | CPM_SECTION_INIT([](std::size_t d){ return std::make_tuple(test{d}); }), 168 | CPM_SECTION_FUNCTOR("std", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 5 * (d + d2.d)) * 1_ns ); }), 169 | CPM_SECTION_FUNCTOR("fast", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); }) 170 | ) 171 | 172 | CPM_DIRECT_SECTION_TWO_PASS_P("conv3", 173 | VALUES_POLICY(1,2,3,4,5,6,7,8,9,10), 174 | CPM_SECTION_INIT([](std::size_t d){ return std::make_tuple(test{d}); }), 175 | CPM_SECTION_FUNCTOR("std", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 5 * (d + d2.d)) * 1_ns ); }), 176 | CPM_SECTION_FUNCTOR("fast", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); }) 177 | ) 178 | 179 | CPM_DIRECT_SECTION_TWO_PASS_PF("conv3", 180 | VALUES_POLICY(1,2,3,4,5,6,7,8,9,10), 181 | FLOPS([](auto d){ return 2 * d; }), 182 | CPM_SECTION_INIT([](std::size_t d){ return std::make_tuple(test{d}); }), 183 | CPM_SECTION_FUNCTOR("std", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 5 * (d + d2.d)) * 1_ns ); }), 184 | CPM_SECTION_FUNCTOR("fast", [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); }) 185 | ) 186 | 187 | CPM_SECTION_OF("fft",11,51, [](std::size_t d){ return 2 * d; }) 188 | test a{3}; 189 | test b{5}; 190 | CPM_GLOBAL("std", [&a](std::size_t d){ std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 191 | CPM_GLOBAL("mkl", [&b](std::size_t d){ std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 192 | } 193 | 194 | CPM_SECTION_PO("gevv [tag1]", NARY_POLICY(STD_STOP_POLICY), 9, 49) 195 | test a{3}; 196 | test b{5}; 197 | CPM_GLOBAL("std", [&a](auto d){ std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 198 | CPM_GLOBAL("mkl", [&b](auto d){ std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 199 | } 200 | 201 | CPM_SECTION_P("gemm [tag1][tag2]", NARY_POLICY(STD_STOP_POLICY, STD_STOP_POLICY)) 202 | test a{3}; 203 | test b{5}; 204 | CPM_GLOBAL("std", [&a](auto d1, auto d2){ auto d = d1 + d2; std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 205 | CPM_GLOBAL("mkl", [&b](auto d1, auto d2){ auto d = d1 + 2 * d2; std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 206 | } 207 | 208 | CPM_SECTION_PF("gemm [tag2][tag3]", NARY_POLICY(STD_STOP_POLICY, STD_STOP_POLICY), [](auto d1, auto d2){ return d1 * 2 * d2; }) 209 | test a{3}; 210 | test b{5}; 211 | CPM_GLOBAL("std", [&a](auto d1, auto d2){ auto d = d1 + d2; std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 212 | CPM_GLOBAL("mkl", [&b](auto d1, auto d2){ auto d = d1 + 2 * d2; std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 213 | } 214 | 215 | CPM_SECTION_P("mmul [tag1][tag3]", NARY_POLICY(STD_STOP_POLICY, VALUES_POLICY(1,2,3,4,5,6), VALUES_POLICY(2,4,8,16,32,64))) 216 | test a{3}; 217 | test b{5}; 218 | CPM_GLOBAL("std", [&a](auto d1, auto d2, auto d3){ auto d = d1 + 2 * d2 + 4 * d3; std::this_thread::sleep_for(factor * d * 1_ns ); }, a); 219 | CPM_GLOBAL("mkl", [&a](auto d1, auto d2, auto d3){ auto d = d1 + d2 + d3; std::this_thread::sleep_for(factor * d * 1_ns ); }, b); 220 | CPM_GLOBAL("bla", [&a](auto d1, auto d2, auto d3){ auto d = d1 + 2 * d2 + 2 * d3; std::this_thread::sleep_for(factor * d * 1_ns ); }, a, b); 221 | } 222 | -------------------------------------------------------------------------------- /examples/simple.cpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #define CPM_PROPAGATE_TUPLE 9 | #define CPM_SECTION_FLOPS 10 | #include "cpm/cpm.hpp" 11 | 12 | #include 13 | 14 | constexpr const double factor = 1.1; 15 | 16 | constexpr std::chrono::nanoseconds operator ""_ns(unsigned long long us){ 17 | return std::chrono::nanoseconds(us); 18 | } 19 | 20 | struct test { std::size_t d; }; 21 | void randomize(test&){} 22 | void random_init(test&){} 23 | 24 | int main(){ 25 | cpm::benchmark<> bench("Test benchmark", "./results"); 26 | 27 | ++bench.warmup; 28 | ++bench.steps; 29 | 30 | bench.section_mflops = true; 31 | 32 | bench.begin(); 33 | 34 | bench.measure_once("once_a", [](){ std::this_thread::sleep_for((factor * 666) * 1_ns ); }); 35 | bench.measure_once("once_b", [](){ std::this_thread::sleep_for((factor * 666) * 1_ns ); }, [](){ return 33; }); 36 | 37 | bench.measure_simple("simple_a", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 1_ns ); }); 38 | bench.measure_simple("simple_b", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }); 39 | bench.measure_simple("simple_c", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 2_ns ); }, [](std::size_t d){ return 2 * d; }); 40 | 41 | bench.measure_simple, cpm::values_policy<2,4,8,16,32,64>>>("simple_a_n", [](auto d){ std::this_thread::sleep_for((factor * std::get<0>(d)) * 1_ns ); }); 42 | bench.measure_simple>>("simple_b_n", [](auto d){ std::this_thread::sleep_for((factor * std::get<0>(d)) * 2_ns ); }); 43 | bench.measure_simple, cpm::values_policy<2,4,8,16,32,64>>>("simple_c_n", 44 | [](auto d){ std::this_thread::sleep_for((factor * std::get<0>(d)) * 2_ns ); }, 45 | [](auto d){ return 2 * std::get<1>(d) + std::get<0>(d); } 46 | ); 47 | 48 | test a{3}; 49 | test b{5}; 50 | bench.measure_global("global_a", [&a](std::size_t d){ std::this_thread::sleep_for((factor * d * a.d) * 1_ns ); }, a); 51 | bench.measure_global("global_b", [&b](std::size_t d){ std::this_thread::sleep_for((factor * d * b.d) * 1_ns ); }, b); 52 | bench.measure_global_flops("global_c", 53 | [&b](std::size_t d){ std::this_thread::sleep_for((factor * d * b.d) * 1_ns ); }, 54 | [](std::size_t d){ return 3 * d; }, b); 55 | 56 | bench.measure_global, cpm::values_policy<2,4,8,16,32,64>>>("global_a_n", 57 | [&a](auto d){ std::this_thread::sleep_for((factor * std::get<0>(d) * a.d + std::get<1>(d)) * 1_ns ); }, a); 58 | bench.measure_global, cpm::values_policy<2,4,8,16,32,64>>>("global_b_n", 59 | [&b](auto d){ std::this_thread::sleep_for((factor * std::get<0>(d) * std::get<1>(d) * b.d) * 1_ns ); }, b); 60 | bench.measure_global_flops, cpm::values_policy<2,4,8,16,32,64>>>("global_c_n", 61 | [&b](auto d){ std::this_thread::sleep_for((factor * std::get<0>(d) * std::get<1>(d) * b.d) * 1_ns ); }, 62 | [](auto d){ return 3 * std::get<0>(d) + 2 * std::get<1>(d); }, b); 63 | 64 | bench.measure_two_pass("2p_a", 65 | [](std::size_t d){ return std::make_tuple(test{d}); }, 66 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * (d + d2.d)) * 1_ns ); } 67 | ); 68 | 69 | bench.measure_two_pass("2p_b", 70 | [](std::size_t d){ return std::make_tuple(test{d}); }, 71 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 2 * (d + d2.d)) * 1_ns ); } 72 | ); 73 | 74 | bench.measure_two_pass("2p_c", 75 | [](std::size_t d){ return std::make_tuple(test{d}); }, 76 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 2 * (d + d2.d)) * 1_ns ); }, 77 | [](std::size_t d){ return 2 * d; } 78 | ); 79 | 80 | bench.measure_two_pass>("2p_b_n", 81 | [](auto dd){ return std::make_tuple(test{std::get<0>(dd)}); }, 82 | [](auto dd, test& d2){ std::this_thread::sleep_for((factor * 2 * (std::get<0>(dd) + std::get<1>(dd) + d2.d)) * 1_ns ); } 83 | ); 84 | 85 | bench.measure_two_pass>("2p_c_n", 86 | [](auto dd){ return std::make_tuple(test{std::get<0>(dd)}); }, 87 | [](auto dd, test& d2){ std::this_thread::sleep_for((factor * 2 * (std::get<0>(dd) + std::get<1>(dd) + d2.d)) * 1_ns ); }, 88 | [](auto dd) { return std::get<0>(dd) * std::get<0>(dd) * std::get<1>(dd); } 89 | ); 90 | 91 | { 92 | auto sec = bench.multi("mmul"); 93 | 94 | sec.measure_simple("std", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 9_ns ); }); 95 | sec.measure_simple("fast", [](std::size_t d){ std::this_thread::sleep_for((factor * (d / 3)) * 1_ns ); }); 96 | sec.measure_simple("common", [](std::size_t d){ std::this_thread::sleep_for((factor * (d / 2)) * 3_ns ); }); 97 | } 98 | 99 | { 100 | auto sec = bench.multi("mmul_flops", [](std::size_t d) { return 3 * d; }); 101 | 102 | sec.measure_simple("std", [](std::size_t d){ std::this_thread::sleep_for((factor * d) * 9_ns ); }); 103 | sec.measure_simple("fast", [](std::size_t d){ std::this_thread::sleep_for((factor * (d / 3)) * 1_ns ); }); 104 | sec.measure_simple("common", [](std::size_t d){ std::this_thread::sleep_for((factor * (d / 2)) * 3_ns ); }); 105 | } 106 | 107 | { 108 | auto sec = bench.multi("mega"); 109 | 110 | sec.measure_once("std", [](){ std::this_thread::sleep_for((factor * 999) * 9_ns ); }); 111 | sec.measure_once("fast", [](){ std::this_thread::sleep_for((factor * 2222) * 1_ns ); }); 112 | sec.measure_once("common", [](){ std::this_thread::sleep_for((factor * 4444) * 3_ns ); }); 113 | } 114 | 115 | { 116 | auto sec = bench.multi("conv"); 117 | 118 | sec.warmup = 20; 119 | sec.steps = 100; 120 | 121 | sec.measure_two_pass("std", 122 | [](std::size_t d){ return std::make_tuple(test{d}); }, 123 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 5 * (d + d2.d)) * 1_ns ); } 124 | ); 125 | 126 | sec.measure_two_pass("fast", 127 | [](std::size_t d){ return std::make_tuple(test{d}); }, 128 | [](std::size_t d, test& d2){ std::this_thread::sleep_for((factor * 3 * (d + d2.d)) * 1_ns ); } 129 | ); 130 | } 131 | 132 | { 133 | auto sec = bench.multi("fft"); 134 | 135 | test a{3}; 136 | test b{5}; 137 | sec.measure_global("std", [&a](std::size_t d){ std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 138 | sec.measure_global("mkl", [&b](std::size_t d){ std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 139 | } 140 | 141 | { 142 | auto sec = bench.multi>("gevv"); 143 | 144 | test a{3}; 145 | test b{5}; 146 | sec.measure_global("std", [&a](std::tuple dd){ auto d = std::get<0>(dd); std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 147 | sec.measure_global("mkl", [&b](std::tuple dd){ auto d = std::get<0>(dd); std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 148 | } 149 | 150 | { 151 | auto sec = bench.multi>("gemm"); 152 | 153 | test a{3}; 154 | test b{5}; 155 | sec.measure_global("std", [&a](std::tuple dd){ auto d = std::get<0>(dd) + std::get<1>(dd); std::this_thread::sleep_for((factor * d * (d % a.d)) * 1_ns ); }, a); 156 | sec.measure_global("mkl", [&b](std::tuple dd){ auto d = std::get<0>(dd) + 2 * std::get<1>(dd); std::this_thread::sleep_for((factor * d * (d % b.d)) * 1_ns ); }, b); 157 | } 158 | 159 | { 160 | auto sec = bench.multi, cpm::values_policy<2,4,8,16,32,64>>>("mamul"); 161 | 162 | test a{3}; 163 | test b{5}; 164 | sec.measure_global("std", [&a](auto dd){ auto d = std::get<0>(dd) + 2 * std::get<1>(dd) + 4 * std::get<2>(dd); std::this_thread::sleep_for(factor * d * 1_ns ); }, a); 165 | sec.measure_global("mkl", [&a](auto dd){ auto d = std::get<0>(dd) + std::get<1>(dd) + std::get<2>(dd); std::this_thread::sleep_for(factor * d * 1_ns ); }, b); 166 | sec.measure_global("bla", [&a](auto dd){ auto d = std::get<0>(dd) + 2 * std::get<1>(dd) + 2 * std::get<2>(dd); std::this_thread::sleep_for(factor * d * 1_ns ); }, a, b); 167 | } 168 | 169 | { 170 | auto sec = bench.multi, cpm::values_policy<2,4,8,16,32,64>>>( 171 | "mamul_flops", [](auto dd) { return 2 * std::get<0>(dd) * std::get<1>(dd) * std::get<2>(dd) - std::get<0>(dd) * std::get<2>(dd); }); 172 | 173 | test a{3}; 174 | test b{5}; 175 | sec.measure_global("std", [&a](auto dd){ auto d = std::get<0>(dd) + 2 * std::get<1>(dd) + 4 * std::get<2>(dd); std::this_thread::sleep_for(factor * d * 1_ns ); }, a); 176 | sec.measure_global("mkl", [&a](auto dd){ auto d = std::get<0>(dd) + std::get<1>(dd) + std::get<2>(dd); std::this_thread::sleep_for(factor * d * 1_ns ); }, b); 177 | sec.measure_global("bla", [&a](auto dd){ auto d = std::get<0>(dd) + 2 * std::get<1>(dd) + 2 * std::get<2>(dd); std::this_thread::sleep_for(factor * d * 1_ns ); }, a, b); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /include/cpm/bootstrap_tabs_theme.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_BOOTSTRAP_TABS_THEME_HPP 9 | #define CPM_BOOTSTRAP_TABS_THEME_HPP 10 | 11 | #include "cpm/io.hpp" 12 | #include "cpm/data.hpp" 13 | #include "cpm/bootstrap_theme.hpp" 14 | 15 | namespace cpm { 16 | 17 | struct bootstrap_tabs_theme : bootstrap_theme { 18 | std::size_t uid = 0; 19 | std::size_t tid = 0; 20 | 21 | std::vector matches; 22 | 23 | bootstrap_tabs_theme(const reports_data& data, cxxopts::Options& options, std::ostream& stream, std::string compiler, std::string configuration) : bootstrap_theme(data, options, stream, compiler, configuration) {} 24 | 25 | void before_result(const std::string& title, bool sub, const std::vector& documents){ 26 | bootstrap_theme::before_result(title, sub, documents); 27 | 28 | stream << "
\n"; 29 | stream << "
\n"; 30 | stream << "
    \n"; 31 | 32 | std::size_t tab_id = 0; 33 | 34 | stream 35 | << "
  • Last results
  • \n"; 37 | ++tab_id; 38 | 39 | if(documents.size() > 1 && !options.count("disable-time")){ 40 | stream 41 | << "
  • Results over time
  • \n"; 43 | ++tab_id; 44 | } 45 | 46 | if(data.compilers.size() > 1 && !options.count("disable-compiler")){ 47 | stream 48 | << "
  • Compilers
  • \n"; 50 | ++tab_id; 51 | } 52 | 53 | if(data.configurations.size() > 1 && !options.count("disable-configuration")){ 54 | stream 55 | << "
  • Configurations
  • \n"; 57 | ++tab_id; 58 | } 59 | 60 | if(!options.count("disable-summary")){ 61 | stream 62 | << "
  • Summary
  • \n"; 64 | ++tab_id; 65 | } 66 | 67 | stream << "
\n"; 68 | stream << "
\n"; 69 | 70 | matches.clear(); 71 | 72 | if(uid == 0){ 73 | stream << "\n"; 83 | } 84 | } 85 | 86 | void after_result(){ 87 | stream << "\n"; 103 | 104 | stream << "
\n"; 105 | stream << "
\n"; 106 | stream << "
\n"; 107 | 108 | bootstrap_theme::after_result(); 109 | 110 | ++uid; 111 | tid = 0; 112 | } 113 | 114 | virtual void start_column(const std::string& /*style*/) override { 115 | auto tab_id = std::string("tab_") + std::to_string(uid) + "_" + std::to_string(tid); 116 | 117 | if(tid == 0){ 118 | stream << "
\n"; 119 | } else { 120 | stream << "
\n"; 121 | } 122 | 123 | matches.push_back(tab_id); 124 | 125 | ++tid; 126 | } 127 | 128 | virtual void close_column(){ 129 | stream << "
\n"; 130 | } 131 | 132 | void before_graph(std::size_t id){ 133 | start_column(""); 134 | 135 | stream << "
\n"; 136 | } 137 | 138 | template 139 | bootstrap_tabs_theme& operator<<(const T& v){ 140 | stream << v; 141 | return *this; 142 | } 143 | }; 144 | 145 | } //end of namespace cpm 146 | 147 | #endif //CPM_BOOTSTRAP_THEME_HPP 148 | -------------------------------------------------------------------------------- /include/cpm/bootstrap_theme.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_BOOTSTRAP_THEME_HPP 9 | #define CPM_BOOTSTRAP_THEME_HPP 10 | 11 | #include "cpm/io.hpp" 12 | #include "cpm/data.hpp" 13 | 14 | namespace cpm { 15 | 16 | struct bootstrap_theme { 17 | const reports_data& data; 18 | cxxopts::Options& options; 19 | std::ostream& stream; 20 | std::string current_compiler; 21 | std::string current_configuration; 22 | 23 | std::size_t current_column = 0; 24 | 25 | bootstrap_theme(const reports_data& data, cxxopts::Options& options, std::ostream& stream, std::string compiler, std::string configuration) 26 | : data(data), options(options), stream(stream), current_compiler(std::move(compiler)), current_configuration(std::move(configuration)) {} 27 | 28 | void include(){ 29 | stream << "\n"; 30 | stream << "\n"; 31 | stream << "\n"; 32 | 33 | stream << R"=====( 34 | 45 | )====="; 46 | } 47 | 48 | void header(){ 49 | stream << R"=====( 50 | 69 | )====="; 70 | } 71 | 72 | void footer(){ 73 | stream << R"=====( 74 |
75 |
76 |
77 |
78 |

Generated with CPM

79 |
80 |
81 |
82 |
83 | )====="; 84 | 85 | if(options.count("pages")){ 86 | stream << "\n"; 87 | stream << "\n"; 88 | } 89 | } 90 | 91 | void before_information(std::string name){ 92 | stream << "
\n"; 93 | stream << "
\n"; 94 | stream << "

" << name << "

\n"; 95 | stream << "
\n"; 96 | stream << "
\n"; 97 | stream << "
    \n"; 98 | } 99 | 100 | void after_information(){ 101 | stream << "
\n"; 102 | stream << "
\n"; //col-xs-3 103 | 104 | if(data.compilers.size() > 1 || data.configurations.size() > 1){ 105 | stream << "
\n"; 106 | } 107 | } 108 | 109 | void compiler_buttons(){ 110 | stream << R"=====(
)====="; 111 | stream << R"=====(
)====="; 112 | 113 | stream << R"=====(Select compiler: )====="; 114 | stream << R"=====(
)====="; 115 | 116 | if(options.count("pages")){ 117 | for(auto& compiler : data.compilers){ 118 | auto file = cpm::filify(compiler, current_configuration, data.sub_part); 119 | if(compiler == current_compiler){ 120 | stream << "" << compiler << "\n"; 121 | } else { 122 | stream << "" << compiler << "\n"; 123 | } 124 | } 125 | } else { 126 | for(auto& compiler : data.compilers){ 127 | if(compiler == current_compiler){ 128 | stream << "" << compiler << "\n"; 129 | } else { 130 | stream << "" << compiler << "\n"; 131 | } 132 | } 133 | } 134 | 135 | stream << "
\n"; //btn-group 136 | 137 | stream << "
\n"; //col-xs-12 138 | stream << "
\n"; //row 139 | } 140 | 141 | void configuration_buttons(){ 142 | if(data.compilers.size() > 1){ 143 | stream << R"=====(
)====="; 144 | } else { 145 | stream << R"=====(
)====="; 146 | } 147 | 148 | stream << R"=====(
)====="; 149 | 150 | stream << R"=====(Select configuration: )====="; 151 | 152 | stream << R"=====(
)====="; 153 | 154 | if(options.count("pages")){ 155 | for(auto& configuration : data.configurations){ 156 | auto file = cpm::filify(current_compiler, configuration, data.sub_part); 157 | if(configuration == current_configuration){ 158 | stream << "" << configuration << "\n"; 159 | } else { 160 | stream << "" << configuration << "\n"; 161 | } 162 | } 163 | } else { 164 | for(auto& configuration : data.configurations){ 165 | if(configuration == current_configuration){ 166 | stream << "" << configuration << "\n"; 167 | } else { 168 | stream << "" << configuration << "\n"; 169 | } 170 | } 171 | } 172 | 173 | stream << "
\n"; //btn-group 174 | stream << "
\n"; //col-xs-12 175 | stream << "
\n"; //row 176 | } 177 | 178 | void after_buttons(){ 179 | if(data.compilers.size() > 1 || data.configurations.size() > 1){ 180 | stream << "
\n"; //col-xs-9 181 | } 182 | 183 | stream << "
\n"; //row 184 | stream << "
\n"; //container-fluid 185 | stream << "
\n"; //jumbotron 186 | 187 | if(options.count("pages")){ 188 | stream << R"=====( 189 |
190 |
191 |
192 | 201 |
202 |
203 |
204 | )====="; 205 | } else { 206 | stream << "
\n"; 207 | } 208 | } 209 | 210 | virtual void start_column(const std::string& style = ""){ 211 | std::size_t columns = 1; //Always the first grapah 212 | 213 | if(data.documents.size() > 1 && !options.count("disable-time")){ 214 | ++columns; 215 | } 216 | 217 | if(data.compilers.size() > 1 && !options.count("disable-compiler")){ 218 | ++columns; 219 | } 220 | 221 | if(data.configurations.size() > 1 && !options.count("disable-configuration")){ 222 | ++columns; 223 | } 224 | 225 | if(!options.count("disable-summary")){ 226 | ++columns; 227 | } 228 | 229 | if(columns < 4){ 230 | stream << "
\n"; 231 | } else if(columns == 4){ 232 | if(current_column == 2){ 233 | stream << "
\n"; 234 | stream << "
\n"; 235 | } 236 | 237 | stream << "
\n"; 238 | } else if(columns == 5){ 239 | if(current_column == 3){ 240 | stream << "
\n"; 241 | stream << "
\n"; 242 | } 243 | 244 | if(current_column < 3){ 245 | stream << "
\n"; 246 | } else if(current_column == 3){ 247 | stream << "
\n"; 248 | } else if(current_column == 4){ 249 | stream << "
\n"; 250 | } 251 | } 252 | 253 | ++current_column; 254 | } 255 | 256 | virtual void close_column(){ 257 | stream << "
\n"; 258 | } 259 | 260 | void before_graph(std::size_t id){ 261 | start_column(); 262 | 263 | stream << "
\n"; 264 | } 265 | 266 | void after_graph(){ 267 | close_column(); 268 | } 269 | 270 | void before_result(const std::string& title, bool sub, const std::vector& /*documents*/){ 271 | stream << "
\n"; 272 | stream << "

" << title << "

\n"; 273 | stream << "
\n"; 274 | 275 | if(sub){ 276 | stream << "
\n"; 277 | } else { 278 | stream << "
\n"; 279 | } 280 | 281 | current_column = 0; 282 | } 283 | 284 | void after_result(){ 285 | stream << "
\n"; 286 | } 287 | 288 | void before_sub_graphs(std::size_t id, std::vector graphs){ 289 | start_column("style=\"align-self: flex-start; \""); 290 | 291 | stream << "
\n"; 292 | stream << "
    \n"; 293 | 294 | std::string active = "class=\"active\""; 295 | std::size_t sub = 0; 296 | for(auto& g : graphs){ 297 | auto sub_id = std::string("sub") + std::to_string(id) + "-" + std::to_string(sub++); 298 | stream << "
  • " << g << "
  • \n"; 300 | active = ""; 301 | } 302 | 303 | stream << "
\n"; 304 | stream << "
\n"; 305 | } 306 | 307 | void after_sub_graphs(){ 308 | stream << "
\n"; 309 | stream << "
\n"; 310 | 311 | close_column(); 312 | } 313 | 314 | void before_sub_graph(std::size_t id, std::size_t sub){ 315 | auto sub_id = std::string("sub") + std::to_string(id) + "-" + std::to_string(sub); 316 | std::string active; 317 | if(sub == 0){ 318 | active = " active"; 319 | } 320 | stream << "
\n"; 321 | 322 | stream << "
\n"; 323 | } 324 | 325 | void after_sub_graph(){ 326 | stream << "
\n"; 327 | } 328 | 329 | void before_summary(){ 330 | start_column(); 331 | 332 | stream << "\n"; 333 | } 334 | 335 | void after_summary(){ 336 | stream << "
\n"; 337 | 338 | close_column(); 339 | } 340 | 341 | void before_sub_summary(std::size_t id, std::size_t sub){ 342 | auto sub_id = std::string("sub") + std::to_string(id) + "-" + std::to_string(sub); 343 | std::string active; 344 | if(sub == 0){ 345 | active = " active"; 346 | } 347 | 348 | stream << "
\n"; 349 | stream << "\n"; 350 | } 351 | 352 | void after_sub_summary(){ 353 | stream << "
\n"; 354 | stream << "
\n"; 355 | } 356 | 357 | void cell(const std::string& v){ 358 | stream << "" << v << "\n"; 359 | } 360 | 361 | void red_cell(const std::string& v){ 362 | stream << ""<< v << "\n"; 363 | } 364 | 365 | void green_cell(const std::string& v){ 366 | stream << "" << v << "\n"; 367 | } 368 | 369 | template 370 | bootstrap_theme& operator<<(const T& v){ 371 | stream << v; 372 | return *this; 373 | } 374 | }; 375 | 376 | } //end of namespace cpm 377 | 378 | #endif //CPM_BOOTSTRAP_THEME_HPP 379 | -------------------------------------------------------------------------------- /include/cpm/compat.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_COMPAT_HPP 9 | #define CPM_COMPAT_HPP 10 | 11 | #ifndef cpp14_constexpr 12 | #ifdef __clang__ 13 | #define cpp14_constexpr constexpr 14 | #else 15 | #define cpp14_constexpr 16 | #endif 17 | #endif 18 | 19 | //Fix an assertion failed in Intel C++ Compiler 20 | 21 | #ifndef intel_decltype_auto 22 | #ifdef __INTEL_COMPILER 23 | #define intel_decltype_auto auto 24 | #else 25 | #define intel_decltype_auto decltype(auto) 26 | #endif 27 | #endif 28 | 29 | #endif //CPM_COMPAT_HPP 30 | -------------------------------------------------------------------------------- /include/cpm/compiler.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_COMPILER_HPP 9 | #define CPM_COMPILER_HPP 10 | 11 | #define STRINGIFY(x) #x 12 | #define TO_STRING(x) STRINGIFY(x) 13 | 14 | #if defined(__clang__) 15 | #define COMPILER "clang" 16 | #define COMPILER_FULL "clang-" TO_STRING(__clang_major__) "." TO_STRING(__clang_minor__) "." TO_STRING(__clang_patchlevel__) 17 | #elif defined(__ICC) || defined(__INTEL_COMPILER) 18 | #define COMPILER "icc" 19 | #define COMPILER_FULL "icc-" TO_STRING(__INTEL_COMPILER) 20 | #elif defined(__GNUC__) || defined(__GNUG__) 21 | #define COMPILER "gcc" 22 | #define COMPILER_FULL "gcc-" TO_STRING(__GNUC__) "." TO_STRING(__GNUC_MINOR__) "." TO_STRING(__GNUC_PATCHLEVEL__) 23 | #else 24 | #define COMPILER "unknown" 25 | #define COMPILER_FULL "unknown" 26 | #endif 27 | 28 | #endif //CPM_COMPILER_HPP 29 | -------------------------------------------------------------------------------- /include/cpm/config.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_CONFIG_HPP 9 | #define CPM_CONFIG_HPP 10 | 11 | namespace cpm { 12 | 13 | #ifdef CPM_STEP_ESTIMATION_MIN 14 | static constexpr const double step_estimation_min = CPM_STEP_ESTIMATION_MIN; //seconds 15 | #else 16 | static constexpr const double step_estimation_min = 0.1; //seconds 17 | #endif 18 | 19 | #ifdef CPM_RUNTIME_TARGET 20 | static constexpr const double runtime_target = CPM_RUNTIME_TARGET; //seconds 21 | #else 22 | static constexpr const double runtime_target = 1.0; //seconds 23 | #endif 24 | 25 | } //end of namespace cpm 26 | 27 | #endif //CPM_CONFIG_HPP 28 | -------------------------------------------------------------------------------- /include/cpm/cpm.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_CPM_HPP 9 | #define CPM_CPM_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "compiler.hpp" 21 | #include "duration.hpp" 22 | #include "random.hpp" 23 | #include "policy.hpp" 24 | #include "io.hpp" 25 | #include "json.hpp" 26 | #include "config.hpp" 27 | 28 | namespace cpm { 29 | 30 | inline void prologue(){ 31 | #ifdef CPM_PROLOGUE 32 | CPM_PROLOGUE 33 | #endif 34 | } 35 | 36 | inline std::string& trim(std::string& s) { 37 | //rtrim 38 | s.erase(std::find_if(s.rbegin(), s.rend(), 39 | [](auto c) { 40 | return !std::isspace(c); 41 | }) 42 | .base(), 43 | s.end()); 44 | //ltrim 45 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), 46 | [](auto c) { 47 | return !std::isspace(c); 48 | })); 49 | return s; 50 | } 51 | 52 | inline std::string extract_title(const std::string& title){ 53 | std::string clean{title.begin(), title.begin() + title.find('[')}; 54 | trim(clean); 55 | return clean; 56 | } 57 | 58 | inline std::vector extract_tags(const std::string& title, bool strict){ 59 | std::string filter = title; 60 | trim(filter); 61 | 62 | std::vector tags; 63 | 64 | std::size_t end = 0; 65 | 66 | if(strict && filter.find('[') > 0){ 67 | return {}; 68 | } else if(end == std::string::npos){ 69 | return {}; 70 | } else { 71 | std::size_t start; 72 | while((start = filter.find('[', end)) != std::string::npos){ 73 | end = filter.find(']', start); 74 | 75 | if(end == std::string::npos){ 76 | return {}; 77 | } 78 | 79 | std::string tag(filter.begin() + start + 1, filter.begin() + end); 80 | 81 | trim(tag); 82 | 83 | tags.emplace_back(std::move(tag)); 84 | } 85 | } 86 | 87 | return tags; 88 | } 89 | 90 | template = 42> 91 | inline void call_with_data_final(Tuple& data, Functor& functor, std::index_sequence /*indices*/, Args... args){ 92 | functor(args..., std::get(data)...); 93 | } 94 | 95 | template = 42> 96 | inline void call_with_data_final(Tuple& data, Functor& functor, std::index_sequence /*indices*/, Args... /*args*/){ 97 | functor(std::get(data)...); 98 | } 99 | 100 | template 101 | inline void call_with_data(Tuple& data, Functor& functor, std::index_sequence indices, std::size_t arg){ 102 | call_with_data_final(data, functor, indices, arg); 103 | } 104 | 105 | #ifndef CPM_PROPAGATE_TUPLE 106 | template 107 | inline void propagate_call_with_data(Tuple& data, Functor& functor, std::index_sequence indices, std::index_sequence /*i2*/, std::tuple arg){ 108 | call_with_data_final(data, functor, indices, std::get(arg)...); 109 | } 110 | template 111 | inline void call_with_data(Tuple& data, Functor& functor, std::index_sequence indices, std::tuple arg){ 112 | propagate_call_with_data(data, functor, indices, std::make_index_sequence(), arg); 113 | } 114 | #else 115 | template 116 | inline void call_with_data(Tuple& data, Functor& functor, std::index_sequence indices, std::tuple arg){ 117 | call_with_data_final(data, functor, indices, arg); 118 | } 119 | #endif 120 | 121 | template 122 | void call_functor(Functor& functor){ 123 | functor(); 124 | } 125 | 126 | template 127 | void call_functor(Functor& functor, std::size_t d){ 128 | functor(d); 129 | } 130 | 131 | #ifndef CPM_PROPAGATE_TUPLE 132 | template 133 | void propagate_call_functor(Functor& functor, std::tuple d, std::index_sequence /*s*/){ 134 | functor(std::get(d)...); 135 | } 136 | 137 | template 138 | void call_functor(Functor& functor, std::tuple d){ 139 | propagate_call_functor(functor, d, std::make_index_sequence()); 140 | } 141 | #else 142 | template 143 | void call_functor(Functor& functor, std::tuple d){ 144 | functor(d); 145 | } 146 | #endif 147 | 148 | template 149 | auto call_flops(Functor& functor){ 150 | return functor(); 151 | } 152 | 153 | template 154 | auto call_flops(Functor& functor, std::size_t d){ 155 | return functor(d); 156 | } 157 | 158 | #ifndef CPM_PROPAGATE_TUPLE 159 | template 160 | auto propagate_call_flops(Functor& functor, std::tuple d, std::index_sequence /*s*/){ 161 | return functor(std::get(d)...); 162 | } 163 | 164 | template 165 | auto call_flops(Functor& functor, std::tuple d){ 166 | return propagate_call_flops(functor, d, std::make_index_sequence()); 167 | } 168 | #else 169 | template 170 | auto call_flops(Functor& functor, std::tuple d){ 171 | return functor(d); 172 | } 173 | #endif 174 | 175 | template 176 | auto call_init_functor(Functor& functor, std::size_t d){ 177 | return functor(d); 178 | } 179 | 180 | #ifndef CPM_PROPAGATE_TUPLE 181 | template 182 | auto propagate_call_init_functor(Functor& functor, std::tuple d, std::index_sequence /*s*/){ 183 | return functor(std::get(d)...); 184 | } 185 | 186 | template 187 | auto call_init_functor(Functor& functor, std::tuple d){ 188 | return propagate_call_init_functor(functor, d, std::make_index_sequence()); 189 | } 190 | #else 191 | template 192 | auto call_init_functor(Functor& functor, std::tuple d){ 193 | return functor(d); 194 | } 195 | #endif 196 | 197 | inline std::size_t mul_all() { 198 | return 1; 199 | } 200 | 201 | template 202 | inline std::size_t mul_all(std::size_t first, T... args) { 203 | return first * mul_all(args...); 204 | } 205 | 206 | inline std::size_t mul_all(std::size_t first) { 207 | return first; 208 | } 209 | 210 | template 211 | inline std::size_t mul_all(std::tuple tuple, std::index_sequence /*sequence*/) { 212 | return mul_all(std::get(tuple)...); 213 | } 214 | 215 | template 216 | inline std::size_t mul_all(std::tuple tuple) { 217 | return mul_all(tuple, std::make_index_sequence()); 218 | } 219 | 220 | template 221 | struct benchmark; 222 | 223 | struct section_data { 224 | std::string name; 225 | 226 | //TODO This datastructure is probably not ideal 227 | std::vector names; 228 | std::vector sizes; 229 | std::vector sizes_eff; 230 | std::vector> results; 231 | 232 | section_data() = default; 233 | section_data(const section_data&) = default; 234 | section_data& operator=(const section_data&) = default; 235 | section_data(section_data&&) = default; 236 | section_data& operator=(section_data&&) = default; 237 | }; 238 | 239 | template 240 | struct section { 241 | private: 242 | Bench& bench; 243 | Flops flops; 244 | bool enabled; 245 | 246 | section_data data; 247 | 248 | public: 249 | std::size_t warmup = 10; 250 | std::size_t steps = 50; 251 | 252 | section(std::string name, Bench& bench, Flops flops, bool enabled) : bench(bench), flops(flops), enabled(enabled), warmup(bench.warmup), steps(bench.steps) { 253 | data.name = std::move(name); 254 | 255 | if(enabled && bench.standard_report){ 256 | std::cout << std::endl; 257 | } 258 | } 259 | 260 | //Measure once functor (no policy, no randomization) 261 | 262 | template 263 | void measure_once(const std::string& title, Functor functor){ 264 | if(enabled){ 265 | auto duration = bench.measure_only_simple(*this, functor, flops); 266 | report(title, std::size_t(1), duration); 267 | } 268 | } 269 | 270 | //Measure simple functor (no randomization) 271 | 272 | template 273 | void measure_simple(const std::string& title, Functor functor){ 274 | if(enabled){ 275 | bench.template policy_run( 276 | [&title, &functor, this](auto sizes){ 277 | auto duration = bench.measure_only_simple(*this, functor, flops, sizes); 278 | this->report(title, sizes, duration); 279 | return duration; 280 | } 281 | ); 282 | } 283 | } 284 | 285 | //Measure with two-pass functors (init and functor) 286 | 287 | template 288 | void measure_two_pass(const std::string& title, Init init, Functor functor){ 289 | if(enabled){ 290 | bench.template policy_run( 291 | [&title, &functor, &init, this](auto sizes){ 292 | auto duration = bench.template measure_only_two_pass(*this, init, functor, flops, sizes); 293 | this->report(title, sizes, duration); 294 | return duration; 295 | } 296 | ); 297 | } 298 | } 299 | 300 | //measure a function with global references 301 | 302 | template 303 | void measure_global(const std::string& title, Functor functor, T&... references){ 304 | if(enabled){ 305 | bench.template policy_run( 306 | [&title, &functor, &references..., this](auto sizes){ 307 | auto duration = bench.measure_only_global(*this, functor, flops, sizes, references...); 308 | this->report(title, sizes, duration); 309 | return duration; 310 | } 311 | ); 312 | } 313 | } 314 | 315 | ~section(){ 316 | if(bench.standard_report){ 317 | if(data.names.empty()){ 318 | return; 319 | } 320 | 321 | std::vector widths(1 + data.results.size(), 4); 322 | 323 | for(std::size_t i = 0; i < data.sizes.size(); ++i){ 324 | auto s = data.sizes[i]; 325 | 326 | widths[0] = std::max(widths[0], static_cast(s.size())); 327 | } 328 | 329 | auto name = data.name; 330 | trim(name); 331 | auto tags = extract_tags(name, false); 332 | auto title = tags.empty() ? name : extract_title(name); 333 | 334 | widths[0] = std::max(widths[0], static_cast(title.size())); 335 | 336 | for(std::size_t i = 0; i < data.results.size(); ++i){ 337 | for(auto d : data.results[i]){ 338 | if(bench.section_mflops){ 339 | widths[i+1] = std::max(widths[i+1], static_cast(mthroughput_str(d.throughput_f).size())); 340 | } else { 341 | widths[i+1] = std::max(widths[i+1], static_cast(duration_str(d.mean).size())); 342 | } 343 | } 344 | } 345 | 346 | for(std::size_t i = 0; i < data.names.size(); ++i){ 347 | widths[i+1] = std::max(widths[i+1], static_cast(data.names[i].size())); 348 | } 349 | 350 | std::size_t tot_width = 1 + std::accumulate(widths.begin(), widths.end(), 0) + 3 * (1 + data.names.size()); 351 | 352 | std::cout << " " << std::string(tot_width, '-') << std::endl;; 353 | 354 | printf(" | %*s | ", widths[0], title.c_str()); 355 | for(std::size_t i = 0; i < data.names.size(); ++i){ 356 | printf("%*s | ", widths[i+1], data.names[i].c_str()); 357 | } 358 | printf("\n"); 359 | 360 | std::cout << " " << std::string(tot_width, '-') << std::endl;; 361 | 362 | for(std::size_t i = 0; i < data.sizes.size(); ++i){ 363 | auto s = data.sizes[i]; 364 | 365 | printf(" | %*s | ", widths[0], s.c_str()); 366 | 367 | double max = 0; 368 | double min = std::numeric_limits::max(); 369 | 370 | for(std::size_t r = 0; r < data.results.size(); ++r){ 371 | if(i < data.results[r].size()){ 372 | max = std::max(max, data.results[r][i].mean); 373 | min = std::min(min, data.results[r][i].mean); 374 | } 375 | } 376 | 377 | for(std::size_t r = 0; r < data.results.size(); ++r){ 378 | if(i < data.results[r].size()){ 379 | if(data.results[r][i].mean >= 0.99 * static_cast(min) && data.results[r][i].mean <= 1.01 * static_cast(min)){ 380 | std::cout << "\033[0;32m"; 381 | } else if(data.results[r][i].mean >= 0.99 * static_cast(max) && data.results[r][i].mean <= 1.01 * static_cast(max)){ 382 | std::cout << "\033[0;31m"; 383 | } 384 | 385 | if(bench.section_mflops){ 386 | printf("%*s", widths[r+1], mthroughput_str(data.results[r][i].throughput_f).c_str()); 387 | } else { 388 | printf("%*s", widths[r+1], duration_str(data.results[r][i].mean).c_str()); 389 | } 390 | } else { 391 | std::cout << "\033[0;31m"; 392 | printf("%*s", widths[r+1], "*"); 393 | } 394 | 395 | //Reset the color 396 | std::cout << "" << '\033' << "[" << 0 << ";" << 30 << 47 << "m | "; 397 | } 398 | 399 | printf("\n"); 400 | } 401 | 402 | std::cout << " " << std::string(tot_width, '-') << std::endl;; 403 | } 404 | 405 | bench.section_results.push_back(std::move(data)); 406 | } 407 | 408 | private: 409 | template 410 | void report(const std::string& title, Tuple d, measure_result& duration){ 411 | if(data.names.empty() || data.names.back() != title){ 412 | data.names.push_back(title); 413 | data.results.emplace_back(); 414 | } 415 | 416 | if(data.names.size() == 1){ 417 | data.sizes.push_back(size_to_string(d)); 418 | data.sizes_eff.push_back(size_to_eff(d)); 419 | } 420 | 421 | duration.update(size_to_eff(d)); 422 | 423 | data.results.back().push_back(duration); 424 | } 425 | }; 426 | 427 | struct measure_data { 428 | std::string title; 429 | std::vector results; 430 | }; 431 | 432 | template 433 | struct benchmark { 434 | private: 435 | template friend struct section; 436 | std::size_t tests = 0; 437 | std::size_t measures = 0; 438 | std::size_t runs = 0; 439 | 440 | const std::string name; 441 | std::string folder; 442 | std::string tag; 443 | std::string configuration; 444 | std::string final_file; 445 | bool folder_ok = false; 446 | 447 | std::string operating_system; 448 | wall_time_point start_time; 449 | 450 | std::vector results; 451 | std::vector section_results; 452 | 453 | std::string filter; 454 | std::string filter_title; 455 | std::vector filter_tags; 456 | 457 | public: 458 | std::size_t warmup = 10; 459 | std::size_t steps = 50; 460 | 461 | bool standard_report = true; 462 | bool auto_save = true; 463 | bool auto_mkdir = true; 464 | 465 | bool section_mflops = false; 466 | 467 | benchmark(std::string name, std::string f = ".", std::string t = "", std::string c = "") : name(std::move(name)), folder(std::move(f)), tag(std::move(t)), configuration(std::move(c)) { 468 | //Get absolute cwd 469 | if(folder == "" || folder == "."){ 470 | folder = get_cwd(); 471 | } 472 | 473 | //Make it absolute 474 | if(folder.front() != '/'){ 475 | folder = make_absolute(folder); 476 | } 477 | 478 | //Ensure that the results folder exists 479 | if(!folder_exists(folder)){ 480 | if(auto_mkdir){ 481 | if(mkdir(folder.c_str(), 0777)){ 482 | std::cout << "Failed to create the folder" << std::endl; 483 | } else { 484 | folder_ok = true; 485 | } 486 | } else { 487 | std::cout << "Warning: The given folder does not exist or is not a folder" << std::endl; 488 | } 489 | } else { 490 | folder_ok = true; 491 | } 492 | 493 | //Make sure the folder ends with / 494 | if(folder_ok && folder.back() != '/'){ 495 | folder += "/"; 496 | } 497 | 498 | //Select a free file 499 | if(folder_ok){ 500 | auto f = get_free_file(folder); 501 | if(tag.empty()){ 502 | tag = f; 503 | } 504 | final_file = folder + f + ".cpm"; 505 | } 506 | 507 | //Detect the operating system 508 | struct utsname buffer; 509 | if (uname(&buffer) == 0) { 510 | operating_system += buffer.sysname; 511 | operating_system += " "; 512 | operating_system += buffer.machine; 513 | operating_system += " "; 514 | operating_system += buffer.release; 515 | } else { 516 | std::cout << "Warning: Failed to detect operating system" << std::endl; 517 | operating_system = "unknown"; 518 | } 519 | 520 | //Store the time 521 | start_time = wall_clock::now(); 522 | } 523 | 524 | void set_filter(std::string filter){ 525 | this->filter = trim(filter); 526 | 527 | auto open = std::count(filter.begin(), filter.end(), '['); 528 | auto close = std::count(filter.begin(), filter.end(), ']'); 529 | if(open == close && open > 0){ 530 | this->filter_tags = extract_tags(filter, true); 531 | 532 | //Fall back to title if not able to parse the tags 533 | if(filter_tags.empty()){ 534 | this->filter_title = filter; 535 | } 536 | } else { 537 | this->filter_title = filter; 538 | } 539 | 540 | auto_save = false; 541 | } 542 | 543 | bool bench_should_run(std::string title){ 544 | if(filter_title.empty() && filter_tags.empty()){ 545 | return true; 546 | } 547 | 548 | trim(title); 549 | 550 | auto tags = extract_tags(title, false); 551 | auto title_only = tags.empty() ? title : extract_title(title); 552 | 553 | if(!filter_title.empty()){ 554 | return title_only == filter_title; 555 | } else if(!filter_tags.empty()){ 556 | for(auto& tag : tags){ 557 | for(auto& filter_tag : filter_tags){ 558 | if(tag == filter_tag){ 559 | return true; 560 | } 561 | } 562 | } 563 | 564 | return false; 565 | } 566 | 567 | return true; 568 | } 569 | 570 | void begin(){ 571 | if(standard_report){ 572 | std::cout << "Start CPM benchmarks" << std::endl; 573 | if(!folder_ok){ 574 | std::cout << " Impossible to save the results (invalid folder)" << std::endl; 575 | } else if(auto_save){ 576 | std::cout << " Results will be automatically saved in " << final_file << std::endl; 577 | } else { 578 | std::cout << " Results will be saved on-demand in " << final_file << std::endl; 579 | } 580 | 581 | #ifdef CPM_AUTO_STEPS 582 | std::cout << " Number of steps will be automatically computed" << std::endl; 583 | #else 584 | std::cout << " Each test is warmed-up " << warmup << " times" << std::endl; 585 | std::cout << " Each test is repeated " << steps << " times" << std::endl; 586 | #endif 587 | 588 | auto time = wall_clock::to_time_t(start_time); 589 | std::cout << " Time " << std::ctime(&time) << std::endl; 590 | 591 | std::cout << " Tag: " << tag << std::endl; 592 | std::cout << " Configuration: " << configuration << std::endl; 593 | std::cout << " Compiler: " << COMPILER_FULL << std::endl; 594 | std::cout << " Operating System: " << operating_system << std::endl; 595 | 596 | if(!filter_title.empty()){ 597 | std::cout << " Filter by title: " << filter_title << std::endl; 598 | } 599 | 600 | if(!filter_tags.empty()){ 601 | std::cout << " Filter by tags: " 602 | << std::accumulate(filter_tags.begin(), filter_tags.end(), std::string(), 603 | [](auto a, auto b){ return a + "[" + b + "]"; }) 604 | << std::endl; 605 | } 606 | 607 | 608 | std::cout << std::endl; 609 | } 610 | } 611 | 612 | ~benchmark(){ 613 | end(auto_save); 614 | } 615 | 616 | void end(bool save_file = true){ 617 | if(standard_report){ 618 | std::cout << std::endl; 619 | std::cout << "End of CPM benchmarks" << std::endl; 620 | std::cout << " " << tests << " tests have been run" << std::endl; 621 | std::cout << " " << measures << " measures have been taken" << std::endl; 622 | std::cout << " " << runs << " functors calls" << std::endl; 623 | std::cout << std::endl; 624 | } 625 | 626 | if(save_file){ 627 | save(); 628 | } 629 | } 630 | 631 | template 632 | auto multi(const std::string& o_name){ 633 | return multi(o_name, [](auto... args) { return mul_all(args...); }); 634 | } 635 | 636 | template 637 | section, Policy, Flops> multi(const std::string& o_name, Flops&& flops){ 638 | auto name = o_name; 639 | bool rename = false; 640 | std::size_t id = 0; 641 | while(true){ 642 | bool found = false; 643 | 644 | for(auto& section : section_results){ 645 | if(section.name == name){ 646 | name = o_name + "_" + std::to_string(id++); 647 | rename = true; 648 | found = true; 649 | break; 650 | } 651 | } 652 | 653 | if(!found){ 654 | break; 655 | } 656 | } 657 | 658 | if(rename){ 659 | std::cout << "Warning: Section already exists. Renamed in \"" << name << "\"\n"; 660 | } 661 | 662 | return {std::move(name), *this, std::forward(flops), bench_should_run(o_name)}; 663 | } 664 | 665 | std::string check_title(const std::string& o_name){ 666 | auto name = o_name; 667 | trim(name); 668 | auto tags = extract_tags(name, false); 669 | name = tags.empty() ? name : extract_title(name); 670 | 671 | bool rename = false; 672 | std::size_t id = 0; 673 | while(true){ 674 | bool found = false; 675 | 676 | for(auto& data : results){ 677 | if(data.title == name){ 678 | name = o_name + "_" + std::to_string(id++); 679 | rename = true; 680 | found = true; 681 | break; 682 | } 683 | } 684 | 685 | if(!found){ 686 | break; 687 | } 688 | } 689 | 690 | if(rename){ 691 | std::cout << "Warning: Bench already exists. Renamed in \"" << name << "\"\n"; 692 | } 693 | 694 | return name; 695 | } 696 | 697 | //Measure once functor (no policy, no randomization) 698 | 699 | template 700 | void measure_once(const std::string& o_title, Functor&& functor){ 701 | measure_once(o_title, std::forward(functor), [](){return 1UL; }); 702 | } 703 | 704 | template 705 | void measure_once(const std::string& o_title, Functor&& functor, Flops&& flops){ 706 | if(bench_should_run(o_title)){ 707 | auto title = check_title(o_title); 708 | 709 | measure_data data; 710 | data.title = title; 711 | 712 | auto duration = measure_only_simple(*this, std::forward(functor), std::forward(flops)); 713 | report(title, std::size_t(1), duration); 714 | data.results.push_back({1, std::string("1"), duration}); 715 | 716 | results.push_back(std::move(data)); 717 | } 718 | } 719 | 720 | //Measure simple functor (no randomization) 721 | 722 | template 723 | void measure_simple(const std::string& o_title, Functor&& functor){ 724 | measure_simple(o_title, std::forward(functor), [](auto... args){ return mul_all(args...); }); 725 | } 726 | 727 | template 728 | void measure_simple(const std::string& o_title, Functor&& functor, Flops&& flops){ 729 | if(bench_should_run(o_title)){ 730 | auto title = check_title(o_title); 731 | 732 | if(standard_report){ 733 | std::cout << std::endl; 734 | } 735 | 736 | measure_data data; 737 | data.title = title; 738 | 739 | policy_run( 740 | [&data, &title, functor = std::forward(functor), flops = std::forward(flops), this](auto sizes){ 741 | using namespace cpm; 742 | 743 | auto duration = measure_only_simple(*this, functor, flops, sizes); 744 | report(title, sizes, duration); 745 | data.results.push_back({size_to_eff(sizes), size_to_string(sizes), duration}); 746 | return duration; 747 | } 748 | ); 749 | 750 | results.push_back(std::move(data)); 751 | } 752 | } 753 | 754 | //Measure with two-pass functors (init and functor) 755 | 756 | template 757 | void measure_two_pass(const std::string& o_title, Init&& init, Functor&& functor){ 758 | return measure_two_pass(o_title, std::forward(init), std::forward(functor), [](auto... args){return mul_all(args...);}); 759 | } 760 | 761 | template 762 | void measure_two_pass(const std::string& o_title, Init&& init, Functor&& functor, Flops&& flops){ 763 | if(bench_should_run(o_title)){ 764 | auto title = check_title(o_title); 765 | 766 | if(standard_report){ 767 | std::cout << std::endl; 768 | } 769 | 770 | measure_data data; 771 | data.title = title; 772 | 773 | policy_run( 774 | [&data, &title, functor = std::forward(functor), init = std::forward(init), flops = std::forward(flops), this](auto sizes){ 775 | using namespace cpm; 776 | 777 | auto duration = measure_only_two_pass(*this, init, functor, flops, sizes); 778 | report(title, sizes, duration); 779 | data.results.push_back({size_to_eff(sizes), size_to_string(sizes), duration}); 780 | return duration; 781 | } 782 | ); 783 | 784 | results.push_back(std::move(data)); 785 | } 786 | } 787 | 788 | //measure a function with global references 789 | 790 | template 791 | void measure_global(const std::string& o_title, Functor&& functor, T&... references){ 792 | measure_global_flops(o_title, std::forward(functor), [](auto... args){ return mul_all(args...); }, references...); 793 | } 794 | 795 | template 796 | void measure_global_flops(const std::string& o_title, Functor&& functor, Flops&& flops, T&... references){ 797 | if(bench_should_run(o_title)){ 798 | auto title = check_title(o_title); 799 | 800 | if(standard_report){ 801 | std::cout << std::endl; 802 | } 803 | 804 | measure_data data; 805 | data.title = title; 806 | 807 | policy_run( 808 | [&data, &title, functor = std::forward(functor), flops = std::forward(flops), &references..., this](auto sizes){ 809 | using namespace cpm; 810 | 811 | auto duration = measure_only_global(*this, functor, flops, sizes, references...); 812 | report(title, sizes, duration); 813 | data.results.push_back({size_to_eff(sizes), size_to_string(sizes), duration}); 814 | return duration; 815 | } 816 | ); 817 | 818 | results.push_back(std::move(data)); 819 | } 820 | } 821 | 822 | //Measure and return the duration of a simple functor 823 | 824 | template 825 | static std::size_t measure_only(Functor functor){ 826 | auto start_time = timer_clock::now(); 827 | functor(); 828 | prologue(); 829 | auto end_time = timer_clock::now(); 830 | auto duration = std::chrono::duration_cast(end_time - start_time); 831 | 832 | return duration.count(); 833 | } 834 | 835 | private: 836 | void save(){ 837 | if(!folder_ok){ 838 | std::cout << "Impossible save, the folder was not correct" << std::endl; 839 | 840 | return; 841 | } 842 | 843 | auto time = wall_clock::to_time_t(start_time); 844 | std::stringstream ss; 845 | ss << std::ctime(&time); 846 | auto time_str = ss.str(); 847 | 848 | if(time_str.back() == '\n'){ 849 | time_str.pop_back(); 850 | } 851 | 852 | std::ofstream stream(final_file); 853 | 854 | stream << "{\n"; 855 | 856 | std::size_t indent = 2; 857 | 858 | write_value(stream, indent, "name", name); 859 | write_value(stream, indent, "tag", tag); 860 | write_value(stream, indent, "configuration", configuration); 861 | write_value(stream, indent, "compiler", COMPILER_FULL); 862 | write_value(stream, indent, "os", operating_system); 863 | 864 | write_value(stream, indent, "time", time_str); 865 | write_value(stream, indent, "timestamp", std::chrono::duration_cast(start_time.time_since_epoch()).count()); 866 | 867 | start_array(stream, indent, "results"); 868 | 869 | for(std::size_t i = 0; i < results.size(); ++i){ 870 | auto& result = results[i]; 871 | 872 | start_sub(stream, indent); 873 | 874 | write_value(stream, indent, "title", result.title); 875 | start_array(stream, indent, "results"); 876 | 877 | for(std::size_t j = 0; j < result.results.size(); ++j){ 878 | auto& sub = result.results[j]; 879 | 880 | start_sub(stream, indent); 881 | 882 | write_value(stream, indent, "size", sub.size); 883 | write_value(stream, indent, "size_eff", sub.size_eff); 884 | write_value(stream, indent, "mean", sub.result.mean); 885 | write_value(stream, indent, "mean_lb", sub.result.mean_lb); 886 | write_value(stream, indent, "mean_ub", sub.result.mean_ub); 887 | write_value(stream, indent, "stddev", sub.result.stddev); 888 | write_value(stream, indent, "min", sub.result.min); 889 | write_value(stream, indent, "max", sub.result.max); 890 | write_value(stream, indent, "throughput", sub.result.throughput_e); 891 | write_value(stream, indent, "throughput_e", sub.result.throughput_e); 892 | write_value(stream, indent, "throughput_f", sub.result.throughput_f, false); 893 | 894 | close_sub(stream, indent, j < result.results.size() - 1); 895 | } 896 | 897 | close_array(stream, indent, false); 898 | close_sub(stream, indent, i < results.size() - 1); 899 | } 900 | 901 | close_array(stream, indent, true); 902 | 903 | start_array(stream, indent, "sections"); 904 | 905 | for(std::size_t i = 0; i < section_results.size(); ++i){ 906 | auto& section = section_results[i]; 907 | 908 | start_sub(stream, indent); 909 | 910 | write_value(stream, indent, "name", section.name); 911 | start_array(stream, indent, "results"); 912 | 913 | for(std::size_t j = 0; j < section.names.size(); ++j){ 914 | auto& name = section.names[j]; 915 | 916 | start_sub(stream, indent); 917 | 918 | write_value(stream, indent, "name", name); 919 | start_array(stream, indent, "results"); 920 | 921 | for(std::size_t k = 0; k < section.results[j].size(); ++k){ 922 | start_sub(stream, indent); 923 | 924 | write_value(stream, indent, "size", section.sizes[k]); 925 | write_value(stream, indent, "size_eff", section.sizes_eff[k]); 926 | write_value(stream, indent, "mean", section.results[j][k].mean); 927 | write_value(stream, indent, "mean_lb", section.results[j][k].mean_lb); 928 | write_value(stream, indent, "mean_ub", section.results[j][k].mean_ub); 929 | write_value(stream, indent, "stddev", section.results[j][k].stddev); 930 | write_value(stream, indent, "min", section.results[j][k].min); 931 | write_value(stream, indent, "max", section.results[j][k].max); 932 | write_value(stream, indent, "throughput", section.results[j][k].throughput_e); 933 | write_value(stream, indent, "throughput_e", section.results[j][k].throughput_e); 934 | write_value(stream, indent, "throughput_f", section.results[j][k].throughput_f, false); 935 | 936 | close_sub(stream, indent, k < section.results[j].size() - 1); 937 | } 938 | 939 | close_array(stream, indent, false); 940 | close_sub(stream, indent, j < section.names.size() - 1); 941 | } 942 | 943 | close_array(stream, indent, false); 944 | close_sub(stream, indent, i < section_results.size() - 1); 945 | } 946 | 947 | close_array(stream, indent, false); 948 | 949 | stream << "}"; 950 | } 951 | 952 | template 953 | void policy_run(M measure){ 954 | ++tests; 955 | 956 | std::size_t i = 0; 957 | auto d = Policy::begin(); 958 | auto duration = measure(d); 959 | 960 | while(Policy::has_next(i, d, duration)){ 961 | d = Policy::next(i, d); 962 | 963 | duration = measure(d); 964 | 965 | ++i; 966 | } 967 | } 968 | 969 | measure_result measure(const std::vector& durations, std::size_t flops = 1){ 970 | auto n = durations.size(); 971 | 972 | double mean = 0.0; 973 | double min = durations[0]; 974 | double max = durations[0]; 975 | 976 | for(auto& duration : durations){ 977 | mean += duration; 978 | min = std::min(min, static_cast(duration)); 979 | max = std::max(max, static_cast(duration)); 980 | } 981 | 982 | mean /= n; 983 | 984 | double stddev = 0.0; 985 | 986 | for(auto& duration : durations){ 987 | stddev += (duration - mean) * (duration - mean); 988 | } 989 | 990 | stddev = std::sqrt(stddev / n); 991 | 992 | double stderror = stddev / std::sqrt(n); 993 | 994 | double mean_lb = mean - 1.96 * stderror; 995 | double mean_ub = mean + 1.96 * stderror; 996 | 997 | return {mean, mean_lb, mean_ub, stddev, min, max, 0.0, 0.0, flops}; 998 | } 999 | 1000 | template 1001 | measure_result measure_only_simple(const Config& conf, Functor&& functor, Flops&& flops, Args... args){ 1002 | ++measures; 1003 | 1004 | std::size_t steps = conf.steps; 1005 | 1006 | #ifdef CPM_AUTO_STEPS 1007 | steps = 1; 1008 | double seconds = 0.0; 1009 | 1010 | while(true){ 1011 | auto start_time = timer_clock::now(); 1012 | 1013 | for(std::size_t i = 0; i < steps; ++i){ 1014 | call_functor(functor, args...); 1015 | } 1016 | 1017 | prologue(); 1018 | 1019 | auto end_time = timer_clock::now(); 1020 | auto duration = std::chrono::duration_cast(end_time - start_time); 1021 | 1022 | seconds = duration.count() / (1000.0 * 1000.0 * 1000.0); 1023 | 1024 | if(seconds > cpm::step_estimation_min){ 1025 | break; 1026 | } 1027 | 1028 | steps *= 2; 1029 | } 1030 | 1031 | steps = std::max(1UL, std::size_t((cpm::runtime_target * steps) / seconds)); 1032 | #else 1033 | //1. Warmup 1034 | 1035 | for(std::size_t i = 0; i < conf.warmup; ++i){ 1036 | call_functor(functor, args...); 1037 | } 1038 | 1039 | prologue(); 1040 | 1041 | runs += conf.warmup; 1042 | #endif 1043 | 1044 | std::vector durations(steps); 1045 | 1046 | std::size_t i = 0; 1047 | 1048 | for(; i < steps - 1; ++i){ 1049 | auto start_time = timer_clock::now(); 1050 | call_functor(functor, args...); 1051 | auto end_time = timer_clock::now(); 1052 | auto duration = std::chrono::duration_cast(end_time - start_time); 1053 | durations[i] = duration.count(); 1054 | } 1055 | 1056 | auto start_time = timer_clock::now(); 1057 | call_functor(functor, args...); 1058 | prologue(); 1059 | auto end_time = timer_clock::now(); 1060 | auto duration = std::chrono::duration_cast(end_time - start_time); 1061 | durations[i] = duration.count(); 1062 | 1063 | runs += steps; 1064 | 1065 | return measure(durations, call_flops(flops, args...)); 1066 | } 1067 | 1068 | template 1069 | measure_result measure_only_two_pass(const Config& conf, Init&& init, Functor functor, Flops flops, Args... args){ 1070 | ++measures; 1071 | 1072 | auto data = call_init_functor(std::forward(init), args...); 1073 | 1074 | static constexpr const std::size_t tuple_s = std::tuple_size::value; 1075 | std::make_index_sequence sequence; 1076 | 1077 | //0. Initialization 1078 | 1079 | std::size_t steps = conf.steps; 1080 | 1081 | #ifdef CPM_AUTO_STEPS 1082 | random_init_each(data, sequence); 1083 | 1084 | steps = 1; 1085 | double seconds = 0.0; 1086 | 1087 | while(true){ 1088 | auto start_time = timer_clock::now(); 1089 | 1090 | for(std::size_t i = 0; i < steps; ++i){ 1091 | call_with_data(data, functor, sequence, args...); 1092 | } 1093 | 1094 | prologue(); 1095 | 1096 | auto end_time = timer_clock::now(); 1097 | auto duration = std::chrono::duration_cast(end_time - start_time); 1098 | 1099 | seconds = duration.count() / (1000.0 * 1000.0 * 1000.0); 1100 | 1101 | if(seconds > cpm::step_estimation_min){ 1102 | break; 1103 | } 1104 | 1105 | steps *= 2; 1106 | } 1107 | 1108 | steps = std::max(1UL, std::size_t((cpm::runtime_target * steps) / seconds)); 1109 | #else 1110 | //1. Warmup 1111 | 1112 | for(std::size_t i = 0; i < conf.warmup; ++i){ 1113 | randomize_each(data, sequence); 1114 | call_with_data(data, functor, sequence, args...); 1115 | } 1116 | 1117 | prologue(); 1118 | 1119 | runs += conf.warmup; 1120 | #endif 1121 | 1122 | //2. Measures 1123 | 1124 | random_init_each(data, sequence); 1125 | 1126 | std::vector durations(steps); 1127 | 1128 | std::size_t i = 0; 1129 | 1130 | for(; i < steps - 1; ++i){ 1131 | randomize_each(data, sequence); 1132 | auto start_time = timer_clock::now(); 1133 | call_with_data(data, functor, sequence, args...); 1134 | auto end_time = timer_clock::now(); 1135 | auto duration = std::chrono::duration_cast(end_time - start_time); 1136 | durations[i] = duration.count(); 1137 | } 1138 | 1139 | randomize_each(data, sequence); 1140 | auto start_time = timer_clock::now(); 1141 | call_with_data(data, functor, sequence, args...); 1142 | prologue(); 1143 | auto end_time = timer_clock::now(); 1144 | auto duration = std::chrono::duration_cast(end_time - start_time); 1145 | durations[i] = duration.count(); 1146 | 1147 | runs += steps; 1148 | 1149 | return measure(durations, call_flops(flops, args...)); 1150 | } 1151 | 1152 | template 1153 | measure_result measure_only_global(const Config& conf, Functor&& functor, Flops&& flops, Tuple d, T&... references){ 1154 | ++measures; 1155 | 1156 | //0. Initialization 1157 | 1158 | std::size_t steps = conf.steps; 1159 | 1160 | #ifdef CPM_AUTO_STEPS 1161 | random_init(references...); 1162 | 1163 | steps = 1; 1164 | double seconds = 0.0; 1165 | 1166 | while(true){ 1167 | auto start_time = timer_clock::now(); 1168 | 1169 | for(std::size_t i = 0; i < steps; ++i){ 1170 | call_functor(functor, d); 1171 | } 1172 | 1173 | prologue(); 1174 | 1175 | auto end_time = timer_clock::now(); 1176 | auto duration = std::chrono::duration_cast(end_time - start_time); 1177 | 1178 | seconds = duration.count() / (1000.0 * 1000.0 * 1000.0); 1179 | 1180 | if(seconds > cpm::step_estimation_min){ 1181 | break; 1182 | } 1183 | 1184 | steps *= 2; 1185 | } 1186 | 1187 | steps = std::max(1UL, std::size_t((cpm::runtime_target * steps) / seconds)); 1188 | #else 1189 | //1. Warmup 1190 | 1191 | for(std::size_t i = 0; i < conf.warmup; ++i){ 1192 | using cpm::randomize; 1193 | randomize(references...); 1194 | call_functor(functor, d); 1195 | } 1196 | 1197 | prologue(); 1198 | 1199 | runs += conf.warmup; 1200 | #endif 1201 | 1202 | //2. Measures 1203 | 1204 | random_init(references...); 1205 | 1206 | std::vector durations(steps); 1207 | 1208 | std::size_t i = 0; 1209 | 1210 | for(; i < steps; ++i){ 1211 | using cpm::randomize; 1212 | randomize(references...); 1213 | auto start_time = timer_clock::now(); 1214 | call_functor(functor, d); 1215 | auto end_time = timer_clock::now(); 1216 | auto duration = std::chrono::duration_cast(end_time - start_time); 1217 | durations[i] = duration.count(); 1218 | } 1219 | 1220 | using cpm::randomize; 1221 | randomize(references...); 1222 | auto start_time = timer_clock::now(); 1223 | call_functor(functor, d); 1224 | prologue(); 1225 | auto end_time = timer_clock::now(); 1226 | auto duration = std::chrono::duration_cast(end_time - start_time); 1227 | durations[i] = duration.count(); 1228 | 1229 | runs += steps; 1230 | 1231 | return measure(durations, call_flops(flops, d)); 1232 | } 1233 | 1234 | template 1235 | void report(const std::string& title, Tuple d, measure_result& duration){ 1236 | duration.update(size_to_eff(d)); 1237 | 1238 | if(standard_report){ 1239 | std::cout << title << "(" << size_to_string(d) << "): " 1240 | << "mean:" << duration_str(duration.mean, 3) 1241 | << " (" << duration_str(duration.mean_lb, 3) << "," << duration_str(duration.mean_ub, 3) << ")" 1242 | << " stddev:" << duration_str(duration.stddev, 3) 1243 | << " min:" << duration_str(duration.min, 3) 1244 | << " max:" << duration_str(duration.max, 3) 1245 | << " (" << throughput_str(duration.throughput_e, 3) << "Es" 1246 | << "," << throughput_str(duration.throughput_f, 3) << "Flop/s)" 1247 | << "\n"; 1248 | } 1249 | } 1250 | }; 1251 | 1252 | } //end of namespace cpm 1253 | 1254 | #ifdef CPM_BENCHMARK 1255 | #include "cpm_support.hpp" 1256 | #elif defined(CPM_LIB) 1257 | #include "cpm_support.hpp" 1258 | #endif 1259 | 1260 | #endif //CPM_CPM_HPP 1261 | -------------------------------------------------------------------------------- /include/cpm/cpm_support.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_CPM_SUPPORT_HPP 9 | #define CPM_CPM_SUPPORT_HPP 10 | 11 | #include "../../lib/cxxopts/include/cxxopts.hpp" 12 | 13 | namespace cpm { 14 | 15 | struct cpm_registry { 16 | cpm_registry(void (*function)(cpm::benchmark<>&)){ 17 | benchs().emplace_back(function); 18 | } 19 | 20 | static std::vector&)>& benchs(){ 21 | static std::vector&)> vec; 22 | return vec; 23 | } 24 | }; 25 | 26 | template class TT, typename T> 27 | struct is_specialization_of : std::false_type {}; 28 | 29 | template class TT, typename... Args> 30 | struct is_specialization_of> : std::true_type {}; 31 | 32 | template 33 | struct is_section : is_specialization_of> {}; 34 | 35 | } //end of namespace cpm 36 | 37 | //Internal helpers 38 | 39 | #define CPM_UNIQUE_DETAIL(x, y) x##y 40 | #define CPM_UNIQUE(x, y) CPM_UNIQUE_DETAIL(x, y) 41 | #define CPM_UNIQUE_NAME(x) CPM_UNIQUE(x, __LINE__) 42 | 43 | //Declarations of benchs functions 44 | 45 | #define CPM_BENCH() \ 46 | static void CPM_UNIQUE_NAME(bench_) (cpm::benchmark<>& bench); \ 47 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(bench_)); } \ 48 | static void CPM_UNIQUE_NAME(bench_) (cpm::benchmark<>& bench) 49 | 50 | //Declaration of section functions 51 | 52 | #define CPM_SECTION(name)\ 53 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 54 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 55 | void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 56 | auto bench = master.multi(name); 57 | 58 | #define CPM_SECTION_F(name, ...)\ 59 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 60 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 61 | void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 62 | auto bench = master.multi(name, __VA_ARGS__); 63 | 64 | #define CPM_SECTION_O(name, W, R)\ 65 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 66 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 67 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 68 | auto bench = master.multi(name); \ 69 | bench.warmup = W; \ 70 | bench.steps = R; 71 | 72 | #define CPM_SECTION_OF(name, W, R, ...)\ 73 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 74 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 75 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 76 | auto bench = master.multi(name, __VA_ARGS__); \ 77 | bench.warmup = W; \ 78 | bench.steps = R; 79 | 80 | #define CPM_SECTION_P(name, policy)\ 81 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 82 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 83 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 84 | auto bench = master.multi(name); 85 | 86 | #define CPM_SECTION_PF(name, policy, ...)\ 87 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 88 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 89 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 90 | auto bench = master.multi(name, __VA_ARGS__); 91 | 92 | #define CPM_SECTION_PO(name, policy, W, R)\ 93 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 94 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 95 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 96 | auto bench = master.multi(name); \ 97 | bench.warmup = W; \ 98 | bench.steps = R; 99 | 100 | #define CPM_SECTION_POF(name, policy, W, R, ...)\ 101 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master); \ 102 | namespace { cpm::cpm_registry CPM_UNIQUE_NAME(register_) (& CPM_UNIQUE_NAME(section_)); } \ 103 | static void CPM_UNIQUE_NAME(section_) (cpm::benchmark<>& master) { \ 104 | auto bench = master.multi(name, __VA_ARGS__); \ 105 | bench.warmup = W; \ 106 | bench.steps = R; 107 | 108 | //Normal versions for simple bench 109 | #define CPM_SIMPLE(...) bench.measure_simple(__VA_ARGS__) 110 | #define CPM_GLOBAL(...) bench.measure_global(__VA_ARGS__) 111 | #define CPM_GLOBAL_F(...) bench.measure_global_flops(__VA_ARGS__) 112 | #define CPM_TWO_PASS(...) bench.measure_two_pass(__VA_ARGS__) 113 | #define CPM_TWO_PASS_NS(...) bench.measure_two_pass(__VA_ARGS__) 114 | 115 | //Versions with policies 116 | 117 | #define CPM_SIMPLE_P(policy, ...) \ 118 | static_assert(!cpm::is_section::value, "CPM_SIMPLE_P cannot be used inside CPM_SECTION"); \ 119 | bench.measure_simple(__VA_ARGS__)g 120 | 121 | #define CPM_GLOBAL_P(policy, ...) \ 122 | static_assert(!cpm::is_section::value, "CPM_GLOBAL_P cannot be used inside CPM_SECTION"); \ 123 | bench.measure_global(__VA_ARGS__) 124 | 125 | #define CPM_GLOBAL_FP(policy, ...) \ 126 | static_assert(!cpm::is_section::value, "CPM_GLOBAL_FP cannot be used inside CPM_SECTION"); \ 127 | bench.measure_global_flops(__VA_ARGS__) 128 | 129 | #define CPM_TWO_PASS_P(policy, ...) \ 130 | static_assert(!cpm::is_section::value, "CPM_TWO_PASS_P cannot be used inside CPM_SECTION"); \ 131 | bench.measure_two_pass(__VA_ARGS__) 132 | 133 | #define CPM_TWO_PASS_NS_P(policy, ...) \ 134 | static_assert(!cpm::is_section::value, "CPM_TWO_PASS_NS_P cannot be used inside CPM_SECTION"); \ 135 | bench.measure_two_pass(__VA_ARGS__) 136 | 137 | //Direct bench functions 138 | 139 | #define CPM_DIRECT_BENCH_SIMPLE(...) CPM_BENCH() { CPM_SIMPLE(__VA_ARGS__); } 140 | #define CPM_DIRECT_BENCH_TWO_PASS(...) CPM_BENCH() { CPM_TWO_PASS(__VA_ARGS__); } 141 | #define CPM_DIRECT_BENCH_TWO_PASS_NS(...) CPM_BENCH() { CPM_TWO_PASS_NS(__VA_ARGS__); } 142 | 143 | //Direct bench functions with policies 144 | 145 | #define CPM_DIRECT_BENCH_SIMPLE_P(policy,...) CPM_BENCH() { CPM_SIMPLE_P(POLICY(policy),__VA_ARGS__); } 146 | #define CPM_DIRECT_BENCH_TWO_PASS_P(policy,...) CPM_BENCH() { CPM_TWO_PASS_P(POLICY(policy),__VA_ARGS__); } 147 | #define CPM_DIRECT_BENCH_TWO_PASS_NS_P(policy,...) CPM_BENCH() { CPM_TWO_PASS_NS_P(POLICY(policy),__VA_ARGS__); } 148 | 149 | //Direct section functions 150 | 151 | #define FE_1(WHAT, X) WHAT(X) 152 | #define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__) 153 | #define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__) 154 | #define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__) 155 | #define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__) 156 | #define FE_6(WHAT, X, ...) WHAT(X)FE_5(WHAT, __VA_ARGS__) 157 | #define FE_7(WHAT, X, ...) WHAT(X)FE_6(WHAT, __VA_ARGS__) 158 | #define FE_8(WHAT, X, ...) WHAT(X)FE_7(WHAT, __VA_ARGS__) 159 | 160 | #define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME 161 | 162 | #define FOR_EACH(action,...) \ 163 | GET_MACRO(__VA_ARGS__,FE_8,FE_7,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1)(action,__VA_ARGS__) 164 | 165 | #define FFE_1(WHAT, I, X) WHAT(I,X) 166 | #define FFE_2(WHAT, I, X, ...) WHAT(I,X)FFE_1(WHAT, I, __VA_ARGS__) 167 | #define FFE_3(WHAT, I, X, ...) WHAT(I,X)FFE_2(WHAT, I, __VA_ARGS__) 168 | #define FFE_4(WHAT, I, X, ...) WHAT(I,X)FFE_3(WHAT, I, __VA_ARGS__) 169 | #define FFE_5(WHAT, I, X, ...) WHAT(I,X)FFE_4(WHAT, I, __VA_ARGS__) 170 | #define FFE_6(WHAT, I, X, ...) WHAT(I,X)FFE_5(WHAT, I, __VA_ARGS__) 171 | #define FFE_7(WHAT, I, X, ...) WHAT(I,X)FFE_6(WHAT, I, __VA_ARGS__) 172 | #define FFE_8(WHAT, I, X, ...) WHAT(I,X)FFE_7(WHAT, I, __VA_ARGS__) 173 | 174 | #define F_FOR_EACH(action,I,...) \ 175 | GET_MACRO(__VA_ARGS__,FFE_8,FFE_7,FFE_6,FFE_5,FFE_4,FFE_3,FFE_2,FFE_1)(action,I,__VA_ARGS__) 176 | 177 | #define EMIT_TWO_PASS(init, X) CPM_TWO_PASS((X).first, init, (X).second); 178 | #define EMIT_TWO_PASS_NS(init, X) CPM_TWO_PASS_NS((X).first, init, (X).second); 179 | 180 | #define CPM_SECTION_FUNCTOR(name, ...) \ 181 | (std::make_pair(name, (__VA_ARGS__))) 182 | 183 | #define CPM_DIRECT_SECTION_TWO_PASS(name, init, ...) \ 184 | CPM_SECTION(name) \ 185 | F_FOR_EACH(EMIT_TWO_PASS, init, __VA_ARGS__) \ 186 | } 187 | 188 | #define CPM_DIRECT_SECTION_TWO_PASS_F(name, flops, init, ...) \ 189 | CPM_SECTION_F(name, flops) \ 190 | F_FOR_EACH(EMIT_TWO_PASS, init, __VA_ARGS__) \ 191 | } 192 | 193 | #define CPM_DIRECT_SECTION_TWO_PASS_P(name, policy, init, ...) \ 194 | CPM_SECTION_P(name, POLICY(policy)) \ 195 | F_FOR_EACH(EMIT_TWO_PASS, init, __VA_ARGS__) \ 196 | } 197 | 198 | #define CPM_DIRECT_SECTION_TWO_PASS_PF(name, policy, flops, init, ...) \ 199 | CPM_SECTION_PF(name, POLICY(policy), flops) \ 200 | F_FOR_EACH(EMIT_TWO_PASS, init, __VA_ARGS__) \ 201 | } 202 | 203 | #define CPM_DIRECT_SECTION_TWO_PASS_NS(name, init, ...) \ 204 | CPM_SECTION(name) \ 205 | F_FOR_EACH(EMIT_TWO_PASS_NS, init, __VA_ARGS__) \ 206 | } 207 | 208 | #define CPM_DIRECT_SECTION_TWO_PASS_NS_F(name, flops, init, ...) \ 209 | CPM_SECTION_F(name, flops) \ 210 | F_FOR_EACH(EMIT_TWO_PASS_NS, init, __VA_ARGS__) \ 211 | } 212 | 213 | #define CPM_DIRECT_SECTION_TWO_PASS_NS_P(name, policy, init, ...) \ 214 | CPM_SECTION_P(name, POLICY(policy)) \ 215 | F_FOR_EACH(EMIT_TWO_PASS_NS, init, __VA_ARGS__) \ 216 | } 217 | 218 | #define CPM_DIRECT_SECTION_TWO_PASS_NS_PF(name, policy, flops, init, ...) \ 219 | CPM_SECTION_PF(name, POLICY(policy), flops) \ 220 | F_FOR_EACH(EMIT_TWO_PASS_NS, init, __VA_ARGS__) \ 221 | } 222 | 223 | 224 | #define CPM_SECTION_INIT(...) (__VA_ARGS__) 225 | 226 | //Helpers to create policy 227 | #define POLICY(...) __VA_ARGS__ 228 | #define VALUES_POLICY(...) cpm::values_policy<__VA_ARGS__> 229 | #define NARY_POLICY(...) cpm::simple_nary_policy<__VA_ARGS__> 230 | #define STD_STOP_POLICY cpm::std_stop_policy 231 | #define STOP_POLICY(start, stop, add, mul) cpm::increasing_policy 232 | #define TIMEOUT_POLICY(start, stop, add, mul) cpm::increasing_policy 233 | 234 | //Helpers for flops function 235 | #define FLOPS(...) __VA_ARGS__ 236 | 237 | #ifdef CPM_BENCHMARK 238 | 239 | int main(int argc, char* argv[]){ 240 | cxxopts::Options options(argv[0], "filter"); 241 | 242 | try { 243 | options.add_options() 244 | ("n,name", "Benchmark name", cxxopts::value()) 245 | ("t,tag", "Tag name", cxxopts::value()) 246 | ("c,configuration", "Configuration", cxxopts::value()) 247 | ("o,output", "Output folder", cxxopts::value()) 248 | ("f,oneshot", "Don't save result") 249 | ("mflops", "Print section summary with MFlops/s") 250 | ("filter", "Filter tests/sections to run", cxxopts::value()) 251 | ("h,help", "Print help") 252 | ; 253 | 254 | options.parse_positional("filter"); 255 | auto result = options.parse(argc, argv); 256 | 257 | if (result.count("help")){ 258 | std::cout << options.help() << std::endl; 259 | return 0; 260 | } 261 | 262 | std::string output_folder{"./results"}; 263 | 264 | if (result.count("output")){ 265 | output_folder = result["output"].as(); 266 | } 267 | 268 | std::string benchmark_name{CPM_BENCHMARK}; 269 | 270 | if (result.count("name")){ 271 | benchmark_name = result["name"].as(); 272 | } 273 | 274 | std::string tag; 275 | 276 | if (result.count("tag")){ 277 | tag = result["tag"].as(); 278 | } 279 | 280 | std::string configuration; 281 | 282 | if (result.count("configuration")){ 283 | configuration = result["configuration"].as(); 284 | } 285 | 286 | cpm::benchmark<> bench(benchmark_name, output_folder, tag, configuration); 287 | 288 | #ifdef CPM_WARMUP 289 | bench.warmup = CPM_WARMUP; 290 | #endif 291 | 292 | #ifdef CPM_REPEAT 293 | bench.steps = CPM_REPEAT; 294 | #endif 295 | 296 | #ifdef CPM_STEPS 297 | bench.steps = CPM_STEPS; 298 | #endif 299 | 300 | if(result.count("filter")){ 301 | bench.set_filter(result["filter"].as()); 302 | } 303 | 304 | if(result.count("oneshot")){ 305 | bench.auto_save = false; 306 | } 307 | 308 | if(result.count("mflops")){ 309 | bench.section_mflops = true; 310 | } 311 | 312 | bench.begin(); 313 | 314 | for(auto f : cpm::cpm_registry::benchs()){ 315 | f(bench); 316 | } 317 | 318 | } catch (const cxxopts::OptionException& e){ 319 | std::cout << "cpm: error parsing options: " << e.what() << std::endl; 320 | return -1; 321 | } 322 | 323 | return 0; 324 | } 325 | 326 | #endif //CPM_BENCHMARK 327 | 328 | #endif //CPM_CPM_SUPPORT_HPP 329 | -------------------------------------------------------------------------------- /include/cpm/data.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_DATA_HPP 9 | #define CPM_DATA_HPP 10 | 11 | #include "cpm/rapidjson.hpp" 12 | 13 | namespace cpm { 14 | 15 | using document_t = rapidjson::Document; 16 | using document_ref = std::reference_wrapper; 17 | using document_cref = std::reference_wrapper; 18 | 19 | struct reports_data { 20 | std::set compilers; 21 | std::set configurations; 22 | std::vector documents; 23 | 24 | //Temporary data (changed for each generated file) 25 | std::string file; 26 | std::string sub_part; 27 | std::vector> files; 28 | }; 29 | 30 | } //end of namespace cpm 31 | 32 | #endif //CPM_DATA_HPP 33 | -------------------------------------------------------------------------------- /include/cpm/duration.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_DURATION_HPP 9 | #define CPM_DURATION_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "compat.hpp" 16 | 17 | namespace cpm { 18 | 19 | using timer_clock = std::chrono::steady_clock; 20 | using wall_clock = std::chrono::system_clock; 21 | using wall_time_point = wall_clock::time_point; 22 | using seconds = std::chrono::seconds; 23 | using millseconds = std::chrono::milliseconds; 24 | using microseconds = std::chrono::microseconds; 25 | using nanoseconds = std::chrono::nanoseconds; 26 | using clock_resolution = nanoseconds; 27 | 28 | struct measure_result { 29 | double mean; 30 | double mean_lb; 31 | double mean_ub; 32 | double stddev; 33 | double min; 34 | double max; 35 | double throughput_e; 36 | double throughput_f; 37 | std::size_t flops; 38 | 39 | cpp14_constexpr void update(std::size_t size_eff){ 40 | throughput_e = mean == 0.0 ? 0.0 : size_eff / (mean / (1000.0 * 1000.0 * 1000.0)); 41 | throughput_f = mean == 0.0 ? 0.0 : flops / (mean / (1000.0 * 1000.0 * 1000.0)); 42 | } 43 | }; 44 | 45 | struct measure_full { 46 | std::size_t size_eff; 47 | std::string size; 48 | measure_result result; 49 | }; 50 | 51 | inline std::string to_string_precision(double duration, int precision = 6){ 52 | std::ostringstream out; 53 | out << std::setprecision(precision) << duration; 54 | return out.str(); 55 | } 56 | 57 | inline std::string duration_str(double duration, int precision = 6){ 58 | if(duration > 1000.0 * 1000.0 * 1000.0){ 59 | return to_string_precision(duration / (1000.0 * 1000.0 * 1000.0), precision) + "s"; 60 | } else if(duration > 1000.0 * 1000.0){ 61 | return to_string_precision(duration / (1000.0 * 1000.0), precision) + "ms"; 62 | } else if(duration > 1000.0){ 63 | return to_string_precision(duration / 1000.0, precision) + "us"; 64 | } else { 65 | return to_string_precision(duration, precision) + "ns"; 66 | } 67 | } 68 | 69 | inline std::string mthroughput_str(double tp, int precision = 6){ 70 | std::ostringstream out; 71 | 72 | out << std::setprecision(precision); 73 | out << tp / (1000 * 1000); 74 | 75 | return out.str(); 76 | } 77 | 78 | inline std::string throughput_str(double tp, int precision = 6){ 79 | std::ostringstream out; 80 | 81 | out << std::setprecision(precision); 82 | 83 | if(tp > 1000.0 * 1000.0 * 1000.0 * 1000.0){ 84 | out << tp / (1000.0 * 1000.0 * 1000.0 * 1000.0) << "T"; 85 | } else if(tp > 1000 * 1000 * 1000){ 86 | out << tp / (1000 * 1000 * 1000) << "G"; 87 | } else if(tp > 1000 * 1000){ 88 | out << tp / (1000 * 1000) << "M"; 89 | } else if(tp > 1000){ 90 | out << tp / (1000) << "K"; 91 | } else { 92 | out << tp; 93 | } 94 | 95 | return out.str(); 96 | } 97 | 98 | } //end of namespace cpm 99 | 100 | #endif //CPM_DURATION_HPP 101 | -------------------------------------------------------------------------------- /include/cpm/io.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_IO_HPP 9 | #define CPM_IO_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace cpm { 18 | 19 | inline std::size_t max_path_length(){ 20 | auto path_max = pathconf(".", _PC_PATH_MAX); 21 | if (path_max == -1){ 22 | path_max = 1024; 23 | } else if (path_max > 10240){ 24 | path_max = 10240; 25 | } 26 | return path_max; 27 | } 28 | 29 | inline std::string get_cwd(){ 30 | auto path_max = max_path_length(); 31 | 32 | char* buf = static_cast(malloc(sizeof(char) * path_max)); 33 | if(!buf){ 34 | return ""; 35 | } 36 | 37 | char* ptr = getcwd(buf, path_max); 38 | if (!ptr) { 39 | return ""; 40 | } 41 | 42 | std::string cwd = buf; 43 | 44 | free(buf); 45 | 46 | return cwd; 47 | } 48 | 49 | inline std::string make_absolute(const std::string& folder){ 50 | auto path_max = max_path_length(); 51 | 52 | char* buf = static_cast(malloc(sizeof(char) * path_max)); 53 | if(!buf){ 54 | return folder; 55 | } 56 | 57 | char* res = realpath(folder.c_str(), buf); 58 | 59 | if(!res){ 60 | return folder; 61 | } 62 | 63 | std::string abs = buf; 64 | 65 | free(buf); 66 | 67 | return abs; 68 | } 69 | 70 | inline std::string get_free_file(const std::string& base_folder){ 71 | std::size_t result_name = 0; 72 | std::string result_folder; 73 | 74 | struct stat buffer; 75 | do { 76 | ++result_name; 77 | result_folder = base_folder + std::to_string(result_name) + ".cpm"; 78 | } while(stat(result_folder.c_str(), &buffer) == 0); 79 | 80 | return std::to_string(result_name); 81 | } 82 | 83 | inline bool folder_exists(const std::string& folder){ 84 | struct stat buffer; 85 | if (stat(folder.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode)){ 86 | return true; 87 | } else { 88 | return false; 89 | } 90 | } 91 | 92 | inline std::string url_encode(const std::string& value) { 93 | std::ostringstream escaped; 94 | escaped.fill('0'); 95 | escaped << std::hex; 96 | 97 | for (auto i = value.begin(), n = value.end(); i != n; ++i) { 98 | auto c = *i; 99 | 100 | // Keep alphanumeric and other accepted characters intact 101 | if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { 102 | escaped << c; 103 | continue; 104 | } 105 | 106 | // Any other characters are percent-encoded 107 | escaped << '%' << std::setw(2) << int((unsigned char) c); 108 | } 109 | 110 | return escaped.str(); 111 | } 112 | 113 | inline std::string filify(std::string name){ 114 | std::string n{std::move(name)}; 115 | 116 | //Replace all spaces 117 | std::replace(n.begin(), n.end(), ' ', '_'); 118 | 119 | //Replace all forward slashes 120 | std::replace(n.begin(), n.end(), '/', '|'); 121 | 122 | //Append extension 123 | n += ".html"; 124 | 125 | return n; 126 | } 127 | 128 | inline std::string filify(std::string compiler, std::string configuration){ 129 | std::string n1{std::move(compiler)}; 130 | std::string n2{std::move(configuration)}; 131 | 132 | auto n = n1 + "-" + n2; 133 | 134 | return filify(n); 135 | } 136 | 137 | inline std::string filify(std::string compiler, std::string configuration, std::string bench){ 138 | std::string n1{std::move(compiler)}; 139 | std::string n2{std::move(configuration)}; 140 | std::string n3{std::move(bench)}; 141 | 142 | auto n = n1 + "-" + n2 + "-" + n3; 143 | 144 | return filify(n); 145 | } 146 | 147 | } //end of namespace cpm 148 | 149 | #endif //CPM_IO_HPP 150 | -------------------------------------------------------------------------------- /include/cpm/json.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_JSON_HPP 9 | #define CPM_JSON_HPP 10 | 11 | #include 12 | #include 13 | 14 | namespace cpm { 15 | 16 | template 17 | inline void write_value(std::ofstream& stream, std::size_t& indent, const std::string& tag, const T& value, bool comma = true){ 18 | if(comma){ 19 | stream << std::string(indent, ' ') << "\"" << tag << "\": \"" << value << "\",\n"; 20 | } else { 21 | stream << std::string(indent, ' ') << "\"" << tag << "\": \"" << value << "\"\n"; 22 | } 23 | } 24 | 25 | template<> 26 | inline void write_value(std::ofstream& stream, std::size_t& indent, const std::string& tag, const std::size_t& value, bool comma){ 27 | if(comma){ 28 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << ",\n"; 29 | } else { 30 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << "\n"; 31 | } 32 | } 33 | 34 | template<> 35 | inline void write_value(std::ofstream& stream, std::size_t& indent, const std::string& tag, const int64_t& value, bool comma){ 36 | if(comma){ 37 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << ",\n"; 38 | } else { 39 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << "\n"; 40 | } 41 | } 42 | 43 | template<> 44 | inline void write_value(std::ofstream& stream, std::size_t& indent, const std::string& tag, const long long& value, bool comma){ 45 | if(comma){ 46 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << ",\n"; 47 | } else { 48 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << "\n"; 49 | } 50 | } 51 | 52 | template<> 53 | inline void write_value(std::ofstream& stream, std::size_t& indent, const std::string& tag, const double& value, bool comma){ 54 | stream << std::fixed; 55 | if(comma){ 56 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << ",\n"; 57 | } else { 58 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << value << "\n"; 59 | } 60 | stream << std::scientific; 61 | } 62 | 63 | inline void start_array(std::ofstream& stream, std::size_t& indent, const std::string& tag){ 64 | stream << std::string(indent, ' ') << "\"" << tag << "\": " << "[" << "\n"; 65 | indent += 2; 66 | } 67 | 68 | inline void close_array(std::ofstream& stream, std::size_t& indent, bool comma){ 69 | indent -= 2; 70 | 71 | if(comma){ 72 | stream << std::string(indent, ' ') << "]," << "\n"; 73 | } else { 74 | stream << std::string(indent, ' ') << "]" << "\n"; 75 | } 76 | } 77 | 78 | inline void start_sub(std::ofstream& stream, std::size_t& indent){ 79 | stream << std::string(indent, ' ') << "{" << "\n"; 80 | indent += 2; 81 | } 82 | 83 | inline void close_sub(std::ofstream& stream, std::size_t& indent, bool comma){ 84 | indent -= 2; 85 | 86 | if(comma){ 87 | stream << std::string(indent, ' ') << "}," << "\n"; 88 | } else { 89 | stream << std::string(indent, ' ') << "}" << "\n"; 90 | } 91 | } 92 | 93 | } //end of namespace cpm 94 | 95 | #endif //CPM_JSON_HPP 96 | -------------------------------------------------------------------------------- /include/cpm/policy.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_POLICY_HPP 9 | #define CPM_POLICY_HPP 10 | 11 | #include 12 | 13 | #include "duration.hpp" 14 | #include "compat.hpp" 15 | 16 | namespace cpm { 17 | 18 | namespace detail { 19 | 20 | template 21 | bool all_and(H h) { 22 | return h; 23 | } 24 | 25 | template 26 | bool all_and(H h, HT... hs){ 27 | return h && all_and(hs...); 28 | } 29 | 30 | template 31 | struct nth_type { 32 | using type = typename std::tuple_element>::type; 33 | }; 34 | 35 | template 36 | struct has_next; 37 | 38 | template 39 | struct has_next, Policy...> { 40 | static constexpr bool value(std::size_t i, Tuple d, measure_result duration){ 41 | return all_and((nth_type::type::has_next(i, std::get(d), duration))...); 42 | } 43 | }; 44 | 45 | template 46 | struct next; 47 | 48 | template 49 | struct next , Policy...> { 50 | template //Simply to fake debug symbols for auto 51 | static constexpr auto value(std::size_t i, Tuple d){ 52 | return std::make_tuple((nth_type::type::next(i, std::get(d)))...); 53 | } 54 | }; 55 | 56 | template 57 | struct tuple_to_string; 58 | 59 | template 60 | struct tuple_to_string > { 61 | static std::string value(Tuple d){ 62 | std::array::value> values {{std::to_string(std::get(d))...}}; 63 | std::string acc = values[0]; 64 | for(std::size_t i = 1; i < values.size(); ++i){ 65 | acc += "x" + values[i]; 66 | } 67 | return acc; 68 | } 69 | }; 70 | 71 | template 72 | struct tuple_to_eff; 73 | 74 | template 75 | struct tuple_to_eff > { 76 | static std::size_t value(Tuple d){ 77 | std::array::value> values {{std::get(d)...}}; 78 | std::size_t acc = values[0]; 79 | for(std::size_t i = 1; i < values.size(); ++i){ 80 | acc *= values[i]; 81 | } 82 | return acc; 83 | } 84 | }; 85 | 86 | } //end of namespace detail 87 | 88 | enum class stop_policy { 89 | TIMEOUT, 90 | STOP 91 | }; 92 | 93 | enum class nary_combination_policy { 94 | PARALLEL 95 | }; 96 | 97 | template 98 | struct increasing_policy { 99 | static constexpr std::size_t begin(){ 100 | return S; 101 | } 102 | 103 | static constexpr bool has_next(std::size_t /*i*/, std::size_t d, measure_result duration){ 104 | return SP == stop_policy::STOP ? d != E : duration.mean < E; 105 | } 106 | 107 | static constexpr std::size_t next(std::size_t /*i*/, std::size_t d){ 108 | return d * M + A; 109 | } 110 | }; 111 | 112 | template 113 | struct values_policy { 114 | static std::size_t begin(){ 115 | std::array values{{SS...}}; 116 | return values[0]; 117 | } 118 | 119 | static bool has_next(std::size_t i, std::size_t /*d*/, measure_result /*duration*/){ 120 | return (i + 1) < sizeof...(SS); 121 | } 122 | 123 | static std::size_t next(std::size_t i, std::size_t /*d*/){ 124 | std::array values{{SS...}}; 125 | return values[i+1]; 126 | } 127 | }; 128 | 129 | template 130 | struct nary_policy { 131 | template //Simply to fake debug symbols for auto 132 | static cpp14_constexpr auto begin(){ 133 | return std::make_tuple(Policy::begin()...); 134 | } 135 | 136 | template 137 | static cpp14_constexpr bool has_next(std::size_t i, Tuple d, measure_result duration){ 138 | return detail::has_next::value>, Policy...>::value(i, d, duration); 139 | } 140 | 141 | template 142 | static cpp14_constexpr auto next(std::size_t i, Tuple d){ 143 | return detail::next::value>, Policy...>::value(i, d); 144 | } 145 | }; 146 | 147 | using std_stop_policy = increasing_policy<10, 1000000, 0, 10, stop_policy::STOP>; 148 | using std_timeout_policy = increasing_policy<10, 1000, 0, 10, stop_policy::TIMEOUT>; 149 | 150 | template 151 | using simple_nary_policy = nary_policy; 152 | 153 | inline std::string size_to_string(std::size_t t){ 154 | return std::to_string(t); 155 | } 156 | 157 | template 158 | std::string size_to_string(Tuple t){ 159 | return detail::tuple_to_string::value>>::value(t);; 160 | } 161 | 162 | inline std::size_t size_to_eff(std::size_t t){ 163 | return t; 164 | } 165 | 166 | template 167 | std::size_t size_to_eff(Tuple t){ 168 | return detail::tuple_to_eff::value>>::value(t);; 169 | } 170 | 171 | } //end of namespace cpm 172 | 173 | #endif //CPM_POLICY_HPP 174 | -------------------------------------------------------------------------------- /include/cpm/random.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_RANDOM_HPP 9 | #define CPM_RANDOM_HPP 10 | 11 | #include 12 | 13 | #ifdef CPM_PARALLEL_RANDOMIZE 14 | #include 15 | #include 16 | #endif 17 | 18 | namespace cpm { 19 | 20 | #ifndef CPM_PARALLEL_THRESHOLD 21 | #define CPM_PARALLEL_THRESHOLD 10000 22 | #endif 23 | 24 | #ifndef CPM_PARALLEL_THREADS 25 | //We are assuming Hyper-Threading by default 26 | #define CPM_PARALLEL_THREADS std::thread::hardware_concurrency() / 2 27 | #endif 28 | 29 | #ifdef CPM_PARALLEL_RANDOMIZE 30 | 31 | #ifndef CPM_FAST_RANDOMIZE 32 | 33 | template 34 | void randomize_double(T& container){ 35 | if(container.size() > CPM_PARALLEL_THRESHOLD){ 36 | const std::size_t n = CPM_PARALLEL_THREADS; //We are assuming Hyper-Threading 37 | const std::size_t p = container.size() / n; 38 | 39 | std::vector> futures; 40 | 41 | for(std::size_t i = 0; i < n; ++i){ 42 | futures.push_back(std::async(std::launch::async, [&](std::size_t ii){ 43 | static std::random_device rd; 44 | static std::mt19937_64 rand_engine(rd()); 45 | static std::uniform_real_distribution real_distribution(-10000.0, 10000.0); 46 | static auto generator = std::bind(real_distribution, rand_engine); 47 | 48 | for(std::size_t j = p * ii; j < p * (ii + 1); ++j){ 49 | container[j] = generator(); 50 | } 51 | }, i)); 52 | } 53 | 54 | static std::random_device rd; 55 | static std::mt19937_64 rand_engine(rd()); 56 | static std::uniform_real_distribution real_distribution(-10000.0, 10000.0); 57 | static auto generator = std::bind(real_distribution, rand_engine); 58 | 59 | for(std::size_t j = container.size() - container.size() % p; j < container.size(); ++j){ 60 | container[j] = generator(); 61 | } 62 | } else { 63 | static std::random_device rd; 64 | static std::mt19937_64 rand_engine(rd()); 65 | static std::uniform_real_distribution real_distribution(-10000.0, 10000.0); 66 | static auto generator = std::bind(real_distribution, rand_engine); 67 | 68 | for(auto& v : container){ 69 | v = generator(); 70 | } 71 | } 72 | } 73 | 74 | #else 75 | 76 | template 77 | void randomize_double(T& container){ 78 | static std::random_device rd; 79 | static std::mt19937_64 rand_engine(rd()); 80 | static std::uniform_real_distribution real_distribution(-10000.0, 10000.0); 81 | static auto generator = std::bind(real_distribution, rand_engine); 82 | 83 | const auto a = generator(); 84 | const auto b = generator(); 85 | const auto c = generator(); 86 | 87 | if(container.size() > CPM_PARALLEL_THRESHOLD){ 88 | const std::size_t n = CPM_PARALLEL_THREADS; //We are assuming Hyper-Threading 89 | const std::size_t p = container.size() / n; 90 | 91 | std::vector> futures; 92 | 93 | for(std::size_t i = 0; i < n; ++i){ 94 | futures.push_back(std::async(std::launch::async, [&](std::size_t ii){ 95 | for(std::size_t j = p * ii; j < p * (ii + 1); ++j){ 96 | container[j] = a + (j * b) + (-j * c); 97 | } 98 | }, i)); 99 | } 100 | 101 | for(std::size_t j = container.size() - container.size() % p; j < container.size(); ++j){ 102 | container[j] = a + (j * b) + (-j * c); 103 | } 104 | } else { 105 | std::size_t i = 0; 106 | for(auto& v : container){ 107 | v = a + (i * b) + (-i * c); 108 | ++i; 109 | } 110 | } 111 | } 112 | 113 | #endif //CPM_FAST_RANDOMIZE 114 | 115 | #else 116 | 117 | #ifndef CPM_FAST_RANDOMIZE 118 | 119 | template 120 | void randomize_double(T& container){ 121 | static std::random_device rd; 122 | static std::mt19937_64 rand_engine(rd()); 123 | static std::uniform_real_distribution real_distribution(-10000.0, 10000.0); 124 | static auto generator = std::bind(real_distribution, rand_engine); 125 | 126 | for(auto& v : container){ 127 | v = generator(); 128 | } 129 | } 130 | 131 | #else 132 | 133 | template 134 | void randomize_double(T& container){ 135 | static std::random_device rd; 136 | static std::mt19937_64 rand_engine(rd()); 137 | static std::uniform_real_distribution real_distribution(-10000.0, 10000.0); 138 | static auto generator = std::bind(real_distribution, rand_engine); 139 | 140 | auto a = generator(); 141 | auto b = generator(); 142 | auto c = generator(); 143 | 144 | std::size_t i = 0; 145 | for(auto& v : container){ 146 | v = a + (i * b) + (-i * c); 147 | ++i; 148 | } 149 | } 150 | 151 | #endif //CPM_FAST_RANDOMIZE 152 | 153 | #endif //CPM_PARALLEL_RANDOMIZE 154 | 155 | //Functions used by the benchmark 156 | 157 | #ifdef CPM_NO_RANDOM_INITIALIZATION 158 | 159 | template 160 | void random_init(TT&&... /*values*/){ 161 | /* NOP */ 162 | } 163 | 164 | template 165 | void random_init_each(TT&&... /*values*/){ 166 | /* NOP */ 167 | } 168 | 169 | #else 170 | 171 | inline void random_init(){} 172 | 173 | template::value, int> = 42 > 174 | void random_init(T1& container){ 175 | randomize_double(container); 176 | } 177 | 178 | template 179 | void random_init(T1& value, TT&... values){ 180 | random_init(value); 181 | random_init(values...); 182 | } 183 | 184 | template 185 | void random_init_each(Tuple& data, std::index_sequence /*indices*/){ 186 | using cpm::random_init; 187 | random_init(std::get(data)...); 188 | } 189 | 190 | #endif 191 | 192 | #ifdef CPM_NO_RANDOMIZATION 193 | 194 | template 195 | void randomize(TT&... /*values*/){} 196 | 197 | template 198 | void randomize_each(Tuple& /*data*/, std::index_sequence /*indices*/){} 199 | 200 | #else 201 | 202 | inline void randomize(){} 203 | 204 | template::value, int> = 42 > 205 | void randomize(T1& container){ 206 | randomize_double(container); 207 | } 208 | 209 | template 210 | void randomize(T1& value, TT&... values){ 211 | randomize(value); 212 | randomize(values...); 213 | } 214 | 215 | template 216 | void randomize_each(Tuple& data, std::index_sequence /*indices*/){ 217 | using cpm::randomize; 218 | randomize(std::get(data)...); 219 | } 220 | 221 | #endif 222 | 223 | } //end of namespace cpm 224 | 225 | #endif //CPM_RANDOM_HPP 226 | -------------------------------------------------------------------------------- /include/cpm/rapidjson.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_RAPIDJSON_HPP 9 | #define CPM_RAPIDJSON_HPP 10 | 11 | #include "rapidjson/document.h" 12 | #include "rapidjson/filereadstream.h" 13 | #include "rapidjson/error/en.h" 14 | 15 | //Allow range-based for loop on rapidjson objects 16 | 17 | namespace rapidjson { 18 | 19 | inline auto begin(rapidjson::Value& value){ 20 | return value.Begin(); 21 | } 22 | 23 | inline auto end(rapidjson::Value& value){ 24 | return value.End(); 25 | } 26 | 27 | inline auto begin(const rapidjson::Value& value){ 28 | return value.Begin(); 29 | } 30 | 31 | inline auto end(const rapidjson::Value& value){ 32 | return value.End(); 33 | } 34 | 35 | } //end of namespace rapidjson 36 | 37 | #endif //CPM_RAPIDJSON_HPP 38 | -------------------------------------------------------------------------------- /include/cpm/raw_theme.hpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #ifndef CPM_RAW_THEME_HPP 9 | #define CPM_RAW_THEME_HPP 10 | 11 | #include "cpm/io.hpp" 12 | #include "cpm/data.hpp" 13 | 14 | namespace cpm { 15 | 16 | struct raw_theme { 17 | const cpm::reports_data& data; 18 | cxxopts::Options& options; 19 | std::ostream& stream; 20 | 21 | std::string current_compiler; 22 | std::string current_configuration; 23 | 24 | raw_theme(const reports_data& data, cxxopts::Options& options, std::ostream& stream, std::string compiler, std::string configuration) 25 | : data(data), options(options), stream(stream), current_compiler(std::move(compiler)), current_configuration(std::move(configuration)) {} 26 | 27 | void include(){} 28 | void header(){} 29 | void footer(){} 30 | 31 | void before_information(std::string name){ 32 | stream << "

" << name << "

\n"; 33 | stream << "
    \n"; 34 | } 35 | 36 | void after_information(){ 37 | stream << "
\n"; 38 | } 39 | 40 | void compiler_buttons(){ 41 | stream << "
\n"; 42 | stream << R"=====(Select compiler: )====="; 43 | for(auto& compiler : data.compilers){ 44 | stream << "" << compiler << "\n"; 45 | } 46 | stream << "
\n"; 47 | } 48 | 49 | void configuration_buttons(){ 50 | stream << "
\n"; 51 | stream << R"=====(configuration: )====="; 52 | for(auto& configuration : data.configurations){ 53 | stream << "" << configuration << "\n"; 54 | } 55 | stream << "
\n"; 56 | } 57 | 58 | void after_buttons(){ 59 | //Nothing to be done here 60 | } 61 | 62 | void before_graph(std::size_t id){ 63 | stream << "
\n"; 64 | } 65 | 66 | void after_graph(){} 67 | 68 | void before_result(const std::string& title, bool /*sub */, const std::vector& /*documents*/){ 69 | stream << "

" << title << "

\n"; 70 | } 71 | 72 | void after_result(){} 73 | 74 | void before_sub_graphs(std::size_t /*id*/, std::vector /*graphs*/){} 75 | 76 | void after_sub_graphs(){} 77 | 78 | void before_sub_graph(std::size_t id, std::size_t sub){ 79 | stream << "
\n"; 80 | } 81 | 82 | void after_sub_graph(){} 83 | 84 | void before_summary(){ 85 | stream << "\n"; 86 | } 87 | 88 | void after_summary(){ 89 | stream << "
\n"; 90 | } 91 | 92 | void before_sub_summary(std::size_t /*id*/, std::size_t /*sub*/){ 93 | stream << "\n"; 94 | } 95 | 96 | void after_sub_summary(){ 97 | stream << "
\n"; 98 | } 99 | 100 | void cell(const std::string& v){ 101 | stream << "" << v << "\n"; 102 | } 103 | 104 | void red_cell(const std::string& v){ 105 | stream << "" << v << "\n"; 106 | } 107 | 108 | void green_cell(const std::string& v){ 109 | stream << "" << v << "\n"; 110 | } 111 | 112 | template 113 | raw_theme& operator<<(const T& v){ 114 | stream << v; 115 | return *this; 116 | } 117 | }; 118 | 119 | } //end of namespace cpm 120 | 121 | #endif //CPM_RAW_THEME_HPP 122 | -------------------------------------------------------------------------------- /samples.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p results 4 | 5 | cp examples/full.cpp examples/full.cpp.bak 6 | 7 | export CXX=clang++ 8 | export LD=clang++ 9 | 10 | sed -i '14s/.*/constexpr const double factor = 1.1;/' examples/full.cpp 11 | make -B examples 12 | ./debug/bin/full --tag=1 --configuration="-O2" 13 | 14 | sed -i '14s/.*/constexpr const double factor = 1.0;/' examples/full.cpp 15 | make -B examples 16 | ./debug/bin/full --tag=2 --configuration="-O2" 17 | 18 | sed -i '14s/.*/constexpr const double factor = 0.9;/' examples/full.cpp 19 | make -B examples 20 | ./debug/bin/full --tag=1 --configuration="-O3" 21 | 22 | sed -i '14s/.*/constexpr const double factor = 0.7;/' examples/full.cpp 23 | make -B examples 24 | ./debug/bin/full --tag=2 --configuration="-O3" 25 | 26 | export CXX=g++ 27 | export LD=g++ 28 | 29 | sed -i '14s/.*/constexpr const double factor = 1.0;/' examples/full.cpp 30 | make -B examples 31 | ./debug/bin/full --tag=1 --configuration="-O2" 32 | 33 | sed -i '14s/.*/constexpr const double factor = 0.8;/' examples/full.cpp 34 | make -B examples 35 | ./debug/bin/full --tag=2 --configuration="-O2" 36 | 37 | sed -i '14s/.*/constexpr const double factor = 0.8;/' examples/full.cpp 38 | make -B examples 39 | ./debug/bin/full --tag=1 --configuration="-O3" 40 | 41 | sed -i '14s/.*/constexpr const double factor = 0.65;/' examples/full.cpp 42 | make -B examples 43 | ./debug/bin/full --tag=2 --configuration="-O3" 44 | 45 | mv -f examples/full.cpp.bak examples/full.cpp 46 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=cpm 2 | sonar.projectName=cpm 3 | sonar.projectVersion=1.0 4 | 5 | sonar.sourceEncoding=UTF-8 6 | sonar.sources=include,src,examples 7 | sonar.language=c++ 8 | 9 | sonar.cxx.cppcheck.reportPath=cppcheck_report.xml 10 | -------------------------------------------------------------------------------- /src/cpm.cpp: -------------------------------------------------------------------------------- 1 | //======================================================================= 2 | // Copyright (c) 2015-2016 Baptiste Wicht 3 | // Distributed under the terms of the MIT License. 4 | // (See accompanying file LICENSE or copy at 5 | // http://opensource.org/licenses/MIT) 6 | //======================================================================= 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "cxxopts.hpp" 19 | 20 | #include "cpm/io.hpp" 21 | #include "cpm/rapidjson.hpp" 22 | #include "cpm/data.hpp" 23 | #include "cpm/raw_theme.hpp" 24 | #include "cpm/bootstrap_theme.hpp" 25 | #include "cpm/bootstrap_tabs_theme.hpp" 26 | #include "cpm/duration.hpp" 27 | 28 | namespace { 29 | 30 | using json_value = const rapidjson::Value&; 31 | 32 | bool str_equal(const char* lhs, const char* rhs){ 33 | return std::strcmp(lhs, rhs) == 0; 34 | } 35 | 36 | std::string dark_unica_theme = 37 | #include "dark_unica.inc.js" 38 | ; 39 | 40 | std::string strip_tags(const std::string& name){ 41 | auto open = std::count(name.begin(), name.end(), '['); 42 | auto close = std::count(name.begin(), name.end(), ']'); 43 | 44 | std::string stripped; 45 | if(open == close && open > 0){ 46 | stripped = std::string{name.begin(), name.begin() + name.find('[')}; 47 | } else { 48 | stripped = name; 49 | } 50 | 51 | // Trim 52 | stripped.erase(std::find_if(stripped.rbegin(), stripped.rend(), std::not1(std::ptr_fun(std::isspace))).base(), stripped.end()); 53 | stripped.erase(stripped.begin(), std::find_if(stripped.begin(), stripped.end(), std::not1(std::ptr_fun(std::isspace)))); 54 | 55 | return stripped; 56 | } 57 | 58 | 59 | bool strip_equal(const std::string& lhs, const std::string& rhs){ 60 | return strip_tags(lhs) == strip_tags(rhs); 61 | } 62 | 63 | cpm::document_t read_document(const std::string& folder, const std::string& file){ 64 | FILE* pFile = fopen((folder + "/" + file).c_str(), "rb"); 65 | char buffer[65536]; 66 | 67 | rapidjson::FileReadStream is(pFile, buffer, sizeof(buffer)); 68 | cpm::document_t doc; 69 | doc.ParseStream<0>(is); 70 | 71 | return doc; 72 | } 73 | 74 | std::vector read(const std::string& source_folder, cxxopts::Options& options){ 75 | std::vector documents; 76 | 77 | struct dirent* entry; 78 | DIR* dp = opendir(source_folder.c_str()); 79 | 80 | if(!dp){ 81 | return documents; 82 | } 83 | 84 | while((entry = readdir(dp))){ 85 | if(str_equal(entry->d_name, ".") || str_equal(entry->d_name, "..")){ 86 | continue; 87 | } 88 | 89 | if(entry->d_type == DT_REG){ 90 | intel_decltype_auto doc = read_document(source_folder, entry->d_name); 91 | if(doc.HasParseError()){ 92 | std::cout 93 | << "Impossible to read document " << entry->d_name << ":" << doc.GetErrorOffset() 94 | << ", parse error: " << rapidjson::GetParseError_En(doc.GetParseError()) << std::endl; 95 | } else { 96 | documents.push_back(std::move(doc)); 97 | } 98 | } 99 | } 100 | 101 | if(options.count("sort-by-tag")){ 102 | std::sort(documents.begin(), documents.end(), 103 | [](cpm::document_t& lhs, cpm::document_t& rhs){ 104 | if(std::string(lhs["tag"].GetString()) < std::string(rhs["tag"].GetString())){ 105 | return true; 106 | } else if(std::string(lhs["tag"].GetString()) > std::string(rhs["tag"].GetString())){ 107 | return false; 108 | } else { 109 | //If same tag, sort by time 110 | return lhs["timestamp"].GetInt() < rhs["timestamp"].GetInt(); 111 | } 112 | } 113 | ); 114 | } else { 115 | std::sort(documents.begin(), documents.end(), 116 | [](cpm::document_t& lhs, cpm::document_t& rhs){ return lhs["timestamp"].GetInt() < rhs["timestamp"].GetInt(); }); 117 | } 118 | 119 | return documents; 120 | } 121 | 122 | //Select relevant documents 123 | std::vector select_documents(const std::vector& documents, const cpm::document_t& base){ 124 | std::vector relevant; 125 | 126 | for(auto& doc : documents){ 127 | //Two documents are relevant if the configuration is the same 128 | if(str_equal(doc["compiler"].GetString(), base["compiler"].GetString()) && str_equal(doc["configuration"].GetString(), base["configuration"].GetString())){ 129 | relevant.push_back(std::cref(doc)); 130 | } 131 | } 132 | 133 | return relevant; 134 | } 135 | 136 | template 137 | void header(Theme& theme){ 138 | theme << "\n"; 139 | theme << "\n"; 140 | theme << "\n"; 141 | 142 | //Metas 143 | theme << "\n"; 144 | theme << "\n"; 145 | theme << "\n"; 146 | 147 | theme << "" << theme.data.documents.back()["name"].GetString() << "\n"; 148 | 149 | //We need JQuery 150 | theme << "\n"; 151 | theme << "\n"; 152 | 153 | //We need Highcharts 154 | theme << "\n"; 155 | theme << "\n"; 156 | 157 | theme.include(); 158 | 159 | theme << "\n"; 160 | 161 | theme << "\n"; 162 | 163 | theme.header(); 164 | } 165 | 166 | template 167 | void footer(Theme& theme){ 168 | theme.footer(); 169 | 170 | theme << "\n"; 171 | theme << "\n"; 172 | } 173 | 174 | template 175 | void information(Theme& theme, const cpm::document_t& doc){ 176 | theme.before_information(doc["name"].GetString()); 177 | 178 | theme << "
  • Tag: " << doc["tag"].GetString() << "
  • \n"; 179 | theme << "
  • Compiler: " << doc["compiler"].GetString() << "
  • \n"; 180 | theme << "
  • Configuration: " << doc["configuration"].GetString() << "
  • \n"; 181 | theme << "
  • Operating System: " << doc["os"].GetString() << "
  • \n"; 182 | theme << "
  • Time: " << doc["time"].GetString() << "
  • \n"; 183 | 184 | theme.after_information(); 185 | } 186 | 187 | template 188 | void compiler_buttons(Theme& theme){ 189 | if(theme.data.compilers.size() > 1){ 190 | theme.compiler_buttons(); 191 | } 192 | } 193 | 194 | template 195 | void configuration_buttons(Theme& theme){ 196 | if(theme.data.configurations.size() > 1){ 197 | theme.configuration_buttons(); 198 | } 199 | } 200 | 201 | template 202 | void after_buttons(Theme& theme){ 203 | theme.after_buttons(); 204 | } 205 | 206 | template 207 | void start_graph(Theme& theme, const std::string& id, const std::string& title){ 208 | theme << "\n"; 221 | } 222 | 223 | template 224 | void y_axis_configuration(Theme& theme){ 225 | theme << "yAxis: {\n"; 226 | 227 | if(theme.options.count("mflops-graphs")){ 228 | theme << "title: { text: 'Throughput [MFlops/s]' },\n"; 229 | } else { 230 | theme << "title: { text: 'Time [ns]' },\n"; 231 | } 232 | 233 | theme << "plotLines: [{ value: 0, width: 1, color: '#808080'}]\n"; 234 | theme << "},\n"; 235 | 236 | if(theme.options.count("mflops-graphs")){ 237 | theme << "tooltip: { valueSuffix: 'MFlops/s' },\n"; 238 | } else { 239 | theme << "tooltip: { valueSuffix: 'ns' },\n"; 240 | } 241 | } 242 | 243 | template 244 | void json_array_string(Theme& theme, const Elements& elements){ 245 | theme << "["; 246 | 247 | std::string comma = ""; 248 | for(auto& element : elements){ 249 | theme << comma << "'" << element << "'"; 250 | comma = ","; 251 | } 252 | 253 | theme << "]"; 254 | } 255 | 256 | template 257 | void json_array_value(Theme& theme, const Elements& elements){ 258 | theme << "["; 259 | 260 | std::string comma = ""; 261 | for(auto& element : elements){ 262 | theme << comma << element; 263 | comma = ","; 264 | } 265 | 266 | theme << "]"; 267 | } 268 | 269 | template 270 | std::vector string_collect(const T& parent, const char* attr){ 271 | std::vector values; 272 | for(auto& r : parent){ 273 | values.emplace_back(strip_tags(r[attr].GetString())); 274 | } 275 | return values; 276 | } 277 | 278 | template 279 | std::vector int_collect(const T& parent, const char* attr){ 280 | std::vector values; 281 | for(auto& r : parent){ 282 | values.emplace_back(r[attr].GetInt()); 283 | } 284 | return values; 285 | } 286 | 287 | template 288 | std::vector double_collect(const T& parent, const char* attr){ 289 | std::vector values; 290 | for(auto& r : parent){ 291 | values.emplace_back(r[attr].GetDouble()); 292 | } 293 | return values; 294 | } 295 | 296 | template 297 | const char* value_key_name(Theme& theme){ 298 | if(theme.options.count("mflops-graphs")){ 299 | return "throughput_f"; 300 | } else { 301 | return "mean"; 302 | } 303 | } 304 | 305 | template 306 | void generate_run_graph(Theme& theme, std::size_t& id, const rapidjson::Value& result){ 307 | theme.before_graph(id); 308 | 309 | std::string title = std::string("Last run") + 310 | (theme.options.count("pages") ? std::string() : std::string(": ") + strip_tags(result["title"].GetString())); 311 | 312 | start_graph(theme, std::string("chart_") + std::to_string(id), title); 313 | 314 | theme << "xAxis: { categories: \n"; 315 | 316 | json_array_string(theme, string_collect(result["results"], "size")); 317 | 318 | theme << "},\n"; 319 | 320 | y_axis_configuration(theme); 321 | 322 | theme << "legend: { enabled: false },\n"; 323 | 324 | theme << "series: [\n"; 325 | theme << "{\n"; 326 | 327 | theme << "name: '',\n"; 328 | theme << "data: "; 329 | 330 | json_array_value(theme, double_collect(result["results"], value_key_name(theme))); 331 | 332 | theme << "\n}\n"; 333 | theme << "]\n"; 334 | 335 | end_graph(theme); 336 | theme.after_graph(); 337 | ++id; 338 | } 339 | 340 | template 341 | void generate_compare_graph(Theme& theme, std::size_t& id, json_value base_result, const std::string& title, const char* attr, Filter f){ 342 | theme.before_graph(id); 343 | 344 | std::string graph_title = title + 345 | (theme.options.count("pages") ? std::string() : std::string(": ") + strip_tags(base_result["title"].GetString())); 346 | start_graph(theme, std::string("chart_") + std::to_string(id), graph_title); 347 | 348 | theme << "xAxis: { categories: \n"; 349 | 350 | json_array_string(theme, string_collect(base_result["results"], "size")); 351 | 352 | theme << "},\n"; 353 | 354 | y_axis_configuration(theme); 355 | 356 | theme << "legend: { align: 'left', verticalAlign: 'top', floating: false, borderWidth: 0, y: 20 },\n"; 357 | 358 | theme << "series: [\n"; 359 | 360 | std::string comma = ""; 361 | for(auto& document : theme.data.documents){ 362 | if(f(document)){ 363 | for(auto& result : document["results"]){ 364 | if(strip_equal(result["title"].GetString(), base_result["title"].GetString())){ 365 | theme << comma << "{\n"; 366 | theme << "name: '" << document[attr].GetString() << "',\n"; 367 | theme << "data: "; 368 | 369 | json_array_value(theme, double_collect(result["results"], value_key_name(theme))); 370 | 371 | theme << "\n}\n"; 372 | 373 | comma = ","; 374 | } 375 | } 376 | } 377 | } 378 | 379 | theme << "]\n"; 380 | 381 | end_graph(theme); 382 | theme.after_graph(); 383 | ++id; 384 | } 385 | 386 | bool is_compiler_relevant(const cpm::document_t& base, const cpm::document_t& doc){ 387 | return str_equal(doc["tag"].GetString(), base["tag"].GetString()) 388 | && str_equal(doc["configuration"].GetString(), base["configuration"].GetString()); 389 | } 390 | 391 | bool is_configuration_relevant(const cpm::document_t& base, const cpm::document_t& doc){ 392 | return str_equal(doc["tag"].GetString(), base["tag"].GetString()) 393 | && str_equal(doc["compiler"].GetString(), base["compiler"].GetString()); 394 | } 395 | 396 | auto compiler_filter(const cpm::document_t& base){ 397 | return [&base](const cpm::document_t& doc){ 398 | return is_compiler_relevant(base, doc); 399 | }; 400 | } 401 | 402 | auto configuration_filter(const cpm::document_t& base){ 403 | return [&base](const cpm::document_t& doc){ 404 | return is_configuration_relevant(base, doc); 405 | }; 406 | } 407 | 408 | template 409 | void generate_compiler_graph(Theme& theme, std::size_t& id, const rapidjson::Value& base_result, const cpm::document_t& base){ 410 | generate_compare_graph(theme, id, base_result, "Compiler", "compiler", compiler_filter(base)); 411 | } 412 | 413 | template 414 | void generate_configuration_graph(Theme& theme, std::size_t& id, const rapidjson::Value& base_result, const cpm::document_t& base){ 415 | generate_compare_graph(theme, id, base_result, "Configuration", "configuration", configuration_filter(base)); 416 | } 417 | 418 | template 419 | std::pair find_same_duration(Theme& theme, const rapidjson::Value& base_result, const rapidjson::Value& r, const cpm::document_t& doc){ 420 | for(auto& p_r : doc["results"]){ 421 | if(strip_equal(p_r["title"].GetString(), base_result["title"].GetString())){ 422 | for(auto& p_r_r : p_r["results"]){ 423 | if(str_equal(p_r_r["size"].GetString(), r["size"].GetString())){ 424 | return std::make_pair(true, p_r_r[value_key_name(theme)].GetDouble()); 425 | } 426 | } 427 | } 428 | } 429 | 430 | return std::make_pair(false, 0); 431 | } 432 | 433 | template 434 | double add_compare_cell(Theme& theme, double current, double previous){ 435 | double diff = 0.0; 436 | 437 | if(theme.options.count("mflops-graphs")){ 438 | if(previous == 0.0){ 439 | if(current < 0.0){ 440 | theme.red_cell(std::to_string(current) + "%"); 441 | } else if(current > 0.0){ 442 | theme.green_cell("+" + std::to_string(current) + "%"); 443 | } else { 444 | theme.cell("+0%"); 445 | } 446 | } else { 447 | if(current < previous){ 448 | diff = -1.0 * (100.0 * (static_cast(previous) / current) - 100.0); 449 | theme.red_cell(std::to_string(diff) + "%"); 450 | } else if(current > previous){ 451 | diff = 1.0 * (100.0 * (static_cast(current) / previous) - 100.0); 452 | theme.green_cell("+" + std::to_string(diff) + "%"); 453 | } else { 454 | theme.cell("+0%"); 455 | } 456 | } 457 | } else { 458 | if(previous == 0.0){ 459 | if(current < 0.0){ 460 | theme.green_cell(std::to_string(current) + "%"); 461 | } else if(current > 0.0){ 462 | theme.red_cell("+" + std::to_string(current) + "%"); 463 | } else { 464 | theme.cell("+0%"); 465 | } 466 | } else { 467 | if(current < previous){ 468 | diff = -1.0 * (100.0 * (static_cast(previous) / current) - 100.0); 469 | theme.green_cell(std::to_string(diff) + "%"); 470 | } else if(current > previous){ 471 | diff = 1.0 * (100.0 * (static_cast(current) / previous) - 100.0); 472 | theme.red_cell("+" + std::to_string(diff) + "%"); 473 | } else { 474 | theme.cell("+0%"); 475 | } 476 | } 477 | } 478 | 479 | return diff; 480 | } 481 | 482 | template 483 | std::pair compare(Theme& theme, const rapidjson::Value& base_result, const rapidjson::Value& r, const cpm::document_t& doc){ 484 | bool found; 485 | int previous; 486 | std::tie(found, previous) = find_same_duration(theme, base_result, r, doc); 487 | 488 | if(found){ 489 | auto current = r[value_key_name(theme)].GetDouble(); 490 | 491 | double diff = add_compare_cell(theme, current, previous); 492 | return std::make_pair(true, diff); 493 | } else { 494 | return std::make_pair(false, 0.0); 495 | } 496 | } 497 | 498 | template 499 | void summary_header(Theme& theme){ 500 | theme << "\n"; 501 | theme << "Size\n"; 502 | theme << "Time\n"; 503 | 504 | if(theme.options.count("mflops")){ 505 | theme << "Throughput [Flops/s]\n"; 506 | } else { 507 | theme << "Throughput [E/s]\n"; 508 | } 509 | 510 | theme << "Previous\n"; 511 | theme << "First\n"; 512 | 513 | if(theme.data.compilers.size() > 1){ 514 | theme << "Best compiler\n"; 515 | theme << "Max difference\n"; 516 | } 517 | 518 | if(theme.data.configurations.size() > 1){ 519 | theme << "Best configuration\n"; 520 | theme << "Max difference\n"; 521 | } 522 | 523 | theme << "\n"; 524 | } 525 | 526 | template 527 | void summary_footer(Theme& theme, double previous_acc, double first_acc){ 528 | theme << "\n"; 529 | 530 | theme << " \n"; 531 | theme << " \n"; 532 | theme << " \n"; 533 | 534 | add_compare_cell(theme, previous_acc, 0.0); 535 | add_compare_cell(theme, first_acc, 0.0); 536 | 537 | if(theme.data.compilers.size() > 1){ 538 | theme.cell(" "); 539 | theme.cell(" "); 540 | } 541 | 542 | if(theme.data.configurations.size() > 1){ 543 | theme.cell(" "); 544 | theme.cell(" "); 545 | } 546 | 547 | theme << "\n"; 548 | } 549 | 550 | template 551 | void generate_summary_table(Theme& theme, const rapidjson::Value& base_result, const cpm::document_t& base){ 552 | theme.before_summary(); 553 | 554 | summary_header(theme); 555 | 556 | double previous_acc = 0; 557 | double first_acc = 0; 558 | 559 | bool flops = theme.options.count("mflops"); 560 | 561 | for(auto& r : base_result["results"]){ 562 | theme << "\n"; 563 | 564 | theme << "" << r["size"].GetString() << "\n"; 565 | theme << "" << r["mean"].GetDouble() << "\n"; 566 | 567 | if(flops){ 568 | theme << "" << cpm::throughput_str(r["throughput_f"].GetDouble()) << "\n"; 569 | } else { 570 | theme << "" << cpm::throughput_str(r["throughput_e"].GetDouble()) << "\n"; 571 | } 572 | 573 | bool previous_found = false; 574 | double diff = 0.0; 575 | 576 | auto documents = select_documents(theme.data.documents, base); 577 | 578 | for(std::size_t i = 0; i < documents.size() - 1; ++i){ 579 | if(&static_cast(documents[i+1]) == &base){ 580 | auto& doc = static_cast(documents[i]); 581 | std::tie(previous_found, diff) = compare(theme, base_result, r, doc); 582 | 583 | if(previous_found){ 584 | previous_acc += diff; 585 | break; 586 | } 587 | } 588 | } 589 | 590 | if(!previous_found){ 591 | theme.cell("N/A"); 592 | } 593 | 594 | previous_found = false; 595 | 596 | if(documents.size() > 1){ 597 | auto& doc = static_cast(documents[0]); 598 | std::tie(previous_found, diff) = compare(theme, base_result, r, doc); 599 | 600 | first_acc += diff; 601 | } 602 | 603 | if(!previous_found){ 604 | theme.cell("N/A"); 605 | } 606 | 607 | if(theme.data.compilers.size() > 1){ 608 | std::string best_compiler = base["compiler"].GetString(); 609 | auto best = r[value_key_name(theme)].GetDouble(); 610 | auto worst = r[value_key_name(theme)].GetDouble(); 611 | 612 | for(auto& doc : theme.data.documents){ 613 | if(is_compiler_relevant(base, doc)){ 614 | bool found; 615 | int duration; 616 | std::tie(found, duration) = find_same_duration(theme, base_result, r, doc); 617 | 618 | if(found){ 619 | if(flops){ 620 | if(duration > best){ 621 | best = duration; 622 | best_compiler = doc["compiler"].GetString(); 623 | } else if(duration < worst){ 624 | worst = duration; 625 | } 626 | } else { 627 | if(duration < best){ 628 | best = duration; 629 | best_compiler = doc["compiler"].GetString(); 630 | } else if(duration > worst){ 631 | worst = duration; 632 | } 633 | } 634 | } 635 | } 636 | } 637 | 638 | theme.cell(best_compiler); 639 | 640 | auto max_diff = std::abs(100.0 * (static_cast(worst) / best) - 100.0); 641 | 642 | theme.cell(std::to_string(max_diff) + "%"); 643 | } 644 | 645 | if(theme.data.configurations.size() > 1){ 646 | std::string best_configuration = base["configuration"].GetString(); 647 | auto best = r[value_key_name(theme)].GetDouble(); 648 | auto worst = r[value_key_name(theme)].GetDouble(); 649 | 650 | for(auto& doc : theme.data.documents){ 651 | if(is_configuration_relevant(base, doc)){ 652 | bool found; 653 | int duration; 654 | std::tie(found, duration) = find_same_duration(theme, base_result, r, doc); 655 | 656 | if(found){ 657 | if(flops){ 658 | if(duration > best){ 659 | best = duration; 660 | best_configuration = doc["configuration"].GetString(); 661 | } else if(duration < worst){ 662 | worst = duration; 663 | } 664 | } else { 665 | if(duration < best){ 666 | best = duration; 667 | best_configuration = doc["configuration"].GetString(); 668 | } else if(duration > worst){ 669 | worst = duration; 670 | } 671 | } 672 | } 673 | } 674 | } 675 | 676 | theme.cell(best_configuration); 677 | 678 | auto max_diff = std::abs(100.0 * (static_cast(worst) / best) - 100.0); 679 | 680 | theme.cell(std::to_string(max_diff) + "%"); 681 | } 682 | 683 | theme << "\n"; 684 | } 685 | 686 | previous_acc /= base_result["results"].Size(); 687 | first_acc /= base_result["results"].Size(); 688 | 689 | summary_footer(theme, previous_acc, first_acc); 690 | 691 | theme.after_summary(); 692 | } 693 | 694 | template 695 | void generate_time_graph(Theme& theme, std::size_t& id, const rapidjson::Value& result, const std::vector& documents){ 696 | theme.before_graph(id); 697 | 698 | std::string graph_title = "Time" + 699 | (theme.options.count("pages") ? std::string() : std::string(": ") + strip_tags(result["title"].GetString())); 700 | start_graph(theme, std::string("chart_") + std::to_string(id), graph_title); 701 | 702 | theme << "xAxis: { type: 'datetime', title: { text: 'Date' } },\n"; 703 | 704 | y_axis_configuration(theme); 705 | 706 | if(!theme.options.count("time-sizes")){ 707 | theme << "legend: { enabled: false },\n"; 708 | } 709 | 710 | theme << "series: [\n"; 711 | 712 | if(theme.options.count("time-sizes")){ 713 | std::string comma = ""; 714 | for(auto& r : result["results"]){ 715 | theme << comma << "{\n"; 716 | 717 | theme << "name: '" << r["size"].GetString() << "',\n"; 718 | theme << "data: ["; 719 | 720 | std::string inner_comma = ""; 721 | 722 | for(auto& document_r : documents){ 723 | auto& document = static_cast(document_r); 724 | 725 | for(auto& o_result : document["results"]){ 726 | if(strip_equal(o_result["title"].GetString(), result["title"].GetString())){ 727 | for(auto& o_rr : o_result["results"]){ 728 | if(o_rr["size"].GetString() == r["size"].GetString()){ 729 | theme << inner_comma << "[" << size_t(document["timestamp"].GetInt()) * 1000 << ","; 730 | theme << o_rr[value_key_name(theme)].GetDouble() << "]"; 731 | inner_comma = ","; 732 | } 733 | } 734 | } 735 | } 736 | } 737 | 738 | theme << "]\n"; 739 | theme << "}\n"; 740 | comma =","; 741 | } 742 | } else { 743 | theme << "{\n"; 744 | 745 | theme << "name: '',\n"; 746 | theme << "data: ["; 747 | 748 | std::string comma = ""; 749 | 750 | for(auto& document_r : documents){ 751 | auto& document = static_cast(document_r); 752 | 753 | for(auto& o_result : document["results"]){ 754 | if(strip_equal(o_result["title"].GetString(), result["title"].GetString())){ 755 | theme << comma << "[" << size_t(document["timestamp"].GetInt()) * 1000 << ","; 756 | auto& o_r_results = o_result["results"]; 757 | theme << o_r_results[o_r_results.Size() - 1][value_key_name(theme)].GetDouble() << "]"; 758 | comma = ","; 759 | } 760 | } 761 | } 762 | 763 | theme << "]\n"; 764 | theme << "}\n"; 765 | } 766 | 767 | theme << "]\n"; 768 | 769 | end_graph(theme); 770 | theme.after_graph(); 771 | ++id; 772 | } 773 | 774 | std::vector gather_sizes(const rapidjson::Value& section){ 775 | std::vector sizes; 776 | std::set set_sizes; 777 | 778 | for(auto& r : section["results"]){ 779 | for(auto& rr : r["results"]){ 780 | auto s = rr["size"].GetString(); 781 | if(!set_sizes.count(s)){ 782 | set_sizes.insert(s); 783 | sizes.push_back(s); 784 | } 785 | } 786 | } 787 | 788 | return sizes; 789 | } 790 | 791 | template 792 | void generate_section_run_graph(Theme& theme, std::size_t& id, const rapidjson::Value& section){ 793 | theme.before_graph(id); 794 | 795 | std::string graph_title = "Last run" + 796 | (theme.options.count("pages") ? std::string() : std::string(": ") + strip_tags(section["name"].GetString())); 797 | start_graph(theme, std::string("chart_") + std::to_string(id), graph_title); 798 | 799 | theme << "xAxis: { categories: \n"; 800 | 801 | auto sizes = gather_sizes(section); 802 | 803 | json_array_string(theme, sizes); 804 | 805 | theme << "},\n"; 806 | 807 | y_axis_configuration(theme); 808 | 809 | theme << "legend: { align: 'left', verticalAlign: 'top', floating: false, borderWidth: 0, y: 20 },\n"; 810 | 811 | theme << "series: [\n"; 812 | 813 | std::string comma = ""; 814 | for(auto& r : section["results"]){ 815 | theme << comma << "{\n"; 816 | 817 | theme << "name: '" << strip_tags(r["name"].GetString()) << "',\n"; 818 | theme << "data: "; 819 | 820 | json_array_value(theme, double_collect(r["results"], value_key_name(theme))); 821 | 822 | theme << "\n}\n"; 823 | comma = ","; 824 | } 825 | 826 | theme << "]\n"; 827 | 828 | end_graph(theme); 829 | theme.after_graph(); 830 | ++id; 831 | } 832 | 833 | template 834 | void generate_section_time_graph(Theme& theme, std::size_t& id, const rapidjson::Value& section, const std::vector& documents){ 835 | theme.before_graph(id); 836 | 837 | std::string graph_title = "Time" + 838 | (theme.options.count("pages") ? std::string() : std::string(": ") + strip_tags(section["name"].GetString())); 839 | start_graph(theme, std::string("chart_") + std::to_string(id), graph_title); 840 | 841 | theme << "xAxis: { type: 'datetime', title: { text: 'Date' } },\n"; 842 | 843 | y_axis_configuration(theme); 844 | 845 | theme << "legend: { align: 'left', verticalAlign: 'top', floating: false, borderWidth: 0, y: 20 },\n"; 846 | 847 | theme << "series: [\n"; 848 | 849 | std::string comma = ""; 850 | for(auto& r : section["results"]){ 851 | theme << comma << "{\n"; 852 | 853 | theme << "name: '" << strip_tags(r["name"].GetString()) << "',\n"; 854 | theme << "data: ["; 855 | 856 | std::string comma_inner = ""; 857 | 858 | for(auto& r_doc_r : documents){ 859 | auto& r_doc = static_cast(r_doc_r); 860 | 861 | for(auto& r_section : r_doc["sections"]){ 862 | if(strip_equal(r_section["name"].GetString(), section["name"].GetString())){ 863 | for(auto& r_r : r_section["results"]){ 864 | if(strip_equal(r_r["name"].GetString(), r["name"].GetString())){ 865 | theme << comma_inner << "[" << size_t(r_doc["timestamp"].GetInt()) * 1000 << ","; 866 | auto& r_r_results = r_r["results"]; 867 | theme << r_r_results[r_r_results.Size() - 1][value_key_name(theme)].GetDouble() << "]"; 868 | comma_inner = ","; 869 | } 870 | } 871 | } 872 | } 873 | } 874 | 875 | theme << "]\n"; 876 | theme << "}\n"; 877 | comma = ","; 878 | } 879 | 880 | theme << "]\n"; 881 | 882 | end_graph(theme); 883 | theme.after_graph(); 884 | ++id; 885 | } 886 | 887 | template 888 | void generate_section_compare_graph(Theme& theme, std::size_t& id, const rapidjson::Value& section, const std::string& title, const char* attr, Filter f){ 889 | std::size_t sub_id = 0; 890 | 891 | theme.before_sub_graphs(id, string_collect(section["results"], "name")); 892 | 893 | for(auto& r : section["results"]){ 894 | theme.before_sub_graph(id, sub_id++); 895 | 896 | start_graph(theme, 897 | std::string("chart_") + std::to_string(id) + "-" + std::to_string(sub_id - 1), 898 | title + strip_tags(section["name"].GetString()) + "-" + strip_tags(r["name"].GetString())); 899 | 900 | theme << "xAxis: { categories: \n"; 901 | 902 | auto sizes = gather_sizes(section); 903 | 904 | json_array_string(theme, sizes); 905 | 906 | theme << "},\n"; 907 | 908 | y_axis_configuration(theme); 909 | 910 | theme << "legend: { align: 'left', verticalAlign: 'top', floating: false, borderWidth: 0, y: 20 },\n"; 911 | 912 | theme << "series: [\n"; 913 | 914 | std::string comma = ""; 915 | for(auto& document : theme.data.documents){ 916 | if(f(document)){ 917 | for(auto& o_section : document["sections"]){ 918 | if(strip_equal(o_section["name"].GetString(), section["name"].GetString())){ 919 | for(auto& o_r : o_section["results"]){ 920 | if(strip_equal(o_r["name"].GetString(), r["name"].GetString())){ 921 | theme << comma << "{\n"; 922 | theme << "name: '" << document[attr].GetString() << "',\n"; 923 | theme << "data: "; 924 | 925 | json_array_value(theme, double_collect(o_r["results"], value_key_name(theme))); 926 | 927 | theme << "\n}\n"; 928 | 929 | comma = ","; 930 | } 931 | } 932 | } 933 | } 934 | } 935 | } 936 | 937 | theme << "]\n"; 938 | 939 | end_graph(theme); 940 | 941 | theme.after_sub_graph(); 942 | } 943 | 944 | theme.after_sub_graphs(); 945 | 946 | ++id; 947 | } 948 | 949 | template 950 | void generate_section_compiler_graph(Theme& theme, std::size_t& id, const rapidjson::Value& section, const cpm::document_t& base){ 951 | generate_section_compare_graph(theme, id, section, "Compiler:", "compiler", compiler_filter(base)); 952 | } 953 | 954 | template 955 | void generate_section_configuration_graph(Theme& theme, std::size_t& id, const rapidjson::Value& section, const cpm::document_t& base){ 956 | generate_section_compare_graph(theme, id, section, "Configuration:", "configuration", configuration_filter(base)); 957 | } 958 | 959 | template 960 | std::pair find_same_duration_section(Theme& theme, json_value base_result, json_value base_section, json_value r, const cpm::document_t& doc){ 961 | for(auto& section : doc["sections"]){ 962 | if(strip_equal(section["name"].GetString(), base_section["name"].GetString())){ 963 | for(auto& result : section["results"]){ 964 | if(strip_equal(result["name"].GetString(), base_result["name"].GetString())){ 965 | for(auto& p_r_r : result["results"]){ 966 | if(str_equal(p_r_r["size"].GetString(), r["size"].GetString())){ 967 | return std::make_pair(true, p_r_r[value_key_name(theme)].GetDouble()); 968 | } 969 | } 970 | } 971 | } 972 | } 973 | } 974 | 975 | return std::make_pair(false, 0.0); 976 | } 977 | 978 | template 979 | std::pair compare_section(Theme& theme, json_value base_result, json_value base_section, json_value r, const cpm::document_t& doc){ 980 | bool found; 981 | int previous; 982 | std::tie(found, previous) = find_same_duration_section(theme, base_result, base_section, r, doc); 983 | 984 | if(found){ 985 | auto current = r[value_key_name(theme)].GetDouble(); 986 | 987 | double diff = add_compare_cell(theme, current, previous); 988 | return std::make_pair(true, diff); 989 | } 990 | 991 | return std::make_pair(false, 0.0); 992 | } 993 | 994 | template 995 | void generate_section_summary_table(Theme& theme, std::size_t id, json_value base_section, const cpm::document_t& base){ 996 | std::size_t sub_id = 0; 997 | theme.before_sub_graphs(id * 1000000, string_collect(base_section["results"], "name")); 998 | 999 | auto flops = theme.options.count("mflops"); 1000 | 1001 | for(auto& base_result : base_section["results"]){ 1002 | theme.before_sub_summary(id * 1000000, sub_id++); 1003 | 1004 | summary_header(theme); 1005 | 1006 | double previous_acc = 0; 1007 | double first_acc = 0; 1008 | 1009 | for(auto& r : base_result["results"]){ 1010 | theme << "\n"; 1011 | theme << "" << r["size"].GetString() << "\n"; 1012 | theme << "" << r["mean"].GetDouble() << "\n"; 1013 | 1014 | if(flops){ 1015 | theme << "" << cpm::throughput_str(r["throughput_f"].GetDouble()) << "\n"; 1016 | } else { 1017 | theme << "" << cpm::throughput_str(r["throughput_e"].GetDouble()) << "\n"; 1018 | } 1019 | 1020 | bool previous_found = false; 1021 | double diff = 0.0; 1022 | 1023 | auto documents = select_documents(theme.data.documents, base); 1024 | 1025 | for(std::size_t i = 0; i < documents.size() - 1; ++i){ 1026 | if(&static_cast(documents[i+1]) == &base){ 1027 | auto& doc = static_cast(documents[i]); 1028 | std::tie(previous_found, diff) = compare_section(theme, base_result, base_section, r, doc); 1029 | 1030 | if(previous_found){ 1031 | previous_acc += diff; 1032 | break; 1033 | } 1034 | } 1035 | } 1036 | 1037 | if(!previous_found){ 1038 | theme << "N/A\n"; 1039 | } 1040 | 1041 | previous_found = false; 1042 | 1043 | if(documents.size() > 1){ 1044 | auto& doc = static_cast(documents[0]); 1045 | std::tie(previous_found, diff) = compare_section(theme, base_result, base_section, r, doc); 1046 | 1047 | first_acc += diff; 1048 | } 1049 | 1050 | if(!previous_found){ 1051 | theme << "N/A\n"; 1052 | } 1053 | 1054 | if(theme.data.compilers.size() > 1){ 1055 | std::string best_compiler = base["compiler"].GetString(); 1056 | auto best = r[value_key_name(theme)].GetDouble(); 1057 | auto worst = r[value_key_name(theme)].GetDouble(); 1058 | 1059 | for(auto& doc : theme.data.documents){ 1060 | if(is_compiler_relevant(base, doc)){ 1061 | bool found; 1062 | int duration; 1063 | std::tie(found, duration) = find_same_duration_section(theme, base_result, base_section, r, doc); 1064 | 1065 | if(found){ 1066 | if(flops){ 1067 | if(duration > best){ 1068 | best = duration; 1069 | best_compiler = doc["compiler"].GetString(); 1070 | } else if(duration < worst){ 1071 | worst = duration; 1072 | } 1073 | } else { 1074 | if(duration < best){ 1075 | best = duration; 1076 | best_compiler = doc["compiler"].GetString(); 1077 | } else if(duration > worst){ 1078 | worst = duration; 1079 | } 1080 | } 1081 | } 1082 | } 1083 | } 1084 | 1085 | theme.cell(best_compiler); 1086 | 1087 | auto max_diff = std::abs(100.0 * (static_cast(worst) / best) - 100.0); 1088 | 1089 | theme.cell(std::to_string(max_diff) + "%"); 1090 | } 1091 | 1092 | if(theme.data.configurations.size() > 1){ 1093 | std::string best_configuration = base["configuration"].GetString(); 1094 | auto best = r[value_key_name(theme)].GetDouble(); 1095 | auto worst = r[value_key_name(theme)].GetDouble(); 1096 | 1097 | for(auto& doc : theme.data.documents){ 1098 | if(is_configuration_relevant(base, doc)){ 1099 | bool found; 1100 | int duration; 1101 | std::tie(found, duration) = find_same_duration_section(theme, base_result, base_section, r, doc); 1102 | 1103 | if(found){ 1104 | if(flops){ 1105 | if(duration > best){ 1106 | best = duration; 1107 | best_configuration = doc["configuration"].GetString(); 1108 | } else if(duration < worst){ 1109 | worst = duration; 1110 | } 1111 | } else { 1112 | if(duration < best){ 1113 | best = duration; 1114 | best_configuration = doc["configuration"].GetString(); 1115 | } else if(duration > worst){ 1116 | worst = duration; 1117 | } 1118 | } 1119 | } 1120 | } 1121 | } 1122 | 1123 | theme.cell(best_configuration); 1124 | 1125 | auto max_diff = std::abs(100.0 * (static_cast(worst) / best) - 100.0); 1126 | 1127 | theme.cell(std::to_string(max_diff) + "%"); 1128 | } 1129 | 1130 | theme << "\n"; 1131 | } 1132 | 1133 | previous_acc /= base_result["results"].Size(); 1134 | first_acc /= base_result["results"].Size(); 1135 | 1136 | summary_footer(theme, previous_acc, first_acc); 1137 | 1138 | theme.after_sub_summary(); 1139 | } 1140 | 1141 | theme.after_sub_graphs(); 1142 | } 1143 | 1144 | template 1145 | void generate_standard_page(const std::string& target_folder, const std::string& file, cpm::reports_data& data, const cpm::document_t& doc, const std::vector& documents, cxxopts::Options& options, bool one = false, bool section = false, const std::string& filter = ""){ 1146 | bool time_graphs = !options.count("disable-time") && documents.size() > 1; 1147 | bool compiler_graphs = !options.count("disable-compiler") && data.compilers.size() > 1; 1148 | bool configuration_graphs = !options.count("disable-configuration") && data.configurations.size() > 1; 1149 | bool summary_table = !options.count("disable-summary"); 1150 | 1151 | std::string target_file = target_folder + "/" + file; 1152 | 1153 | std::ofstream stream(target_file); 1154 | 1155 | Theme theme(data, options, stream, doc["compiler"].GetString(), doc["configuration"].GetString()); 1156 | 1157 | //Header of the page 1158 | header(theme); 1159 | 1160 | //Configure the highcharts theme 1161 | if(options["hctheme"].as() == "dark_unica"){ 1162 | theme << "\n"; 1165 | } 1166 | 1167 | if(one){ 1168 | data.file = file; 1169 | 1170 | if(section){ 1171 | data.sub_part = std::string("section_") + filter; 1172 | } else { 1173 | data.sub_part = std::string("bench_") + filter; 1174 | } 1175 | 1176 | data.files.clear(); 1177 | 1178 | for(const auto& result : doc["results"]){ 1179 | std::string name(strip_tags(result["title"].GetString())); 1180 | data.files.emplace_back(name, cpm::filify(doc["compiler"].GetString(), doc["configuration"].GetString(), std::string("bench_") + name)); 1181 | } 1182 | 1183 | for(const auto& section : doc["sections"]){ 1184 | std::string name(strip_tags(section["name"].GetString())); 1185 | data.files.emplace_back(name, cpm::filify(doc["compiler"].GetString(), doc["configuration"].GetString(), std::string("section_") + name)); 1186 | } 1187 | } 1188 | 1189 | //Information block about the last run 1190 | information(theme, doc); 1191 | 1192 | //Compiler selection 1193 | compiler_buttons(theme); 1194 | 1195 | //Configuration selection 1196 | configuration_buttons(theme); 1197 | 1198 | //More informations for some theme (navigation list for instance) 1199 | after_buttons(theme); 1200 | 1201 | std::size_t id = 1; 1202 | 1203 | if(!one || !section){ 1204 | for(const auto& result : doc["results"]){ 1205 | if(!one || filter == strip_tags(result["title"].GetString())){ 1206 | theme.before_result(strip_tags(result["title"].GetString()), false, documents); 1207 | 1208 | generate_run_graph(theme, id, result); 1209 | 1210 | if(time_graphs){ 1211 | generate_time_graph(theme, id, result, documents); 1212 | } 1213 | 1214 | if(compiler_graphs){ 1215 | generate_compiler_graph(theme, id, result, doc); 1216 | } 1217 | 1218 | if(configuration_graphs){ 1219 | generate_configuration_graph(theme, id, result, doc); 1220 | } 1221 | 1222 | if(summary_table){ 1223 | generate_summary_table(theme, result, doc); 1224 | } 1225 | 1226 | theme.after_result(); 1227 | } 1228 | } 1229 | } 1230 | 1231 | if(!one || section){ 1232 | for(auto& section : doc["sections"]){ 1233 | if(!one || filter == strip_tags(section["name"].GetString())){ 1234 | theme.before_result(strip_tags(section["name"].GetString()), compiler_graphs, documents); 1235 | 1236 | generate_section_run_graph(theme, id, section); 1237 | 1238 | if(time_graphs){ 1239 | generate_section_time_graph(theme, id, section, documents); 1240 | } 1241 | 1242 | if(compiler_graphs){ 1243 | generate_section_compiler_graph(theme, id, section, doc); 1244 | } 1245 | 1246 | if(configuration_graphs){ 1247 | generate_section_configuration_graph(theme, id, section, doc); 1248 | } 1249 | 1250 | if(summary_table){ 1251 | generate_section_summary_table(theme, id, section, doc); 1252 | } 1253 | 1254 | theme.after_result(); 1255 | } 1256 | } 1257 | } 1258 | 1259 | footer(theme); 1260 | } 1261 | 1262 | template 1263 | void generate_pages(const std::string& target_folder, cpm::reports_data& data, cxxopts::Options& options){ 1264 | //Select the base document 1265 | auto& base = data.documents.back(); 1266 | 1267 | std::set pages; 1268 | 1269 | if(options.count("pages")){ 1270 | //Generate pages for each (bench-section)/configuration/compiler 1271 | std::for_each(data.documents.rbegin(), data.documents.rend(), [&](cpm::document_t& d){ 1272 | for(const auto& result : d["results"]){ 1273 | auto file = cpm::filify(d["compiler"].GetString(), d["configuration"].GetString(), std::string("bench_") + strip_tags(result["title"].GetString())); 1274 | if(!pages.count(file)){ 1275 | generate_standard_page(target_folder, file, data, d, select_documents(data.documents, d), options, true, false, strip_tags(result["title"].GetString())); 1276 | 1277 | if(pages.empty()){ 1278 | generate_standard_page(target_folder, "index.html", data, d, select_documents(data.documents, d), options, true, false, strip_tags(result["title"].GetString())); 1279 | } 1280 | 1281 | pages.insert(file); 1282 | } 1283 | } 1284 | 1285 | for(const auto& section : d["sections"]){ 1286 | auto file = cpm::filify(d["compiler"].GetString(), d["configuration"].GetString(), std::string("section_") + strip_tags(section["name"].GetString())); 1287 | if(!pages.count(file)){ 1288 | generate_standard_page(target_folder, file, data, d, select_documents(data.documents, d), options, true, true, strip_tags(section["name"].GetString())); 1289 | 1290 | if(pages.empty()){ 1291 | generate_standard_page(target_folder, "index.html", data, d, select_documents(data.documents, d), options, true, true, strip_tags(section["name"].GetString())); 1292 | } 1293 | 1294 | pages.insert(file); 1295 | } 1296 | } 1297 | }); 1298 | } else { 1299 | //Generate the index 1300 | generate_standard_page(target_folder, "index.html", data, base, select_documents(data.documents, base), options); 1301 | 1302 | //Generate the compiler pages 1303 | std::for_each(data.documents.rbegin(), data.documents.rend(), [&](cpm::document_t& d){ 1304 | auto file = cpm::filify(d["compiler"].GetString(), d["configuration"].GetString()); 1305 | if(!pages.count(file)){ 1306 | generate_standard_page(target_folder, file, data, d, select_documents(data.documents, d), options); 1307 | pages.insert(file); 1308 | } 1309 | }); 1310 | } 1311 | } 1312 | 1313 | } //end of anonymous namespace 1314 | 1315 | int main(int argc, char* argv[]){ 1316 | cxxopts::Options options(argv[0], " results_folder"); 1317 | 1318 | try { 1319 | options.add_options() 1320 | ("time-sizes", "Display multiple sizes in the time graphs") 1321 | ("t,theme", "Theme name [raw,bootstrap,boostrap-tabs]", cxxopts::value()->default_value("bootstrap")) 1322 | ("c,hctheme", "Highcharts Theme name [std,dark_unica]", cxxopts::value()->default_value("dark_unica"), "theme_name") 1323 | ("o,output", "Output folder", cxxopts::value()->default_value("reports"), "output_folder") 1324 | ("input", "Input results", cxxopts::value()) 1325 | ("s,sort-by-tag", "Sort by tag instaed of time") 1326 | ("p,pages", "General several HTML pages (one per bench/section)") 1327 | ("m,mflops", "Use MFlops/s instead of E/s in summary") 1328 | ("g,mflops-graphs", "Use MFlops/s instead of time in graphs") 1329 | ("d,disable-time", "Disable time graphs") 1330 | ("disable-compiler", "Disable compiler graphs") 1331 | ("disable-configuration", "Disable configuration graphs") 1332 | ("disable-summary", "Disable summary table") 1333 | ("h,help", "Print help") 1334 | ; 1335 | 1336 | options.parse_positional("input"); 1337 | options.parse(argc, argv); 1338 | 1339 | if (options.count("help")){ 1340 | std::cout << options.help({""}) << std::endl; 1341 | return 0; 1342 | } 1343 | 1344 | if (!options.count("input")){ 1345 | std::cout << "cpm: No input provided, exiting" << std::endl; 1346 | return 0; 1347 | } 1348 | } catch (const cxxopts::OptionException& e){ 1349 | std::cout << "cpm: error parsing options: " << e.what() << std::endl; 1350 | return -1; 1351 | } 1352 | 1353 | //Get the entered folders 1354 | auto source_folder = options["input"].as(); 1355 | auto target_folder = options["output"].as(); 1356 | 1357 | if(!cpm::folder_exists(source_folder)){ 1358 | std::cout << "cpm: The input folder does not exists, exiting" << std::endl; 1359 | return -1; 1360 | } 1361 | 1362 | if(!cpm::folder_exists(target_folder)){ 1363 | std::cout << "cpm: The target folder does not exists, exiting" << std::endl; 1364 | return -1; 1365 | } 1366 | 1367 | cpm::reports_data data; 1368 | 1369 | //Get all the documents 1370 | data.documents = read(source_folder, options); 1371 | 1372 | if(data.documents.empty()){ 1373 | std::cout << "Unable to read any files" << std::endl; 1374 | return -1; 1375 | } 1376 | 1377 | //Collect the list of compilers 1378 | for(auto& doc : data.documents){ 1379 | data.compilers.insert(doc["compiler"].GetString()); 1380 | } 1381 | 1382 | //Collect the list of configurations 1383 | for(auto& doc : data.documents){ 1384 | data.configurations.insert(doc["configuration"].GetString()); 1385 | } 1386 | 1387 | if(options["theme"].as() == "raw"){ 1388 | generate_pages(target_folder, data, options); 1389 | } else if(options["theme"].as() == "bootstrap-tabs"){ 1390 | generate_pages(target_folder, data, options); 1391 | } else if(options["theme"].as() == "bootstrap"){ 1392 | generate_pages(target_folder, data, options); 1393 | } else { 1394 | std::cout << "Invalid theme" << std::endl; 1395 | } 1396 | 1397 | return 0; 1398 | } 1399 | -------------------------------------------------------------------------------- /src/dark_unica.inc.js: -------------------------------------------------------------------------------- 1 | R"=====( 2 | /** 3 | * Dark theme for Highcharts JS 4 | * @author Torstein Honsi 5 | */ 6 | 7 | // Load the fonts 8 | Highcharts.createElement('link', { 9 | href: '//fonts.googleapis.com/css?family=Unica+One', 10 | rel: 'stylesheet', 11 | type: 'text/css' 12 | }, null, document.getElementsByTagName('head')[0]); 13 | 14 | Highcharts.theme = { 15 | colors: ["#2b908f", "#90ee7e", "#f45b5b", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 16 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 17 | chart: { 18 | backgroundColor: { 19 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, 20 | stops: [ 21 | [0, '#2a2a2b'], 22 | [1, '#3e3e40'] 23 | ] 24 | }, 25 | style: { 26 | fontFamily: "'Unica One', sans-serif" 27 | }, 28 | plotBorderColor: '#606063' 29 | }, 30 | title: { 31 | style: { 32 | color: '#E0E0E3', 33 | textTransform: 'uppercase', 34 | fontSize: '20px' 35 | } 36 | }, 37 | subtitle: { 38 | style: { 39 | color: '#E0E0E3', 40 | textTransform: 'uppercase' 41 | } 42 | }, 43 | xAxis: { 44 | gridLineColor: '#707073', 45 | labels: { 46 | style: { 47 | color: '#E0E0E3' 48 | } 49 | }, 50 | lineColor: '#707073', 51 | minorGridLineColor: '#505053', 52 | tickColor: '#707073', 53 | title: { 54 | style: { 55 | color: '#A0A0A3' 56 | 57 | } 58 | } 59 | }, 60 | yAxis: { 61 | gridLineColor: '#707073', 62 | labels: { 63 | style: { 64 | color: '#E0E0E3' 65 | } 66 | }, 67 | lineColor: '#707073', 68 | minorGridLineColor: '#505053', 69 | tickColor: '#707073', 70 | tickWidth: 1, 71 | title: { 72 | style: { 73 | color: '#A0A0A3' 74 | } 75 | } 76 | }, 77 | tooltip: { 78 | backgroundColor: 'rgba(0, 0, 0, 0.85)', 79 | style: { 80 | color: '#F0F0F0' 81 | } 82 | }, 83 | plotOptions: { 84 | series: { 85 | dataLabels: { 86 | color: '#B0B0B3' 87 | }, 88 | marker: { 89 | lineColor: '#333' 90 | } 91 | }, 92 | boxplot: { 93 | fillColor: '#505053' 94 | }, 95 | candlestick: { 96 | lineColor: 'white' 97 | }, 98 | errorbar: { 99 | color: 'white' 100 | } 101 | }, 102 | legend: { 103 | itemStyle: { 104 | color: '#E0E0E3' 105 | }, 106 | itemHoverStyle: { 107 | color: '#FFF' 108 | }, 109 | itemHiddenStyle: { 110 | color: '#606063' 111 | } 112 | }, 113 | credits: { 114 | style: { 115 | color: '#666' 116 | } 117 | }, 118 | labels: { 119 | style: { 120 | color: '#707073' 121 | } 122 | }, 123 | 124 | drilldown: { 125 | activeAxisLabelStyle: { 126 | color: '#F0F0F3' 127 | }, 128 | activeDataLabelStyle: { 129 | color: '#F0F0F3' 130 | } 131 | }, 132 | 133 | navigation: { 134 | buttonOptions: { 135 | symbolStroke: '#DDDDDD', 136 | theme: { 137 | fill: '#505053' 138 | } 139 | } 140 | }, 141 | 142 | // scroll charts 143 | rangeSelector: { 144 | buttonTheme: { 145 | fill: '#505053', 146 | stroke: '#000000', 147 | style: { 148 | color: '#CCC' 149 | }, 150 | states: { 151 | hover: { 152 | fill: '#707073', 153 | stroke: '#000000', 154 | style: { 155 | color: 'white' 156 | } 157 | }, 158 | select: { 159 | fill: '#000003', 160 | stroke: '#000000', 161 | style: { 162 | color: 'white' 163 | } 164 | } 165 | } 166 | }, 167 | inputBoxBorderColor: '#505053', 168 | inputStyle: { 169 | backgroundColor: '#333', 170 | color: 'silver' 171 | }, 172 | labelStyle: { 173 | color: 'silver' 174 | } 175 | }, 176 | 177 | navigator: { 178 | handles: { 179 | backgroundColor: '#666', 180 | borderColor: '#AAA' 181 | }, 182 | outlineColor: '#CCC', 183 | maskFill: 'rgba(255,255,255,0.1)', 184 | series: { 185 | color: '#7798BF', 186 | lineColor: '#A6C7ED' 187 | }, 188 | xAxis: { 189 | gridLineColor: '#505053' 190 | } 191 | }, 192 | 193 | scrollbar: { 194 | barBackgroundColor: '#808083', 195 | barBorderColor: '#808083', 196 | buttonArrowColor: '#CCC', 197 | buttonBackgroundColor: '#606063', 198 | buttonBorderColor: '#606063', 199 | rifleColor: '#FFF', 200 | trackBackgroundColor: '#404043', 201 | trackBorderColor: '#404043' 202 | }, 203 | 204 | // special colors for some of the 205 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 206 | background2: '#505053', 207 | dataLabelsColor: '#B0B0B3', 208 | textColor: '#C0C0C0', 209 | contrastTextColor: '#F0F0F3', 210 | maskColor: 'rgba(255,255,255,0.3)' 211 | }; 212 | 213 | // Apply the theme 214 | Highcharts.setOptions(Highcharts.theme); 215 | )=====" 216 | --------------------------------------------------------------------------------