aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Rossetto <aaron.rossetto@ni.com>2022-02-18 15:40:45 -0600
committerAaron Rossetto <aaron.rossetto@ni.com>2022-02-28 14:47:47 -0600
commit751ef29584d678ef17a3dfebd5570045a34b0d88 (patch)
treeff90d29eb5cf636c4c2109c6ec70cf1105810b00
parent16d6ef5c8a6ed6846686f8b49fe12701b6adea0a (diff)
downloaduhd-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.cpp107
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)));
}