// // Copyright 2015-2016 Ettus Research LLC // Copyright 2018 Ettus Research, a National Instruments Company // // SPDX-License-Identifier: GPL-3.0-or-later // #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; using namespace uhd::convert; enum buf_init_t { RANDOM, INC }; // Convert `sc16_item32_le' -> `sc16' // Finds the first _ in format and returns the string // until then. Returns the entire string if no _ is found. std::string format_to_type(const std::string& format) { std::string ret_val = ""; for (size_t i = 0; i < format.length(); i++) { if (format[i] == '_') { return ret_val; } ret_val.append(1, format[i]); } return ret_val; } void configure_conv( converter::sptr conv, const std::string& in_type, const std::string& out_type) { if (in_type == "sc16") { if (out_type == "fc32") { std::cout << "Setting scalar to 1./32767." << std::endl; conv->set_scalar(1. / 32767.); return; } } if (in_type == "fc32") { if (out_type == "sc16") { std::cout << "Setting scalar to 32767." << std::endl; conv->set_scalar(32767.); return; } } std::cout << "No configuration required." << std::endl; } template void init_random_vector_complex_float(std::vector& buf_ptr, const size_t n_items) { std::complex* const buf = reinterpret_cast* const>(&buf_ptr[0]); for (size_t i = 0; i < n_items; i++) { buf[i] = std::complex( T(std::rand() / (RAND_MAX / 2.0) - 1), T(std::rand() / (RAND_MAX / 2.0) - 1)); } } template void init_random_vector_complex_int(std::vector& buf_ptr, const size_t n_items) { std::complex* const buf = reinterpret_cast* const>(&buf_ptr[0]); for (size_t i = 0; i < n_items; i++) { buf[i] = std::complex(T(std::rand()), T(std::rand())); } } struct item32_sc12_3x { uint32_t line0; uint32_t line1; uint32_t line2; }; template void init_random_vector_complex_sc12(std::vector& buf_ptr, const size_t n_items) { item32_sc12_3x* const buf = reinterpret_cast(&buf_ptr[0]); if (n_items % 4) throw std::invalid_argument(""); for (size_t i = 0; i < n_items / 4; i++) { int16_t iq[8]; for (auto& k : iq) k = rand() & 0xfff; buf[i].line0 = iq[0] << 20 | iq[1] << 8 | iq[2] >> 4; buf[i].line1 = iq[2] << 28 | iq[3] << 16 | iq[4] << 4 | iq[5] >> 8; buf[i].line2 = iq[5] << 24 | iq[6] << 12 | iq[7] << 0; } } template void init_random_vector_real_int(std::vector& buf_ptr, size_t n_items) { T* const buf = reinterpret_cast(&buf_ptr[0]); for (size_t i = 0; i < n_items; i++) { buf[i] = T(std::rand()); } } // Fill a buffer with increasing numbers template void init_inc_vector(std::vector& buf_ptr, size_t n_items) { T* const buf = reinterpret_cast(&buf_ptr[0]); for (size_t i = 0; i < n_items; i++) { buf[i] = T(i); } } void init_buffers(std::vector>& buf, const std::string& type, size_t bytes_per_item, buf_init_t buf_seed_mode) { if (buf.empty()) { return; } size_t n_items = buf[0].size() / bytes_per_item; /// Fill with incrementing integers if (buf_seed_mode == INC) { for (size_t i = 0; i < buf.size(); i++) { if (type == "sc8") { init_inc_vector>(buf[i], n_items); } else if (type == "sc16") { init_inc_vector>(buf[i], n_items); } else if (type == "sc32") { init_inc_vector>(buf[i], n_items); } else if (type == "fc32") { init_inc_vector>(buf[i], n_items); } else if (type == "fc64") { init_inc_vector>(buf[i], n_items); } else if (type == "s8") { init_inc_vector(buf[i], n_items); } else if (type == "s16") { init_inc_vector(buf[i], n_items); } else if (type == "item32") { init_inc_vector(buf[i], n_items); init_random_vector_real_int(buf[i], n_items); } else { throw uhd::runtime_error( str(boost::format("Cannot handle data type: %s") % type)); } } return; } assert(buf_seed_mode == RANDOM); /// Fill with random data for (size_t i = 0; i < buf.size(); i++) { if (type == "sc8") { init_random_vector_complex_int(buf[i], n_items); } else if (type == "sc12") { init_random_vector_complex_sc12(buf[i], n_items); } else if (type == "sc16") { init_random_vector_complex_int(buf[i], n_items); } else if (type == "sc32") { init_random_vector_complex_int(buf[i], n_items); } else if (type == "fc32") { init_random_vector_complex_float(buf[i], n_items); } else if (type == "fc64") { init_random_vector_complex_float(buf[i], n_items); } else if (type == "s8") { init_random_vector_real_int(buf[i], n_items); } else if (type == "s16") { init_random_vector_real_int(buf[i], n_items); } else if (type == "item32") { init_random_vector_real_int(buf[i], n_items); } else { throw uhd::runtime_error( str(boost::format("Cannot handle data type: %s") % type)); } } } // Returns time elapsed double run_benchmark(converter::sptr conv, const std::vector& input_buf_refs, const std::vector& output_buf_refs, size_t n_items, size_t iterations) { auto start = std::chrono::steady_clock::now(); for (size_t i = 0; i < iterations; i++) { conv->conv(input_buf_refs, output_buf_refs, n_items); } auto stop = std::chrono::steady_clock::now(); std::chrono::duration duration = stop - start; return duration.count(); } template std::string void_ptr_to_hexstring(const void* v_ptr, size_t index) { const T* ptr = reinterpret_cast(v_ptr); return str(boost::format("%X") % ptr[index]); } std::string item_to_hexstring(const void* v_ptr, size_t index, const std::string& type) { if (type == "fc32") { return void_ptr_to_hexstring(v_ptr, index); } else if (type == "sc16" || type == "item32") { return void_ptr_to_hexstring(v_ptr, index); } else if (type == "sc8" || type == "s16") { return void_ptr_to_hexstring(v_ptr, index); } else if (type == "u8") { return void_ptr_to_hexstring(v_ptr, index); } else { return str(boost::format("") % type); } } std::string item_to_string( const void* v_ptr, size_t index, const std::string& type, const bool print_hex) { if (print_hex) { return item_to_hexstring(v_ptr, index, type); } if (type == "sc16") { const std::complex* ptr = reinterpret_cast*>(v_ptr); return boost::lexical_cast(ptr[index]); } else if (type == "sc8") { const std::complex* ptr = reinterpret_cast*>(v_ptr); return boost::lexical_cast(ptr[index]); } else if (type == "fc32") { const std::complex* ptr = reinterpret_cast*>(v_ptr); return boost::lexical_cast(ptr[index]); } else if (type == "item32") { const uint32_t* ptr = reinterpret_cast(v_ptr); return boost::lexical_cast(ptr[index]); } else if (type == "s16") { const int16_t* ptr = reinterpret_cast(v_ptr); return boost::lexical_cast(ptr[index]); } else { return str(boost::format("") % type); } } int UHD_SAFE_MAIN(int argc, char* argv[]) { std::string in_format, out_format; std::string priorities; std::string seed_mode; priority_type prio = -1, max_prio; size_t iterations, n_samples; size_t n_inputs, n_outputs; buf_init_t buf_seed_mode = RANDOM; /// Command line arguments po::options_description desc("Converter benchmark options:"); // clang-format off desc.add_options() ("help", "help message") ("in", po::value(&in_format), "Input format (e.g. 'sc16')") ("out", po::value(&out_format), "Output format (e.g. 'sc16')") ("samples", po::value(&n_samples)->default_value(1000000), "Number of samples per iteration") ("iterations", po::value(&iterations)->default_value(10000), "Number of iterations per benchmark") ("priorities", po::value(&priorities)->default_value("default"), "Converter priorities. Can be 'default', 'all', or a comma-separated list of priorities.") ("max-prio", po::value(&max_prio)->default_value(4), "Largest available priority (advanced feature)") ("n-inputs", po::value(&n_inputs)->default_value(1), "Number of input vectors") ("n-outputs", po::value(&n_outputs)->default_value(1), "Number of output vectors") ("debug-converter", "Skip benchmark and print conversion results. Implies iterations==1 and will only run on a single converter.") ("seed-mode", po::value(&seed_mode)->default_value("random"), "How to initialize the data: random, incremental") ("hex", "When using debug mode, dump memory in hex") ; // clang-format on po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); // print the help message if (vm.count("help")) { std::cout << boost::format("UHD Converter Benchmark Tool %s") % desc << std::endl << std::endl; std::cout << " Use this to benchmark or debug converters." << std::endl << " When using as a benchmark tool, it will output the execution time\n" " for every conversion run in CSV format to stdout. Every line between\n" " the output delimiters {{{ }}} is of the format: ,