// Copyright (c) Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #include #include #include #include #include #include #include #include namespace miopen { namespace seq { struct TestData { int x; }; enum class Modes { Standard, Template, Native, Range, Unknown, }; enum class Sequences { Seq, Span, IRange, Join, Unknown, }; enum class Instances { Single, PerCall, Static, Unknown, }; template struct MemberPtr> { using Container = TContainer; MemberPtr(const std::integral_constant&) {} const TType& RV(const TContainer& cont) const { return cont.*field; } TType& LV(TContainer& cont) const { return cont.*field; } }; template struct NativeImpl { }; template <> struct NativeImpl { bool Next(TestData& v) const { if(v.x == 1) { v.x = 2; return false; } if(v.x == 2) { v.x = 7; return false; } v.x = 1; return true; } }; template <> struct NativeImpl { bool Next(TestData& v) const { if(v.x == 8) { v.x = 0; return true; } ++v.x; return false; } }; template <> struct NativeImpl { bool Next(TestData&) const { std::cerr << "irange is only for range" << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) } }; template <> struct NativeImpl { bool Next(TestData& td) const { if(td.x < 8) { ++td.x; return false; } if(td.x == 8) { td.x = 10; return false; } if(td.x == 10) { td.x = 11; return false; } if(td.x == 10) { td.x = 11; return false; } if(td.x == 11) { td.x = 14; return false; } if(td.x == 14) { td.x = 15; return false; } td.x = 0; return true; } }; template bool Next(const TRange& range, TValue& value) { auto it = GenericFind(range, value); assert(it != range.end()); if(++it == range.end()) { value = *range.begin(); return true; } value = *it; return false; } template struct RangeSequence { using value_type = TValue; using const_iterator = const TValue*; #ifdef _MSC_VER constexpr const_iterator begin() const { return arr.begin()._Unwrapped(); } constexpr const_iterator end() const { return arr.end()._Unwrapped(); } #else constexpr const_iterator begin() const { return arr.begin(); } constexpr const_iterator end() const { return arr.end(); } #endif private: static constexpr std::size_t count = sizeof...(values); static constexpr std::array arr = {{values...}}; }; template struct span_impl; template struct span_impl> { using value_type = T; using const_iterator = const T*; T data[sizeof...(Ns)] = {(Ns + Start{})...}; constexpr const T* begin() const { return data; } constexpr const T* end() const { return data + sizeof...(Ns); } constexpr const T* find(T value) const { if(value < Start{} || value > (Start{} + sizeof...(Ns))) { return end(); } else { return data + (value - Start{}); } } }; template struct RangeSpan : span_impl, std::make_integer_sequence> { }; template struct RangeImpl { }; template <> struct RangeImpl { bool Next(TestData& v) const { return miopen::seq::Next(std::views::iota(0, 9), v.x); } }; struct SpeedTestDriver : public test_driver { SpeedTestDriver() { add(iterations, "iterations"); add(modeStr, "mode"); add(sequenceStr, "seq"); add(instance_str, "inst"); } void run() { const auto seq = ParseSequence(sequenceStr); mode = ParseMode(modeStr); instance = ParseInstance(instance_str); switch(seq) { case Sequences::Seq: RunCore(); break; case Sequences::Span: RunCore(); break; case Sequences::IRange: RunCore(); break; case Sequences::Join: RunCore(); break; case Sequences::Unknown: std::cerr << "Unknown sequence." << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) } } void show_help() { test_driver::show_help(); std::cout << "Permitted modes: nat, std, tmpl, range" << std::endl; std::cout << "Permitted sequences: seq, span, irange (range), join(nat, std, tmpl)" << std::endl; std::cout << "Permitted instances: single, percall, static" << std::endl; } private: int iterations = 10; Instances instance = Instances::Unknown; Modes mode = Modes::Unknown; std::string instance_str = "single"; std::string modeStr = "std"; std::string sequenceStr = "seq"; static Modes ParseMode(const std::string& str) { if(str == "std") return Modes::Standard; if(str == "tmpl") return Modes::Template; if(str == "nat") return Modes::Native; if(str == "range") return Modes::Range; return Modes::Unknown; } static Sequences ParseSequence(const std::string& str) { if(str == "seq") return Sequences::Seq; if(str == "span") return Sequences::Span; if(str == "join") return Sequences::Join; if(str == "irange") return Sequences::IRange; return Sequences::Unknown; } static Instances ParseInstance(const std::string& str) { if(str == "single") return Instances::Single; if(str == "percall") return Instances::PerCall; if(str == "static") return Instances::Static; return Instances::Unknown; } template void RunCore() const { switch(mode) { case Modes::Standard: Standard(); break; case Modes::Template: Template(); break; case Modes::Native: Native(); break; case Modes::Range: Range(); break; case Modes::Unknown: std::cerr << "Unknown mode." << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) } } template void SaveDeadCode(const TType& value) const { static const std::string dead_code_saver; if(dead_code_saver.data() == nullptr) { std::cout << value << std::endl; std::terminate(); } } template void PrintCollectionImpl(rank<0>, const TRule&) const { } template void PrintCollectionImpl(rank<1>, const TRule& rule) const { TestData td{1}; rule.FillBegin(td); auto i = 0; do { std::cout << td.x << " "; } while(!rule.Next(td) && ++i < 1024); std::cout << std::endl; } template void PrintCollection(const TRule& rule) const { PrintCollectionImpl(rank<16>{}, rule); } template void TestCore(const TRuleGetter& rule_getter) const { PrintCollection(rule_getter()); TestData td{1}; const auto start = std::chrono::steady_clock::now(); for(auto i = 0; i < iterations; i++) { for(auto j = 0; j < 128 * 1024 * 1024; j++) rule_getter().Next(td); } const auto time = std::chrono::duration_cast( std::chrono::steady_clock::now() - start) .count() * .001 * .001; std::cout << "Test time: " << time << " seconds" << std::endl; SaveDeadCode(td.x); // required in release builds } template void Test(const TRuleGetter& rule_getter) const { switch(instance) { case Instances::PerCall: TestCore(rule_getter); break; case Instances::Single: { const auto inst = rule_getter(); TestCore([&inst]() -> const auto& { return inst; }); break; } case Instances::Static: TestCore([&rule_getter]() -> const auto& { static const auto inst = rule_getter(); return inst; }); break; case Instances::Unknown: std::cerr << "Unknown instance type" << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) } } template void Native() const { Test([]() { return NativeImpl{}; }); } static auto MakeSeq() { return Sequence{}; } static auto MakeBSeq() { return RangeSequence{}; } static auto MakeSpan() { return Span{}; } static auto MakeBSpan() { return RangeSpan{}; } static auto MakeJoin() { return Join, Sequence>{}; } static auto TmplMember() { return std::integral_constant{}; } template static auto RS(TParams... args) { return MakeRuleSet(std::make_tuple(args...)); } template void Standard() const { switch(seq) { case Sequences::Seq: Test([]() { return RS(MakeSeq(), &TestData::x); }); break; case Sequences::Span: Test([]() { return RS(MakeSpan(), &TestData::x); }); break; case Sequences::Join: Test([]() { return RS(MakeJoin(), &TestData::x); }); break; case Sequences::IRange: std::cerr << "irange is only for range" << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) } } template void Template() const { switch(seq) { case Sequences::Seq: Test([]() { return RS(MakeSeq(), TmplMember()); }); break; case Sequences::Span: Test([]() { return RS(MakeSpan(), TmplMember()); }); break; case Sequences::Join: Test([]() { return RS(MakeJoin(), TmplMember()); }); break; case Sequences::IRange: std::cerr << "irange is only for range" << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) } } template void Range() const { switch(seq) { case Sequences::Seq: Test([]() { return RS(MakeBSeq(), &TestData::x); }); break; case Sequences::Span: Test([]() { return RS(MakeBSpan(), &TestData::x); }); break; case Sequences::Join: std::cerr << "join is only for nat/std/tmpl" << std::endl; std::exit(-1); // NOLINT (concurrency-mt-unsafe) case Sequences::IRange: Test([]() { return RangeImpl{}; }); break; } } }; } // namespace seq } // namespace miopen int main(int argc, const char* argv[]) { test_drive(argc, argv); return 0; }