aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorCiro Nishiguchi <ciro.nishiguchi@ni.com>2018-10-27 11:10:26 -0500
committerBrent Stapleton <brent.stapleton@ettus.com>2019-01-10 17:26:18 -0800
commitfd3f5d011fb99304402cbf3c1e8c596478316119 (patch)
tree17d98a9879d7dff88227cbe53fab342119963675 /host
parente934e56ce2f8ca0c2f843577032f1a22bf938930 (diff)
downloaduhd-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.hpp4
-rw-r--r--host/tests/CMakeLists.txt13
-rw-r--r--host/tests/common/CMakeLists.txt2
-rw-r--r--host/tests/common/mock_zero_copy.cpp40
-rw-r--r--host/tests/common/mock_zero_copy.hpp82
-rw-r--r--host/tests/device3_test.cpp4
-rw-r--r--host/tests/packet_handler_benchmark.cpp423
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;
+}