diff options
author | Aaron Rossetto <aaron.rossetto@ni.com> | 2022-02-18 15:40:45 -0600 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2022-02-28 14:47:47 -0600 |
commit | 751ef29584d678ef17a3dfebd5570045a34b0d88 (patch) | |
tree | ff90d29eb5cf636c4c2109c6ec70cf1105810b00 | |
parent | 16d6ef5c8a6ed6846686f8b49fe12701b6adea0a (diff) | |
download | uhd-751ef29584d678ef17a3dfebd5570045a34b0d88.tar.gz uhd-751ef29584d678ef17a3dfebd5570045a34b0d88.tar.bz2 uhd-751ef29584d678ef17a3dfebd5570045a34b0d88.zip |
convert: Add benchmarking abilities
This commit adds code to the convert tests to support the ability to
benchmark individual conversion test cases.
-rw-r--r-- | host/tests/convert_test.cpp | 107 |
1 files changed, 97 insertions, 10 deletions
diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 939711b22..3d144eec9 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -14,6 +14,7 @@ #include <boost/test/data/test_case.hpp> #include <boost/test/unit_test.hpp> #include <array> +#include <chrono> #include <complex> #include <cstdlib> #include <iostream> @@ -26,6 +27,18 @@ typedef std::complex<int16_t> sc16_t; typedef std::complex<float> fc32_t; typedef std::complex<double> fc64_t; +static constexpr size_t BENCHMARK_NSAMPS = 8 * 1024 * 1024; +static constexpr size_t BENCHMARK_NITERS = 4; + +// Holds performance information about a conversion run +struct benchmark_result +{ + convert::id_type id; + uhd::convert::priority_type prio; + double elapsed_ns; + size_t nsamps; +}; + // List of priority types. This must be manually kept in sync with whatever is // defined in convert_common.hpp const std::array<uhd::convert::priority_type, 5> CONV_PRIO_TYPES{-1, 0, 1, 2, 3}; @@ -70,6 +83,7 @@ static convert::id_type reverse_converter(const convert::id_type& in) * Loopback runner: * convert input buffer into intermediate buffer * convert intermediate buffer into output buffer + * optionally collect benchmark data **********************************************************************/ template <typename Range> static void loopback(size_t nsamps, @@ -78,7 +92,8 @@ static void loopback(size_t nsamps, const Range& input, Range& output, const int prio_in, - const int prio_out) + const int prio_out, + std::vector<benchmark_result>* benchmark_data = nullptr) { // make this buffer large enough for all test types std::vector<uint64_t> interm(nsamps); @@ -89,12 +104,28 @@ static void loopback(size_t nsamps, // convert to intermediate type convert::converter::sptr c0 = convert::get_converter(in_id, prio_in)(); c0->set_scalar(32767.); - c0->conv(input0, output0, nsamps); + if(benchmark_data) { + const auto start_time = std::chrono::steady_clock::now(); + c0->conv(input0, output0, nsamps); + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double, std::nano> elapsed_in2out = end_time - start_time; + benchmark_data->push_back({in_id, prio_in, elapsed_in2out.count(), nsamps}); + } else { + c0->conv(input0, output0, nsamps); + } // convert back to host type convert::converter::sptr c1 = convert::get_converter(out_id, prio_out)(); c1->set_scalar(1 / 32767.); - c1->conv(input1, output1, nsamps); + if(benchmark_data) { + const auto start_time = std::chrono::steady_clock::now(); + c1->conv(input1, output1, nsamps); + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double, std::nano> elapsed_out2in = end_time - start_time; + benchmark_data->push_back({out_id, prio_out, elapsed_out2in.count(), nsamps}); + } else { + c1->conv(input1, output1, nsamps); + } } // Use this to call the loopback runner from a test so that missing prio won't @@ -107,13 +138,66 @@ static void loopback(size_t nsamps, } /*********************************************************************** + * Run converter test code under a benchmark + **********************************************************************/ +template <typename ConverterFunction> +static void benchmark_converter( + convert::id_type id, + uhd::convert::priority_type prio, + ConverterFunction&& converter_fn) +{ + std::vector<benchmark_result> benchmarks; + for(size_t iter = 0; iter < BENCHMARK_NITERS; iter++) + { + std::vector<benchmark_result> benchmarks_iter; + converter_fn(BENCHMARK_NSAMPS, id, prio, &benchmarks_iter); + // Detect if the benchmark didn't run because the converter type + // with the given priority wasn't found; if that's the case, bail + // on the test case + if(benchmarks_iter.empty()) { + return; + } + // Save the results for this iteration + std::copy(benchmarks_iter.begin(), benchmarks_iter.end(), std::back_inserter(benchmarks)); + } + + // Now collate and print the results + while(!benchmarks.empty()) + { + // Get the first entry from the per-iteration runs + struct benchmark_data bd = *(benchmarks.begin()); + // Remove that entry from the list, and look for other entries in + // the list that have the same converter and priority + auto b_iter = benchmarks.erase(benchmarks.begin()); + while(b_iter != benchmarks.end()) + { + if(b_iter->id == bd.id && b_iter->prio == bd.prio) { + // If a match is found, accumulate the elapsed time and + // number of samples + bd.elapsed_ns += b_iter->elapsed_ns; + bd.nsamps += b_iter->nsamps; + // And then remove it from the list + b_iter = benchmarks.erase(b_iter); + } else { + // Not a match; move on + b_iter++; + } + } + double ns_per_sample = bd.elapsed_ns / bd.nsamps; + std::cout << "For converter " << bd.id.to_string() << " prio " << prio << ": " << + ns_per_sample << " ns/sample" << std::endl; + } +} + +/*********************************************************************** * Test short conversion **********************************************************************/ static void test_convert_types_sc16(size_t nsamps, convert::id_type& id, uhd::convert::priority_type prio, const int extra_div = 1, - int mask = 0xffff) + int mask = 0xffff, + std::vector<benchmark_result>* benchmark_data = nullptr) { // fill the input samples std::vector<sc16_t> input(nsamps), output(nsamps); @@ -129,9 +213,11 @@ static void test_convert_types_sc16(size_t nsamps, // run the loopback and test convert::id_type in_id = id; convert::id_type out_id = reverse_converter(id); - CALL_LOOPBACK_SAFE(nsamps, in_id, out_id, input, output, prio, prio); - BOOST_CHECK_EQUAL_COLLECTIONS( - input.begin(), input.end(), output.begin(), output.end()); + CALL_LOOPBACK_SAFE(nsamps, in_id, out_id, input, output, prio, prio, benchmark_data); + if(!benchmark_data) { + BOOST_CHECK_EQUAL_COLLECTIONS( + input.begin(), input.end(), output.begin(), output.end()); + } } MULTI_CONVERTER_TEST_CASE(test_convert_types_be_sc16) @@ -182,7 +268,8 @@ MULTI_CONVERTER_TEST_CASE(test_convert_types_chdr_sc16) template <typename data_type> static void test_convert_types_for_floats(size_t nsamps, convert::id_type& id, - const double extra_scale = 1.0) + const double extra_scale = 1.0, + std::vector<benchmark_result>* benchmark_data = nullptr) { typedef typename data_type::value_type value_type; @@ -206,8 +293,8 @@ static void test_convert_types_for_floats(size_t nsamps, // loopback foreach prio combo (generic vs best) for (const auto& prio : prios) { - CALL_LOOPBACK_SAFE(nsamps, in_id, out_id, input, output, prio.first, prio.second); - for (size_t i = 0; i < nsamps; i++) { + CALL_LOOPBACK_SAFE(nsamps, in_id, out_id, input, output, prio.first, prio.second, benchmark_data); + for (size_t i = 0; i < nsamps && (!benchmark_data); i++) { MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1. / (1 << 14))); MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1. / (1 << 14))); } |