ComPPare 1.0.0
Loading...
Searching...
No Matches
comppare.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 | Leong Fan FUNG | funglf | stanleyfunglf@gmail.com
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the “Software”), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
22
23*/
24
34#pragma once
35#include <chrono>
36#include <cmath>
37#include <functional>
38#include <iomanip>
39#include <iostream>
40#include <string>
41#include <vector>
42#include <array>
43#include <memory>
44#include <string_view>
45#include <concepts>
46#include <stdexcept>
47
53
54#if defined(HAVE_GOOGLE_BENCHMARK) && defined(HAVE_NV_BENCH)
55#warning "Please only use one Plugin."
56#endif
57
58#if defined(HAVE_GOOGLE_BENCHMARK)
60#endif
61
62#if defined(HAVE_NV_BENCH)
64#endif
65
70namespace comppare
71{
72 /*
73 DoNotOptimize() and ClobberMemory() are utility functions to prevent compiler optimizations
74
75 Reference:
76 CppCon 2015: Chandler Carruth "Tuning C++: Benchmarks, and CPUs, and Compilers! Oh My!"
77 Google Benchmark: https://github.com/google/benchmark
78
79 Copyright 2015 Google Inc. All rights reserved.
80
81 Licensed under the Apache License, Version 2.0 (the "License");
82 you may not use this file except in compliance with the License.
83 You may obtain a copy of the License at
84
85 http://www.apache.org/licenses/LICENSE-2.0
86
87 Unless required by applicable law or agreed to in writing, software
88 distributed under the License is distributed on an "AS IS" BASIS,
89 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90 See the License for the specific language governing permissions and
91 limitations under the License.
92 */
93
102 template <typename T>
103 inline __attribute__((always_inline)) void DoNotOptimize(T const &value)
104 {
105 asm volatile("" : : "r,m"(value) : "memory");
106 }
107
116 template <typename T>
117 inline __attribute__((always_inline)) void DoNotOptimize(T &value)
118 {
119#if defined(__clang__)
120 asm volatile("" : "+r,m"(value) : : "memory");
121#else
122 asm volatile("" : "+m,r"(value) : : "memory");
123#endif
124 }
125
134 template <typename T>
135 inline __attribute__((always_inline)) void DoNotOptimize(T &&value)
136 {
137#if defined(__clang__)
138 asm volatile("" : "+r,m"(value) : : "memory");
139#else
140 asm volatile("" : "+m,r"(value) : : "memory");
141#endif
142 }
143
144 // This implementation is verbatim from Google Benchmark’s benchmark::ClobberMemory(),
145 // licensed under Apache 2.0. No changes have been made.
146 inline __attribute__((always_inline)) void ClobberMemory()
147 {
148 std::atomic_signal_fence(std::memory_order_acq_rel);
149 }
150
191 template <typename Value, typename Policy = void>
192 struct outspec;
193
214 template <typename Value>
216 struct outspec<Value, void>
217 {
218 using outtype_t = std::decay_t<Value>;
220 };
221
241 template <typename Value, typename Policy>
242 struct outspec<outspec<Value, Policy>, void>
243 {
244 using outtype_t = std::decay_t<Value>;
245 using policy_t = Policy;
246 };
247
257 template <typename Value, typename Policy>
259 struct outspec<Value, Policy>
260 {
261 using outtype_t = std::decay_t<Value>;
262 using policy_t = Policy;
263 };
264
266 template <typename Value, typename Policy>
268
270 template <typename T>
271 concept OutSpec =
273 typename outspec<T>::outtype_t,
274 typename outspec<T>::policy_t>;
275
281 template <typename... Inputs>
283 {
284 public:
290 template <OutSpec... OutputSpecs>
292 {
293 private:
298 template <typename S>
304 template <typename S>
305 using pol_t = typename outspec<S>::policy_t;
306
319 using Func = std::function<void(const std::decay_t<Inputs> &..., outtype_t<OutputSpecs> &...)>;
320
324 using InTup = std::tuple<std::decay_t<Inputs>...>;
328 using OutTup = std::tuple<outtype_t<OutputSpecs>...>;
332 using PolicyTup = std::tuple<pol_t<OutputSpecs>...>;
333
339 using OutPtr = std::shared_ptr<OutTup>;
340
351 std::vector<OutPtr> outputs_;
358
366 static constexpr size_t NUM_OUT = sizeof...(OutputSpecs);
367
374 std::shared_ptr<plugin::Plugin<InTup, OutTup>> plugin_;
375
381 void register_plugin(const std::shared_ptr<plugin::Plugin<InTup, OutTup>> &p)
382 {
383 if (!plugin_)
384 plugin_ = p;
385 else if (plugin_ != p)
386 throw std::logic_error("Multiple plugins are not supported");
387 }
388
405 struct Impl
406 {
412 std::string name;
421
431
439 std::unique_ptr<OutTup> plugin_output = nullptr;
440
441#ifdef HAVE_GOOGLE_BENCHMARK
451#endif
452
453#ifdef HAVE_NV_BENCH
459 decltype(auto) nvbench()
460 {
462 }
463#endif
464
475 template <template <class, class> class Plugin>
477 decltype(auto) attach()
478 {
479 auto adp = Plugin<InTup, OutTup>::instance();
480
482
483 plugin_output = std::make_unique<OutTup>();
484
485 return adp->register_impl(name, fn, *inputs_ptr, *plugin_output);
486 }
487 };
488
495 std::vector<Impl> impls_;
496
497 // helpers -----------------------------------------------------------
504 template <std::size_t I>
505 static constexpr std::size_t spec_metric_count() { return std::tuple_element_t<I, PolicyTup>::metric_count(); }
506
514 template <std::size_t I>
515 static constexpr std::string_view spec_metric_name(std::size_t m) { return std::tuple_element_t<I, PolicyTup>::metric_name(m); }
516
520 static constexpr int PRINT_COL_WIDTH = 20;
521
530 void print_header() const
531 {
532 std::cout << std::left << comppare::internal::ansi::BOLD
533 << "*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n============ "
534 << comppare::internal::ansi::ITALIC("ComPPare Framework")
535 << " ============\n=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*"
536 << comppare::internal::ansi::BOLD_OFF << "\n\n";
537 std::cout
538 << std::left << std::setw(30) << "Number of implementations: "
539 << std::right << std::setw(10) << impls_.size() << "\n"
540 << std::left << std::setw(30) << "Warmup iterations: "
541 << std::right << std::setw(10) << comppare::config::warmup_iters() << "\n"
542 << std::left << std::setw(30) << "Benchmark iterations: "
543 << std::right << std::setw(10) << comppare::config::bench_iters() << "\n"
544 << std::left << comppare::internal::ansi::BOLD("=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*") << "\n\n";
545
546 // Print header for the output table
547 std::cout << comppare::internal::ansi::UNDERLINE << comppare::internal::ansi::BOLD
548 << std::left
549 << std::setw(PRINT_COL_WIDTH) << "Implementation"
550 << std::right
551 << std::setw(PRINT_COL_WIDTH) << "ROI µs/Iter"
552 << std::setw(PRINT_COL_WIDTH) << "Func µs"
553 << std::setw(PRINT_COL_WIDTH) << "Ovhd µs";
554
555 // prints metric header
556 auto &&_print_metric_header = [this]<std::size_t I>()
557 {
558 for (std::size_t m = 0; m < this->template spec_metric_count<I>(); ++m)
559 {
560 std::cout << std::setw(PRINT_COL_WIDTH)
561 << comppare::internal::ansi::UNDERLINE(
562 comppare::internal::ansi::BOLD(
563 std::string(this->template spec_metric_name<I>(m)) + "[" + std::to_string(I) + "]"));
564 }
565 };
566
567 // lambda to call print metric header across each metric by unpacking I
568 [&]<std::size_t... I>(std::index_sequence<I...>)
569 {
570 // .template is a disambiguator as
571 // if using _print_metric_header<I>(),
572 // the compiler will treat `<` as an operator
573 (_print_metric_header.template operator()<I>(), ...);
574 }(std::make_index_sequence<NUM_OUT>{});
575
576 std::cout << std::endl;
577 }
578
587 void compute_errors(PolicyTup &errs, const OutTup &test, const OutTup &ref)
588 {
589 auto &&_compute_errors = [&]<std::size_t I>()
590 { comppare::internal::policy::compute_error(std::get<I>(errs), std::get<I>(test), std::get<I>(ref)); };
591
592 [&]<std::size_t... I>(std::index_sequence<I...>)
593 {
594 (_compute_errors.template operator()<I>(), ...);
595 }(std::make_index_sequence<NUM_OUT>{});
596 }
597
604 bool any_fail(const PolicyTup &errs) const
605 {
606 auto &&_any_fail = [&]<std::size_t I>() -> bool
607 {
608 return comppare::internal::policy::is_fail(std::get<I>(errs));
609 };
610
611 return [&]<std::size_t... I>(std::index_sequence<I...>) -> bool
612 {
613 bool fail = false;
614 ((fail |= _any_fail.template operator()<I>()), ...);
615 return fail;
616 }(std::make_index_sequence<NUM_OUT>{});
617 }
618
624 void print_metrics(const PolicyTup &errs) const
625 {
626 auto &&_print_metrics = [&errs]<std::size_t I>()
627 {
628 for (std::size_t m = 0; m < spec_metric_count<I>(); ++m)
629 std::cout << std::setw(PRINT_COL_WIDTH) << std::scientific << std::get<I>(errs).metric(m);
630 };
631
632 [&]<std::size_t... I>(std::index_sequence<I...>)
633 {
634 (_print_metrics.template operator()<I>(), ...);
635 }(std::make_index_sequence<NUM_OUT>{});
636 }
637
644 inline OutPtr get_output_by_index_(const size_t idx) const
645 {
646 if (outputs_.empty())
647 throw std::logic_error("run() has not been executed");
648 if (idx >= outputs_.size())
649 throw std::out_of_range("Index out of range for outputs");
650
651 return outputs_[idx];
652 }
653
660 inline OutPtr get_output_by_name_(const std::string_view name) const
661 {
662 if (outputs_.empty())
663 throw std::logic_error("run() has not been executed");
664 for (size_t i = 0; i < impls_.size(); ++i)
665 {
666 if (impls_[i].name == name)
667 return outputs_[i];
668 }
669 std::stringstream os;
670 os << "Output with name '" << name << "' not found";
671 throw std::invalid_argument(os.str());
672 }
673
680 void unpack_output_(const OutTup &outtup, outtype_t<OutputSpecs> *...outs) const
681 {
682 std::apply(
683 [&](auto &...outtup_elem)
684 {
685 ((*outs = outtup_elem), ...);
686 },
687 outtup);
688 }
689
690 public:
700 template <typename... Ins>
701 explicit OutputContext(Ins &&...ins)
702 : inputs_(std::forward<Ins>(ins)...) {}
703
705 OutputContext(const OutputContext &other) = delete;
707 OutputContext &operator=(const OutputContext &other) = delete;
709 OutputContext(OutputContext &&other) = delete;
712
724 template <typename F>
725 requires std::invocable<F, const std::decay_t<Inputs> &..., outtype_t<OutputSpecs> &...>
726 Impl &set_reference(std::string name, F &&f)
727 {
728 impls_.insert(impls_.begin(), {std::move(name), Func(std::forward<F>(f)), &inputs_, this});
729 return impls_.front();
730 }
731
743 template <typename F>
744 requires std::invocable<F, const std::decay_t<Inputs> &..., outtype_t<OutputSpecs> &...>
745 Impl &add(std::string name, F &&f)
746 {
747 impls_.push_back({std::move(name), Func(std::forward<F>(f)), &inputs_, this});
748 return impls_.back();
749 }
750
751 /*
752 Getter for the output results
753 */
762 {
763 return get_output_by_index_(0);
764 }
765
774 const OutPtr get_output(const size_t idx) const
775 {
776 return get_output_by_index_(idx);
777 }
778
787 const OutPtr get_output(const std::string_view name) const
788 {
789 return get_output_by_name_(name);
790 }
791
805 requires(sizeof...(OutputSpecs) > 0)
806 {
807 const auto &outtup = *get_output_by_index_(0);
808 unpack_output_(outtup, outs...);
809 }
810
823 void get_output(const size_t idx, outtype_t<OutputSpecs> *...outs) const
824 requires(sizeof...(OutputSpecs) > 0)
825 {
826 const auto &outtup = *get_output_by_index_(idx);
827 unpack_output_(outtup, outs...);
828 }
829
842 void get_output(const std::string_view name, outtype_t<OutputSpecs> *...outs) const
843 requires(sizeof...(OutputSpecs) > 0)
844 {
845 const auto &outtup = *get_output_by_name_(name);
846 unpack_output_(outtup, outs...);
847 }
848
855 void run(int argc = 0,
856 char **argv = nullptr)
857 {
859
860 if (impls_.empty())
861 {
862 std::cerr << "\n*----------*\nNo implementations added to the ComPPare Framework.\n*----------*\n";
863 return;
864 }
865
866 outputs_.reserve(impls_.size()); // reserve space for outputs -- resize and use index works too.
867
868 print_header();
869
870 // Main loop to iterate over all implementations
871 for (size_t k = 0; k < impls_.size(); ++k)
872 {
873 // Get the current implementation
874 auto &impl = impls_[k];
875
876 OutTup outs;
877
878 double func_duration;
879 /*
880 use std::apply to unpack the inputs and outputs completely to do 1 function call of the implementation
881 this is equivalent to calling:
882 impl.fn(inputs[0], inputs[1], ..., outputs[0], outputs[1], iters, roi_us);
883 */
884 std::apply([&](auto const &...in)
885 { std::apply(
886 [&](auto &...out)
887 {
888 auto func_start = comppare::config::clock_t::now();
889 impl.fn(in..., out...);
890 auto func_end = comppare::config::clock_t::now();
891 func_duration = std::chrono::duration<double, std::micro>(func_end - func_start).count();
892 },
893 outs); },
894 inputs_);
895
896 // Calculate the time taken by the function in microseconds
897 double roi_us = comppare::config::get_roi_us();
898 double warmup_us = comppare::config::get_warmup_us();
899 double func_us = func_duration - warmup_us;
900 double ovhd_us = func_us - roi_us;
901
902 roi_us /= static_cast<double>(comppare::config::bench_iters());
903
904 PolicyTup errs{};
905 if (k)
906 {
907 compute_errors(errs, outs, *outputs_[0]);
908 }
909 outputs_.push_back(std::make_shared<OutTup>(std::move(outs)));
910 // print row
911 std::cout << comppare::internal::ansi::RESET
912 << std::left << std::setw(PRINT_COL_WIDTH) << comppare::internal::ansi::GREEN(impl.name)
913 << std::fixed << std::setprecision(2) << std::right
914 << comppare::internal::ansi::YELLOW
915 << std::setw(PRINT_COL_WIDTH) << roi_us
916 << comppare::internal::ansi::DIM
917 << std::setw(PRINT_COL_WIDTH) << func_us
918 << std::setw(PRINT_COL_WIDTH) << ovhd_us
919 << comppare::internal::ansi::RESET;
920
921 print_metrics(errs);
922 if (k && any_fail(errs))
923 std::cout << comppare::internal::ansi::BG_RED("<-- FAIL");
924 std::cout << '\n';
925
926 } /* for impls */
927
929 if (plugin_)
930 {
931 plugin_->initialize(argc, argv);
932 plugin_->run();
933 }
935 } /* run */
936 }; /* OutputContext */
937 }; /* InputContext */
938
952 template <typename... Outputs, typename... Inputs>
953 auto make_comppare(Inputs &&...ins)
954 {
955 return typename InputContext<std::decay_t<Inputs>...>::template OutputContext<std::decay_t<Outputs>...>(
956 std::forward<Inputs>(ins)...);
957 }
958} // namespace comppare
959
964#define HOTLOOPSTART \
965 auto &&hotloop_body = [&]() { /* start of lambda */
966
971#define COMPPARE_HOTLOOP_BENCH \
972 /* Warm-up */ \
973 auto warmup_t0 = comppare::config::clock_t::now(); \
974 for (std::size_t i = 0; i < comppare::config::warmup_iters(); ++i) \
975 hotloop_body(); \
976 auto warmup_t1 = comppare::config::clock_t::now(); \
977 comppare::config::set_warmup_us(warmup_t0, warmup_t1); \
978 \
979 /* Timed */ \
980 comppare::config::reset_roi_us(); \
981 auto t0 = comppare::config::clock_t::now(); \
982 for (std::size_t i = 0; i < comppare::config::bench_iters(); ++i) \
983 hotloop_body(); \
984 auto t1 = comppare::config::clock_t::now(); \
985 \
986 if (comppare::config::get_roi_us() == double(0.0)) \
987 comppare::config::set_roi_us(t0, t1);
988
989#ifdef PLUGIN_HOTLOOP_BENCH
990#define HOTLOOPEND \
991 } \
992 ; /* end lambda */ \
993 \
994 if (comppare::current_state::using_plugin()) \
995 { \
996 PLUGIN_HOTLOOP_BENCH; \
997 } \
998 else \
999 { \
1000 COMPPARE_HOTLOOP_BENCH; \
1001 }
1002#else
1003#define HOTLOOPEND \
1004 } \
1005 ; /* end lambda */ \
1006 \
1007 COMPPARE_HOTLOOP_BENCH;
1008#endif
1009
1013#define HOTLOOP(LOOP_BODY) \
1014 HOTLOOPSTART LOOP_BODY HOTLOOPEND
1015
1019#define MANUAL_TIMER_START \
1020 auto t_manual_start = comppare::config::clock_t::now();
1021
1025#define MANUAL_TIMER_END \
1026 auto t_manual_stop = comppare::config::clock_t::now(); \
1027 SET_ITERATION_TIME(t_manual_stop - t_manual_start);
1028
1029#ifdef PLUGIN_HOTLOOP_BENCH
1030#define SET_ITERATION_TIME(TIME) \
1031 if (comppare::current_state::using_plugin()) \
1032 { \
1033 PLUGIN_SET_ITERATION_TIME(TIME); \
1034 } \
1035 else \
1036 { \
1037 comppare::config::increment_roi_us(TIME); \
1038 }
1039#else
1040#define SET_ITERATION_TIME(TIME) \
1041 comppare::config::increment_roi_us(TIME);
1042#endif
1043
1044#if defined(__CUDACC__)
1045#define GPU_PREFIX cuda
1046#elif defined(__HIPCC__)
1047#define GPU_PREFIX hip
1048#endif
1049
1050#if defined(GPU_PREFIX)
1051
1052#if defined(HAVE_GOOGLE_BENCHMARK)
1053#warning "Not Recommended to use Google Benchmark with GPU_HOTLOOPEND macro. Use SET_ITERATION_TIME and manual timing instead."
1054#endif
1055
1056#define CONCAT_IMPL(x, y) x##y
1057#define CONCAT(x, y) CONCAT_IMPL(x, y)
1058
1063#define GPU_HOTLOOPSTART \
1064 auto &&hotloop_body = [&]() { /* start of lambda */
1065
1070#define GPU_COMPPARE_HOTLOOP_BENCH \
1071 /* Warm-up */ \
1072 CONCAT(GPU_PREFIX, Event_t) \
1073 start_, stop_; \
1074 CONCAT(GPU_PREFIX, EventCreate)(&start_); \
1075 CONCAT(GPU_PREFIX, EventCreate)(&stop_); \
1076 CONCAT(GPU_PREFIX, EventRecord)(start_); \
1077 for (std::size_t i = 0; i < comppare::config::warmup_iters(); ++i) \
1078 hotloop_body(); \
1079 CONCAT(GPU_PREFIX, EventRecord)(stop_); \
1080 CONCAT(GPU_PREFIX, EventSynchronize)(stop_); \
1081 float ms_warmup_; \
1082 CONCAT(GPU_PREFIX, EventElapsedTime)(&ms_warmup_, start_, stop_); \
1083 comppare::config::set_warmup_us(1e3 * ms_warmup_); \
1084 \
1085 /* Timed */ \
1086 comppare::config::reset_roi_us(); \
1087 CONCAT(GPU_PREFIX, EventRecord)(start_); \
1088 for (std::size_t i = 0; i < comppare::config::bench_iters(); ++i) \
1089 hotloop_body(); \
1090 CONCAT(GPU_PREFIX, EventRecord)(stop_); \
1091 CONCAT(GPU_PREFIX, EventSynchronize)(stop_); \
1092 float ms_; \
1093 CONCAT(GPU_PREFIX, EventElapsedTime)(&ms_, start_, stop_); \
1094 if (comppare::config::get_roi_us() == double(0.0)) \
1095 comppare::config::set_roi_us(1e3 * ms_); \
1096 CONCAT(GPU_PREFIX, EventDestroy)(start_); \
1097 CONCAT(GPU_PREFIX, EventDestroy)(stop_);
1098
1099#if defined(GPU_PLUGIN_HOTLOOP_BENCH)
1100
1101#define GPU_HOTLOOPEND \
1102 } \
1103 ; /* end lambda */ \
1104 if (comppare::current_state::using_plugin()) \
1105 { \
1106 GPU_PLUGIN_HOTLOOP_BENCH; \
1107 } \
1108 else \
1109 { \
1110 GPU_COMPPARE_HOTLOOP_BENCH; \
1111 }
1112#else
1113
1114#define GPU_HOTLOOPEND \
1115 } \
1116 ; /* end lambda */ \
1117 GPU_COMPPARE_HOTLOOP_BENCH;
1118
1119#endif
1120
1125#define GPU_MANUAL_TIMER_START \
1126 CONCAT(GPU_PREFIX, Event_t) \
1127 start_manual_timer, stop_manual_timer; \
1128 CONCAT(GPU_PREFIX, EventCreate)(&start_manual_timer); \
1129 CONCAT(GPU_PREFIX, EventCreate)(&stop_manual_timer); \
1130 CONCAT(GPU_PREFIX, EventRecord)(start_manual_timer);
1131
1136#define GPU_MANUAL_TIMER_END \
1137 CONCAT(GPU_PREFIX, EventRecord)(stop_manual_timer); \
1138 CONCAT(GPU_PREFIX, EventSynchronize)(stop_manual_timer); \
1139 float ms_manual; \
1140 CONCAT(GPU_PREFIX, EventElapsedTime)(&ms_manual, start_manual_timer, stop_manual_timer); \
1141 SET_ITERATION_TIME(1e3 * ms_manual); \
1142 CONCAT(GPU_PREFIX, EventDestroy)(start_manual_timer); \
1143 CONCAT(GPU_PREFIX, EventDestroy)(stop_manual_timer);
1144
1145#endif
This file contains utilities for applying ANSI styles and colors to console output.
OutputContext class template to hold output parameters and manage implementations.
Definition comppare.hpp:292
OutputContext(OutputContext &&other)=delete
Deleted move constructor.
void register_plugin(const std::shared_ptr< plugin::Plugin< InTup, OutTup > > &p)
Register a plugin for the output context.
Definition comppare.hpp:381
InTup inputs_
Tuple instance storing all current input arguments.
Definition comppare.hpp:344
std::shared_ptr< OutTup > OutPtr
Shared pointer to an output tuple.
Definition comppare.hpp:339
void get_reference_output(outtype_t< OutputSpecs > *...outs) const
Copies the reference output into provided pointer to variables.
Definition comppare.hpp:804
std::tuple< outtype_t< OutputSpecs >... > OutTup
Tuple type holding all output values (one element per outspec).
Definition comppare.hpp:328
static constexpr std::size_t spec_metric_count()
Get the implementation details for a specific implementation index.
Definition comppare.hpp:505
void run(int argc=0, char **argv=nullptr)
Runs the comparison for all added implementations.
Definition comppare.hpp:855
OutputContext(Ins &&...ins)
Construct a new OutputContext.
Definition comppare.hpp:701
OutPtr get_output_by_name_(const std::string_view name) const
Get the output by implementation name.
Definition comppare.hpp:660
PolicyTup policies_ref_
Tuple of policy objects for the reference outputs.
Definition comppare.hpp:357
void unpack_output_(const OutTup &outtup, outtype_t< OutputSpecs > *...outs) const
Unpack the output tuple into the provided output pointers.
Definition comppare.hpp:680
void print_header() const
Print the header for the output table.
Definition comppare.hpp:530
OutPtr get_output_by_index_(const size_t idx) const
Get the output by index.
Definition comppare.hpp:644
OutputContext & operator=(OutputContext &&other)=delete
Deleted move assignment operator.
OutputContext & operator=(const OutputContext &other)=delete
Deleted copy assignment operator.
OutputContext(const OutputContext &other)=delete
Deleted copy constructor.
const OutPtr get_reference_output() const
Get the reference output by pointer.
Definition comppare.hpp:761
std::tuple< pol_t< OutputSpecs >... > PolicyTup
Tuple type holding the error/policy object associated with each output outspec.
Definition comppare.hpp:332
void get_output(const size_t idx, outtype_t< OutputSpecs > *...outs) const
Copies the outputs of a specific implementation by index into provided pointer to variables.
Definition comppare.hpp:823
typename outspec< S >::policy_t pol_t
Extracts the policy type from a outspec.
Definition comppare.hpp:305
const OutPtr get_output(const std::string_view name) const
Get the output for a specific implementation by name.
Definition comppare.hpp:787
static constexpr int PRINT_COL_WIDTH
Set the width of the print columns.
Definition comppare.hpp:520
std::vector< OutPtr > outputs_
Storage for reference and comparison outputs.
Definition comppare.hpp:351
Impl & set_reference(std::string name, F &&f)
Set a reference implementation.
Definition comppare.hpp:726
bool any_fail(const PolicyTup &errs) const
Check if any of the error policies indicate a failure.
Definition comppare.hpp:604
void print_metrics(const PolicyTup &errs) const
Print the metrics for each output specification.
Definition comppare.hpp:624
std::function< void(const std::decay_t< Inputs > &..., outtype_t< OutputSpecs > &...)> Func
Alias for the function signature of a user-provided implementation.
Definition comppare.hpp:319
static constexpr size_t NUM_OUT
Number of output specifications.
Definition comppare.hpp:366
static constexpr std::string_view spec_metric_name(std::size_t m)
Get the name of a specific metric for a specific implementation index.
Definition comppare.hpp:515
typename outspec< S >::outtype_t outtype_t
Extracts the value type from a outspec.
Definition comppare.hpp:299
const OutPtr get_output(const size_t idx) const
Get the output for a specific implementation by pointer.
Definition comppare.hpp:774
Impl & add(std::string name, F &&f)
Add a new implementation to the comparison framework.
Definition comppare.hpp:745
void get_output(const std::string_view name, outtype_t< OutputSpecs > *...outs) const
Copies the outputs of a specific implementation by name into provided pointer to variables.
Definition comppare.hpp:842
std::shared_ptr< plugin::Plugin< InTup, OutTup > > plugin_
Shared pointer to the plugin instance.
Definition comppare.hpp:374
std::tuple< std::decay_t< Inputs >... > InTup
Tuple type holding all input arguments.
Definition comppare.hpp:324
void compute_errors(PolicyTup &errs, const OutTup &test, const OutTup &ref)
Compute the error metrics for each output specification.
Definition comppare.hpp:587
std::vector< Impl > impls_
Vector to hold all implementations.
Definition comppare.hpp:495
InputContext class template to hold input parameters for the comparison framework.
Definition comppare.hpp:283
static double get_roi_us()
Get the current roi us value.
Definition config.hpp:163
static uint64_t bench_iters()
Get the number of benchmark iterations.
Definition config.hpp:71
static double get_warmup_us()
Get the current warmup us value.
Definition config.hpp:165
static uint64_t warmup_iters()
Get the number of warmup iterations.
Definition config.hpp:66
static void set_using_plugin(bool v)
Set if a plugin is being used currently.
Definition config.hpp:257
Base class for plugins in the ComPPare framework.
Definition plugin.hpp:60
Concept for output specifications being pair of type and policy.
Definition comppare.hpp:271
Concept for a valid Error Policy.
Definition policy.hpp:192
Concept for types supported by automatic error policy selection.
Definition policy.hpp:254
Concept for a valid plugin class.
Definition plugin.hpp:81
This file contains configuration settings for the ComPPare library.
This file contains the Google Benchmark plugin for the ComPPare framework.
static void parse_args(int argc, char **argv)
Definition helper.hpp:98
typename AutoPolicy< T >::type AutoPolicy_t
Helper alias to get the automatic error policy type for a given type T.
Definition policy.hpp:545
bool is_fail(const EP &ep)
Wrapper function to check if the error policy indicates a failure.
Definition policy.hpp:232
void compute_error(EP &ep, const V &a, const V &b)
Wrapper function to compute the error using the given instance of the error policy.
Definition policy.hpp:219
ComPPare framework main namespace.
void DoNotOptimize(T const &value)
Prevents the compiler from optimizing away the given value.
Definition comppare.hpp:103
void ClobberMemory()
Definition comppare.hpp:146
auto make_comppare(Inputs &&...ins)
Helper function to create a comppare object.
Definition comppare.hpp:953
This file contains the NVBench plugin for the ComPPare framework.
This file contains the base Plugin class and concept of a valid Plugin class for the ComPPare framewo...
This file contains error policies for comparing values in the ComPPare library.
Internal container representing one registered implementation.
Definition comppare.hpp:406
OutputContext * parent_ctx
Reference to the owning OutputContext. This allows the implementation to register plugins.
Definition comppare.hpp:430
decltype(auto) nvbench()
Attach the nvbench plugin.
Definition comppare.hpp:459
Func fn
The user-provided function implementing the operation.
Definition comppare.hpp:420
decltype(auto) attach()
Attach a plugin to the output context.
Definition comppare.hpp:477
InTup * inputs_ptr
Pointer to the input tuple inputs_.
Definition comppare.hpp:425
std::unique_ptr< OutTup > plugin_output
Unique pointer to the output tuple for plugin runs.
Definition comppare.hpp:439
std::string name
Name of the implementation.
Definition comppare.hpp:412
decltype(auto) google_benchmark()
Attach the Google Benchmark plugin.
Definition comppare.hpp:447
Partial specialization of outspec for user-defined error policy selection.
Definition comppare.hpp:260
std::decay_t< Value > outtype_t
Definition comppare.hpp:261
Policy policy_t
Definition comppare.hpp:262
internal::policy::autopolicy::AutoPolicy_t< Value > policy_t
Definition comppare.hpp:219
std::decay_t< Value > outtype_t
Definition comppare.hpp:218
std::decay_t< Value > outtype_t
Definition comppare.hpp:244
Policy policy_t
Definition comppare.hpp:245
Specification struct for pairing an output type with an error policy.
Definition comppare.hpp:192