From 508311768ad4f9538eecda16f1cae899ea31184b Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Mon, 26 Jan 2015 12:17:17 +0100 Subject: tools: Added converter benchmark tool --- host/utils/CMakeLists.txt | 11 + host/utils/converter_benchmark.cpp | 432 +++++++++++++++++++++++++++++++++++++ host/utils/converter_benchmark.py | 193 +++++++++++++++++ 3 files changed, 636 insertions(+) create mode 100644 host/utils/converter_benchmark.cpp create mode 100644 host/utils/converter_benchmark.py (limited to 'host') diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 4f56dad0d..1ef7766f9 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -56,10 +56,14 @@ UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COM # Utilities that get installed into the share path ######################################################################## SET(util_share_sources + converter_benchmark.cpp query_gpsdo_sensors.cpp usrp_burn_db_eeprom.cpp usrp_burn_mb_eeprom.cpp ) +SET(util_share_sources_py + converter_benchmark.py +) IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp @@ -109,6 +113,13 @@ FOREACH(util_source ${util_share_sources}) TARGET_LINK_LIBRARIES(${util_name} uhd ${Boost_LIBRARIES}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) +FOREACH(util_source ${util_share_sources_py}) + UHD_INSTALL(PROGRAMS + ${CMAKE_CURRENT_SOURCE_DIR}/${util_source} + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities + ) +ENDFOREACH(util_source) UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) diff --git a/host/utils/converter_benchmark.cpp b/host/utils/converter_benchmark.cpp new file mode 100644 index 000000000..251dd6a37 --- /dev/null +++ b/host/utils/converter_benchmark.cpp @@ -0,0 +1,432 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#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 32767." << std::endl; + conv->set_scalar(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()/double(RAND_MAX/2)) - 1), + T((std::rand()/double(RAND_MAX/2)) - 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())); + } +} + +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< 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< std::complex >(buf[i], n_items); + } else if (type == "sc16") { + init_inc_vector< std::complex >(buf[i], n_items); + } else if (type == "sc32") { + init_inc_vector< std::complex >(buf[i], n_items); + } else if (type == "fc32") { + init_inc_vector< std::complex >(buf[i], n_items); + } else if (type == "fc64") { + init_inc_vector< std::complex >(buf[i], n_items); + } else if (type == "s8") { + init_inc_vector< boost::int8_t >(buf[i], n_items); + } else if (type == "s16") { + init_inc_vector< boost::int16_t >(buf[i], n_items); + } else if (type == "item32") { + init_inc_vector< boost::uint32_t >(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 == "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 +) { + boost::timer benchmark_timer; + for (size_t i = 0; i < iterations; i++) { + conv->conv(input_buf_refs, output_buf_refs, n_items); + } + return benchmark_timer.elapsed(); +} + +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 boost::uint32_t *ptr = reinterpret_cast(v_ptr); + return boost::lexical_cast(ptr[index]); + } + else if (type == "s16") { + const boost::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:"); + 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") + ; + 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: ,