diff options
author | Ciro Nishiguchi <ciro.nishiguchi@ni.com> | 2018-10-27 11:10:26 -0500 |
---|---|---|
committer | Brent Stapleton <brent.stapleton@ettus.com> | 2019-01-10 17:26:18 -0800 |
commit | fd3f5d011fb99304402cbf3c1e8c596478316119 (patch) | |
tree | 17d98a9879d7dff88227cbe53fab342119963675 /host | |
parent | e934e56ce2f8ca0c2f843577032f1a22bf938930 (diff) | |
download | uhd-fd3f5d011fb99304402cbf3c1e8c596478316119.tar.gz uhd-fd3f5d011fb99304402cbf3c1e8c596478316119.tar.bz2 uhd-fd3f5d011fb99304402cbf3c1e8c596478316119.zip |
tests: Add benchmark of streaming code paths
Add a benchmark of packet handlers and device3 flow control. Benchmarks
use mock transport objects.
Diffstat (limited to 'host')
-rw-r--r-- | host/lib/usrp/device3/device3_flow_ctrl.hpp | 4 | ||||
-rw-r--r-- | host/tests/CMakeLists.txt | 13 | ||||
-rw-r--r-- | host/tests/common/CMakeLists.txt | 2 | ||||
-rw-r--r-- | host/tests/common/mock_zero_copy.cpp | 40 | ||||
-rw-r--r-- | host/tests/common/mock_zero_copy.hpp | 82 | ||||
-rw-r--r-- | host/tests/device3_test.cpp | 4 | ||||
-rw-r--r-- | host/tests/packet_handler_benchmark.cpp | 423 |
7 files changed, 546 insertions, 22 deletions
diff --git a/host/lib/usrp/device3/device3_flow_ctrl.hpp b/host/lib/usrp/device3/device3_flow_ctrl.hpp index 4a7910f0f..50081543a 100644 --- a/host/lib/usrp/device3/device3_flow_ctrl.hpp +++ b/host/lib/usrp/device3/device3_flow_ctrl.hpp @@ -14,6 +14,8 @@ #include <uhd/transport/vrt_if_packet.hpp> #include <boost/shared_ptr.hpp> +namespace uhd { namespace usrp { + //! Stores the state of RX flow control struct rx_fc_cache_t { @@ -299,4 +301,6 @@ inline void tx_flow_ctrl_ack( fc_cache->fc_received = false; } +}}; + #endif /* INCLUDED_DEVICE3_FLOW_CTRL_HPP */ diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 4b068321b..1711d0ab2 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -54,6 +54,10 @@ set(test_sources fe_conn_test.cpp ) +set(benchmark_sources + packet_handler_benchmark.cpp +) + #turn each test cpp file into an executable with an int main() function add_definitions(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) @@ -83,6 +87,7 @@ endif(ENABLE_C_API) include_directories("${CMAKE_SOURCE_DIR}/lib/include") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/common") + #for each source: build an executable, register it as a test foreach(test_source ${test_sources}) get_filename_component(test_name ${test_source} NAME_WE) @@ -92,6 +97,14 @@ foreach(test_source ${test_sources}) UHD_INSTALL(TARGETS ${test_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests) endforeach(test_source) +#for benchmarks, build executable but do not register +foreach(benchmark_source ${benchmark_sources}) + get_filename_component(benchmark_name ${benchmark_source} NAME_WE) + add_executable(${benchmark_name} ${benchmark_source}) + target_link_libraries(${benchmark_name} uhd uhd_test ${Boost_LIBRARIES}) + UHD_INSTALL(TARGETS ${benchmark_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests) +endforeach(benchmark_source) + # Other tests that don't directly link with libuhd: (TODO find a nicer way to do this) include_directories(${CMAKE_BINARY_DIR}/lib/rfnoc/nocscript/) include_directories(${CMAKE_SOURCE_DIR}/lib/rfnoc/nocscript/) diff --git a/host/tests/common/CMakeLists.txt b/host/tests/common/CMakeLists.txt index e48e85c2f..58299c688 100644 --- a/host/tests/common/CMakeLists.txt +++ b/host/tests/common/CMakeLists.txt @@ -12,4 +12,4 @@ add_library(uhd_test ${CMAKE_CURRENT_SOURCE_DIR}/mock_ctrl_iface_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_zero_copy.cpp ${CMAKE_SOURCE_DIR}/lib/rfnoc/graph_impl.cpp ${CMAKE_SOURCE_DIR}/lib/rfnoc/async_msg_handler.cpp -)
\ No newline at end of file +) diff --git a/host/tests/common/mock_zero_copy.cpp b/host/tests/common/mock_zero_copy.cpp index eee96234d..bc49c3f10 100644 --- a/host/tests/common/mock_zero_copy.cpp +++ b/host/tests/common/mock_zero_copy.cpp @@ -11,8 +11,12 @@ using namespace uhd::transport; mock_zero_copy::mock_zero_copy( - vrt::if_packet_info_t::link_type_t link_type -) : _link_type(link_type) { + vrt::if_packet_info_t::link_type_t link_type, + size_t recv_frame_size, + size_t send_frame_size +) : _link_type(link_type) + , _recv_frame_size(recv_frame_size) + , _send_frame_size(send_frame_size) { } uhd::transport::managed_recv_buffer::sptr mock_zero_copy::get_recv_buff(double) { @@ -22,18 +26,32 @@ uhd::transport::managed_recv_buffer::sptr mock_zero_copy::get_recv_buff(double) if (_rx_mems.empty()) { return uhd::transport::managed_recv_buffer::sptr(); // timeout } - _mrbs.push_back(boost::make_shared<mock_mrb>()); + uhd::transport::managed_recv_buffer::sptr mrb = - _mrbs.back()->get_new(_rx_mems.front(), _rx_lens.front()); - _rx_mems.pop_front(); - _rx_lens.pop_front(); + _mrb.get_new(_rx_mems.front(), _rx_lens.front()); + + if (not _reuse_recv_memory) { + _rx_mems.pop_front(); + _rx_lens.pop_front(); + } + return mrb; } uhd::transport::managed_send_buffer::sptr mock_zero_copy::get_send_buff(double) { - _msbs.push_back(boost::make_shared<mock_msb>()); - _tx_mems.push_back( - boost::shared_array<uint8_t>(new uint8_t[SEND_BUFF_SIZE])); - _tx_lens.push_back(SEND_BUFF_SIZE); - return _msbs.back()->get_new(_tx_mems.back(), &_tx_lens.back()); + if (not _reuse_send_memory or _tx_mems.size() == 0) { + _tx_mems.push_back( + boost::shared_array<uint8_t>(new uint8_t[_send_frame_size])); + _tx_lens.push_back(_send_frame_size); + } + + return _msb.get_new(_tx_mems.back(), &_tx_lens.back()); +} + +void mock_zero_copy::set_reuse_recv_memory(bool reuse_recv) { + _reuse_recv_memory = reuse_recv; +} + +void mock_zero_copy::set_reuse_send_memory(bool reuse_send) { + _reuse_send_memory = reuse_send; } diff --git a/host/tests/common/mock_zero_copy.hpp b/host/tests/common/mock_zero_copy.hpp index 867300ac5..8d27c9b46 100644 --- a/host/tests/common/mock_zero_copy.hpp +++ b/host/tests/common/mock_zero_copy.hpp @@ -13,6 +13,7 @@ #include <uhd/transport/zero_copy.hpp> #include <uhd/types/endianness.hpp> #include <uhd/types/sid.hpp> +#include <uhd/exception.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/exception.hpp> #include <boost/make_shared.hpp> @@ -29,8 +30,8 @@ * Tx and Rx are separate. We can access the other end of the FIFOs from * this class. */ -static constexpr size_t SEND_BUFF_SIZE = 1024; -static constexpr size_t RECV_BUFF_SIZE = 1024; +static constexpr size_t DEFAULT_SEND_FRAME_SIZE = 1024; +static constexpr size_t DEFAULT_RECV_FRAME_SIZE = 1024; /*********************************************************************** * Dummy managed buffers for testing @@ -68,15 +69,28 @@ class mock_zero_copy : public uhd::transport::zero_copy_if { public: typedef boost::shared_ptr<mock_zero_copy> sptr; - mock_zero_copy(uhd::transport::vrt::if_packet_info_t::link_type_t type); + mock_zero_copy( + uhd::transport::vrt::if_packet_info_t::link_type_t type, + size_t recv_frame_size = DEFAULT_RECV_FRAME_SIZE, + size_t send_frame_size = DEFAULT_SEND_FRAME_SIZE + ); uhd::transport::managed_recv_buffer::sptr get_recv_buff(double); uhd::transport::managed_send_buffer::sptr get_send_buff(double); size_t get_num_recv_frames(void) const { return 1; } size_t get_num_send_frames(void) const { return 1; } - size_t get_recv_frame_size(void) const { return RECV_BUFF_SIZE; } - size_t get_send_frame_size(void) const { return SEND_BUFF_SIZE; } + size_t get_recv_frame_size(void) const { return _recv_frame_size; } + size_t get_send_frame_size(void) const { return _send_frame_size; } + + template <typename T> + void push_back_packet( + uhd::transport::vrt::if_packet_info_t& ifpi, + const std::vector<T>& otw_data = std::vector<T>(), + uhd::endianness_t endianness = uhd::ENDIANNESS_BIG); + + void set_reuse_recv_memory(bool reuse_recv); + void set_reuse_send_memory(bool reuse_send); void set_simulate_io_error(bool status) { _simulate_io_error = status; } @@ -93,6 +107,13 @@ class mock_zero_copy : public uhd::transport::zero_copy_if { ); template <uhd::endianness_t endianness = uhd::ENDIANNESS_BIG> + void push_back_flow_ctrl_packet( + uhd::transport::vrt::if_packet_info_t::packet_type_t type, + uint32_t packet_count, + uint32_t byte_count + ); + + template <uhd::endianness_t endianness = uhd::ENDIANNESS_BIG> void pop_send_packet( uhd::transport::vrt::if_packet_info_t &ifpi ); @@ -104,11 +125,18 @@ class mock_zero_copy : public uhd::transport::zero_copy_if { std::list<boost::shared_array<uint8_t>> _rx_mems; std::list<size_t> _rx_lens; - std::vector<boost::shared_ptr<mock_msb>> _msbs; - std::vector<boost::shared_ptr<mock_mrb>> _mrbs; + mock_msb _msb; + mock_mrb _mrb; uhd::transport::vrt::if_packet_info_t::link_type_t _link_type; + size_t _recv_frame_size = DEFAULT_RECV_FRAME_SIZE; + size_t _send_frame_size = DEFAULT_RECV_FRAME_SIZE; + bool _simulate_io_error = false; + + bool _reuse_recv_memory = false; + bool _reuse_send_memory = false; + }; template <typename T, uhd::endianness_t endianness> @@ -130,7 +158,7 @@ void mock_zero_copy::push_back_recv_packet( const size_t max_pkt_len = ifpi.num_payload_words32*sizeof(uint32_t)+max_hdr_len; - UHD_ASSERT_THROW(max_pkt_len <= RECV_BUFF_SIZE); + UHD_ASSERT_THROW(max_pkt_len <= _recv_frame_size); // Create recv buffer _rx_mems.push_back(boost::shared_array<uint8_t>(new uint8_t[max_pkt_len])); @@ -199,4 +227,42 @@ void mock_zero_copy::pop_send_packet( _tx_lens.pop_front(); } +template <uhd::endianness_t endianness> +void mock_zero_copy::push_back_flow_ctrl_packet( + uhd::transport::vrt::if_packet_info_t::packet_type_t type, + uint32_t packet_count, + uint32_t byte_count +) +{ + using namespace uhd::transport; + + UHD_ASSERT_THROW( + type == vrt::if_packet_info_t::PACKET_TYPE_FC or + type == vrt::if_packet_info_t::PACKET_TYPE_ACK); + + // Only implemented for chdr packets currently + UHD_ASSERT_THROW(_link_type == vrt::if_packet_info_t::LINK_TYPE_CHDR); + + const size_t packet_len_in_words32 = 2; + + vrt::if_packet_info_t ifpi; + ifpi.packet_type = type; + ifpi.num_payload_words32 = packet_len_in_words32; + ifpi.num_payload_bytes = ifpi.num_payload_words32*sizeof(uint32_t); + ifpi.has_tsf = false; + + std::vector<uint32_t> data(packet_len_in_words32, 0); + + if (endianness == uhd::ENDIANNESS_BIG) { + data[0] = uhd::ntohx(packet_count); + data[1] = uhd::ntohx(byte_count); + } + else { + data[0] = uhd::wtohx(packet_count); + data[1] = uhd::wtohx(byte_count); + } + + push_back_recv_packet<uint32_t, endianness>(ifpi, data); +} + #endif /*INCLUDED_MOCK_XPORT_HPP*/ diff --git a/host/tests/device3_test.cpp b/host/tests/device3_test.cpp index db613d06e..71b3225e1 100644 --- a/host/tests/device3_test.cpp +++ b/host/tests/device3_test.cpp @@ -34,10 +34,10 @@ uhd::both_xports_t make_mock_transport(const uhd::sid_t& tx_sid) { uhd::both_xports_t xports; xports.send_sid = tx_sid; xports.recv_sid = tx_sid.reversed(); - xports.send_buff_size = SEND_BUFF_SIZE; - xports.recv_buff_size = RECV_BUFF_SIZE; xports.send = boost::make_shared<mock_zero_copy>(if_packet_info_t::LINK_TYPE_CHDR); xports.recv = xports.send; + xports.send_buff_size = xports.send->get_send_frame_size(); + xports.recv_buff_size = xports.recv->get_recv_frame_size(); return xports; } diff --git a/host/tests/packet_handler_benchmark.cpp b/host/tests/packet_handler_benchmark.cpp new file mode 100644 index 000000000..37c6861bb --- /dev/null +++ b/host/tests/packet_handler_benchmark.cpp @@ -0,0 +1,423 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file contains a set of benchmarks for the various portions of the +// streamer implementation. + +// Disable sequence checking for recv packet handler so that the benchmark +// code does not need to create new mock packet contents in every recv call. +// This should have very little effect on packet handler performance. +#define SRPH_DONT_CHECK_SEQUENCE 1 + +#include "common/mock_zero_copy.hpp" +#include "../lib/transport/super_send_packet_handler.hpp" +#include "../lib/transport/super_recv_packet_handler.hpp" +#include "../lib/usrp/device3/device3_flow_ctrl.hpp" +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/thread.hpp> +#include <uhd/convert.hpp> +#include <uhd/transport/chdr.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/sid.hpp> +#include <boost/program_options.hpp> +#include <chrono> +#include <vector> + +namespace po = boost::program_options; +using namespace uhd::transport; +using namespace uhd::usrp; + +void benchmark_recv_packet_handler( + const size_t spp, + const std::string& format +) { + const size_t bpi = uhd::convert::get_bytes_per_item(format); + const size_t frame_size = bpi * spp + DEVICE3_RX_MAX_HDR_LEN; + + mock_zero_copy::sptr xport( + new mock_zero_copy( + vrt::if_packet_info_t::LINK_TYPE_CHDR, + frame_size, + frame_size)); + + xport->set_reuse_recv_memory(true); + + sph::recv_packet_streamer streamer(spp); + streamer.set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); + streamer.set_tick_rate(1.0); + streamer.set_samp_rate(1.0); + + uhd::convert::id_type id; + id.output_format = format; + id.num_inputs = 1; + id.input_format = "sc16_item32_be"; + id.num_outputs = 1; + streamer.set_converter(id); + + streamer.set_xport_chan_get_buff( + 0, + [xport](double timeout) { + return xport->get_recv_buff(timeout); + }, + false // flush + ); + + // Create packet for packet handler to read + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + packet_info.num_payload_words32 = spp; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(uint32_t); + packet_info.has_tsf = true; + packet_info.tsf = 1; + + std::vector<uint32_t> recv_data(spp, 0); + xport->push_back_recv_packet(packet_info, recv_data); + + // Allocate buffer + std::vector<uint8_t> buffer(spp*bpi); + std::vector<void*> buffers; + buffers.push_back(buffer.data()); + + // Run benchmark + uhd::rx_metadata_t md; + const auto start_time = std::chrono::steady_clock::now(); + const size_t iterations = 1e7; + + for (size_t i = 0; i < iterations; i++) { + streamer.recv(buffers, spp, md, 1.0, true); + } + + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double> elapsed_time(end_time-start_time); + const double time_per_packet = elapsed_time.count() / iterations; + + std::cout << format << ": " + << time_per_packet / spp * 1e9 + << " ns/sample, " + << time_per_packet * 1e9 + << " ns/packet\n"; +} + +void benchmark_send_packet_handler( + const size_t spp, + const std::string& format, + bool use_time_spec +) { + const size_t bpi = uhd::convert::get_bytes_per_item(format); + const size_t frame_size = bpi * spp + DEVICE3_TX_MAX_HDR_LEN; + + mock_zero_copy::sptr xport( + new mock_zero_copy( + vrt::if_packet_info_t::LINK_TYPE_CHDR, + frame_size, + frame_size)); + + xport->set_reuse_send_memory(true); + + sph::send_packet_streamer streamer(spp); + streamer.set_vrt_packer(&vrt::chdr::if_hdr_pack_be); + + uhd::convert::id_type id; + id.input_format = format; + id.num_inputs = 1; + id.output_format = "sc16_item32_be"; + id.num_outputs = 1; + streamer.set_converter(id); + streamer.set_enable_trailer(false); + + streamer.set_xport_chan_get_buff( + 0, + [xport](double timeout) { + return xport->get_send_buff(timeout); + }); + + // Allocate buffer + std::vector<uint8_t> buffer(spp*bpi); + std::vector<void*> buffers; + buffers.push_back(buffer.data()); + + // Run benchmark + uhd::tx_metadata_t md; + md.has_time_spec = use_time_spec; + + const auto start_time = std::chrono::steady_clock::now(); + const size_t iterations = 1e7; + + for (size_t i = 0; i < iterations; i++) { + if (use_time_spec) { + md.time_spec = uhd::time_spec_t(i, 0.0); + } + streamer.send(buffers, spp, md, 1.0); + } + + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double> elapsed_time(end_time-start_time); + const double time_per_packet = elapsed_time.count() / iterations; + + std::cout << format << ": " + << time_per_packet / spp * 1e9 + << " ns/sample, " + << time_per_packet * 1e9 + << " ns/packet\n"; +} + +void benchmark_device3_rx_flow_ctrl( + bool send_flow_control_packet +) { + // Arbitrary sizes + constexpr uint32_t fc_window = 10000; + + mock_zero_copy::sptr xport( + new mock_zero_copy( + vrt::if_packet_info_t::LINK_TYPE_CHDR)); + + xport->set_reuse_recv_memory(true); + xport->set_reuse_send_memory(true); + + boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t()); + fc_cache->to_host = uhd::ntohx<uint32_t>; + fc_cache->from_host = uhd::htonx<uint32_t>; + fc_cache->pack = vrt::chdr::if_hdr_pack_be; + fc_cache->unpack = vrt::chdr::if_hdr_unpack_be; + fc_cache->xport = xport; + fc_cache->interval = fc_window; + + // Create data buffer to pass to flow control function. Number of payload + // words is arbitrary, just has to fit in the buffer. + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + packet_info.num_payload_words32 = 100; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(uint32_t); + packet_info.has_tsf = false; + + std::vector<uint32_t> recv_data(packet_info.num_payload_words32, 0); + xport->push_back_recv_packet(packet_info, recv_data); + + auto recv_buffer = xport->get_recv_buff(1.0); + + // Run benchmark + const auto start_time = std::chrono::steady_clock::now(); + + constexpr size_t iterations = 1e7; + + for (size_t i = 0; i < iterations; i++) { + fc_cache->total_bytes_consumed = send_flow_control_packet? fc_window:0; + fc_cache->last_byte_count = 0; + + rx_flow_ctrl(fc_cache, recv_buffer); + } + + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double> elapsed_time(end_time-start_time); + + std::cout << elapsed_time.count() / iterations * 1e9 + << " ns per call\n"; +} + +void benchmark_device3_handle_rx_flow_ctrl_ack() { + // Arbitrary sizes + constexpr uint32_t fc_window = 10000; + + mock_zero_copy::sptr xport( + new mock_zero_copy( + vrt::if_packet_info_t::LINK_TYPE_CHDR)); + + xport->set_reuse_recv_memory(true); + xport->set_reuse_send_memory(true); + + boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t()); + fc_cache->to_host = uhd::ntohx<uint32_t>; + fc_cache->from_host = uhd::htonx<uint32_t>; + fc_cache->pack = vrt::chdr::if_hdr_pack_be; + fc_cache->unpack = vrt::chdr::if_hdr_unpack_be; + fc_cache->xport = xport; + fc_cache->interval = fc_window; + fc_cache->total_bytes_consumed = 100; + + // Payload should contain packet count and byte count + std::vector<uint32_t> payload_data; + payload_data.push_back(fc_cache->to_host(10)); // packet count + payload_data.push_back(fc_cache->to_host(100)); // byte count + + // Run benchmark + const auto start_time = std::chrono::steady_clock::now(); + constexpr size_t iterations = 1e7; + + for (size_t i = 0; i < iterations; i++) { + handle_rx_flowctrl_ack(fc_cache, payload_data.data()); + } + + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double> elapsed_time(end_time-start_time); + + std::cout << elapsed_time.count() / iterations * 1e9 + << " ns per call\n"; +} + +void benchmark_device3_tx_flow_ctrl( + bool send_flow_control_packet +) { + // Arbitrary sizes + constexpr uint32_t fc_window = 10000; + + mock_zero_copy::sptr xport( + new mock_zero_copy( + vrt::if_packet_info_t::LINK_TYPE_CHDR)); + + xport->set_reuse_recv_memory(true); + + boost::shared_ptr<tx_fc_cache_t> + fc_cache(new tx_fc_cache_t(fc_window)); + + fc_cache->to_host = uhd::ntohx<uint32_t>; + fc_cache->from_host = uhd::htonx<uint32_t>; + fc_cache->pack = vrt::chdr::if_hdr_pack_be; + fc_cache->unpack = vrt::chdr::if_hdr_unpack_be; + + xport->push_back_flow_ctrl_packet( + vrt::if_packet_info_t::PACKET_TYPE_FC, + 1 /*packet*/, + fc_window /*bytes*/); + + // Run benchmark + const auto start_time = std::chrono::steady_clock::now(); + constexpr size_t iterations = 1e7; + managed_send_buffer::sptr send_buffer = xport->get_send_buff(0.0); + + for (size_t i = 0; i < iterations; i++) { + fc_cache->byte_count = send_flow_control_packet? fc_window:0; + fc_cache->last_byte_ack = 0; + + tx_flow_ctrl(fc_cache, xport, send_buffer); + } + + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double> elapsed_time(end_time-start_time); + + std::cout << elapsed_time.count() / iterations * 1e9 + << " ns per call\n"; +} + +void benchmark_device3_tx_flow_ctrl_ack() { + // Arbitrary sizes + constexpr uint32_t fc_window = 10000; + + mock_zero_copy::sptr xport( + new mock_zero_copy( + vrt::if_packet_info_t::LINK_TYPE_CHDR)); + + xport->set_reuse_send_memory(true); + + boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t(fc_window)); + + fc_cache->to_host = uhd::ntohx<uint32_t>; + fc_cache->from_host = uhd::htonx<uint32_t>; + fc_cache->pack = vrt::chdr::if_hdr_pack_be; + fc_cache->unpack = vrt::chdr::if_hdr_unpack_be; + + // Run benchmark + const auto start_time = std::chrono::steady_clock::now(); + constexpr size_t iterations = 1e7; + uhd::sid_t send_sid; + + for (size_t i = 0; i < iterations; i++) { + // Setup fc_cache to require an ack + fc_cache->fc_received = true; + + tx_flow_ctrl_ack(fc_cache, xport, send_sid); + } + + const auto end_time = std::chrono::steady_clock::now(); + const std::chrono::duration<double> elapsed_time(end_time-start_time); + + std::cout << elapsed_time.count() / iterations * 1e9 + << " ns per call\n"; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]) +{ + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ; + + 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 Packet Handler Benchmark %s") % desc << std::endl; + std::cout << + " Benchmark of send and receive packet handlers and flow control\n" + " functions. All benchmarks use mock transport objects. No\n" + " parameters are needed to run this benchmark.\n" + << std::endl; + return EXIT_FAILURE; + } + + uhd::set_thread_priority_safe(); + + const char* formats[] = {"sc16", "fc32", "fc64" }; + constexpr size_t rx_spp = 2000; + constexpr size_t tx_spp = 1000; + + std::cout << "----------------------------------------------------------\n"; + std::cout << "Benchmark of recv with no flow control and mock transport \n"; + std::cout << "----------------------------------------------------------\n"; + std::cout << "spp: " << rx_spp << "\n"; + + for (size_t i = 0; i < std::extent<decltype(formats)>::value; i++) { + benchmark_recv_packet_handler(rx_spp, formats[i]); + } + + std::cout << "\n"; + + std::cout << "----------------------------------------------------------\n"; + std::cout << "Benchmark of send with no flow control and mock transport \n"; + std::cout << "----------------------------------------------------------\n"; + std::cout << "spp: " << tx_spp << "\n"; + + std::cout << "*** without timespec ***\n"; + for (size_t i = 0; i < std::extent<decltype(formats)>::value; i++) { + benchmark_send_packet_handler(tx_spp, formats[i], false); + } + std::cout << "\n"; + + std::cout << "*** with timespec ***\n"; + for (size_t i = 0; i < std::extent<decltype(formats)>::value; i++) { + benchmark_send_packet_handler(tx_spp, formats[i], true); + } + std::cout << "\n"; + + std::cout << "----------------------------------------------------------\n"; + std::cout << " Benchmark of flow control functions with mock transport \n"; + std::cout << "----------------------------------------------------------\n"; + std::cout << "*** device3_tx_flow_ctrl with no flow control packet ***\n"; + benchmark_device3_tx_flow_ctrl(false); + std::cout << "\n"; + + std::cout << "*** device3_tx_flow_ctrl with flow control packet ***\n"; + benchmark_device3_tx_flow_ctrl(true); + std::cout << "\n"; + + std::cout << "*** device3_tx_flow_ctrl_ack ***\n"; + benchmark_device3_tx_flow_ctrl_ack(); + std::cout << "\n"; + + std::cout << "*** device3_rx_flow_ctrl with no flow control packet ***\n"; + benchmark_device3_rx_flow_ctrl(false); + std::cout << "\n"; + + std::cout << "*** device3_rx_flow_ctrl with flow control packet ***\n"; + benchmark_device3_rx_flow_ctrl(true); + std::cout << "\n"; + + std::cout << "*** device3_handle_rx_flow_ctrl_ack ***\n"; + benchmark_device3_handle_rx_flow_ctrl_ack(); + std::cout << "\n"; + + return EXIT_SUCCESS; +} |