diff --git a/CMakeLists.txt b/CMakeLists.txt index 83abc193..e56dc2d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1209,6 +1209,15 @@ if(BUILD_SHARED) COMPONENT PerfTools) endif() endforeach() + + if(BUILD_STATIC) + add_executable(benchmark_radix_tree perf/benchmark_radix_tree.cpp) + target_link_libraries(benchmark_radix_tree libzmq-static) + target_include_directories(benchmark_radix_tree + PUBLIC + "${CMAKE_SOURCE_DIR}/src") + endif() + endif() elseif(WITH_PERF_TOOL) message(FATAL_ERROR "Shared library disabled - perf-tools unavailable.") diff --git a/Makefile.am b/Makefile.am index dc379169..71e3f7a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -372,6 +372,17 @@ perf_inproc_lat_SOURCES = perf/inproc_lat.cpp perf_inproc_thr_LDADD = src/libzmq.la perf_inproc_thr_SOURCES = perf/inproc_thr.cpp + +if ENABLE_STATIC +noinst_PROGRAMS += \ + perf/benchmark_radix_tree + +perf_benchmark_radix_tree_DEPENDENCIES = src/libzmq.la +perf_benchmark_radix_tree_CPPFLAGS = -I$(top_srcdir)/src +perf_benchmark_radix_tree_LDADD = $(top_builddir)/src/.libs/libzmq.a \ + ${src_libzmq_la_LIBADD} +perf_benchmark_radix_tree_SOURCES = perf/benchmark_radix_tree.cpp +endif endif if ENABLE_CURVE_KEYGEN diff --git a/perf/benchmark_radix_tree.cpp b/perf/benchmark_radix_tree.cpp new file mode 100644 index 00000000..efc889cc --- /dev/null +++ b/perf/benchmark_radix_tree.cpp @@ -0,0 +1,123 @@ +/* + Copyright (c) 2018 Contributors as noted in the AUTHORS file + + This file is part of libzmq, the ZeroMQ core engine in C++. + + libzmq is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + As a special exception, the Contributors give you permission to link + this library with independent modules to produce an executable, + regardless of the license terms of these independent modules, and to + copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the + terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. + If you modify this library, you must extend this exception to your + version of the library. + + libzmq 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 Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#if __cplusplus >= 201103L + +#include "radix_tree.hpp" +#include "trie.hpp" + +#include +#include +#include +#include +#include + +const std::size_t nkeys = 10000; +const std::size_t nqueries = 100000; +const std::size_t warmup_runs = 10; +const std::size_t samples = 10; +const std::size_t key_length = 20; +const char *chars = "abcdefghijklmnopqrstuvwxyz0123456789"; +const int chars_len = 36; + +template +void benchmark_lookup (T &t, + std::vector &input_set, + std::vector &queries) +{ + std::puts ("Starting warmup..."); + for (std::size_t run = 0; run < warmup_runs; ++run) { + for (auto &query : queries) + t.check (query, key_length); + } + + std::puts ("Collecting samples..."); + std::vector samples_vec; + samples_vec.reserve (samples); + for (std::size_t run = 0; run < samples; ++run) { + auto start = std::chrono::high_resolution_clock::now (); + for (auto &query : queries) + t.check (query, key_length); + auto end = std::chrono::high_resolution_clock::now (); + samples_vec.push_back ( + std::chrono::duration_cast ((end - start)) + .count ()); + } + + for (const auto &sample : samples_vec) + std::printf ("%.2lf\n", sample); +} + +int main () +{ + // Generate input set. + std::minstd_rand rng (123456789); + std::vector input_set; + std::vector queries; + input_set.reserve (nkeys); + queries.reserve (nqueries); + + for (std::size_t i = 0; i < nkeys; ++i) { + unsigned char *key = new unsigned char[key_length]; + for (std::size_t j = 0; j < key_length; j++) + key[j] = static_cast (chars[rng () % chars_len]); + input_set.emplace_back (key); + } + for (std::size_t i = 0; i < nqueries; ++i) + queries.push_back (input_set[rng () % nkeys]); + + // Initialize both data structures. + // + // Keeping initialization out of the benchmarking function helps + // heaptrack detect peak memory consumption of the radix tree. + zmq::trie_t trie; + zmq::radix_tree radix_tree; + for (auto &key : input_set) { + trie.add (key, key_length); + radix_tree.add (key, key_length); + } + + // Create a benchmark. + std::puts ("[trie]"); + benchmark_lookup (trie, input_set, queries); + + std::puts ("[radix_tree]"); + benchmark_lookup (radix_tree, input_set, queries); + + for (auto &op : input_set) + delete[] op; +} + +#else + +int main () +{ +} + +#endif diff --git a/src/radix_tree.cpp b/src/radix_tree.cpp index 7bc84508..64e305f5 100644 --- a/src/radix_tree.cpp +++ b/src/radix_tree.cpp @@ -210,9 +210,9 @@ match_result::match_result (size_t i, { } -inline match_result zmq::radix_tree::match (const unsigned char *key, - size_t size, - bool check = false) const +match_result zmq::radix_tree::match (const unsigned char *key, + size_t size, + bool check = false) const { zmq_assert (key); @@ -553,26 +553,3 @@ size_t zmq::radix_tree::size () const { return size_; } - -static void visit_child (node child_node, size_t level) -{ - zmq_assert (level > 0); - - for (size_t i = 0; i < 4 * (level - 1) + level; ++i) - putchar (' '); - printf ("`-> "); - for (uint32_t i = 0; i < child_node.prefix_length (); ++i) - printf ("%c", child_node.prefix ()[i]); - if (child_node.refcount () > 0) - printf (" [*]"); - printf ("\n"); - for (uint32_t i = 0; i < child_node.edgecount (); ++i) - visit_child (child_node.node_at (i), level + 1); -} - -void zmq::radix_tree::print () -{ - puts ("[root]"); - for (uint32_t i = 0; i < root_.edgecount (); ++i) - visit_child (root_.node_at (i), 1); -} diff --git a/src/radix_tree.hpp b/src/radix_tree.hpp index b07e4e4f..3e3834ea 100644 --- a/src/radix_tree.hpp +++ b/src/radix_tree.hpp @@ -133,11 +133,10 @@ class radix_tree void apply (void (*func) (unsigned char *data_, size_t size_, void *arg_), void *arg); - void print (); size_t size () const; private: - match_result + inline match_result match (const unsigned char *key, size_t size, bool check) const; node root_; diff --git a/unittests/unittest_radix_tree.cpp b/unittests/unittest_radix_tree.cpp index 10c2d750..6d999e3a 100644 --- a/unittests/unittest_radix_tree.cpp +++ b/unittests/unittest_radix_tree.cpp @@ -256,26 +256,6 @@ void test_apply () delete vec; } -void test_print () -{ - zmq::radix_tree tree; - - // Adapted from the example on wikipedia. - std::vector keys; - keys.push_back ("tester"); - keys.push_back ("water"); - keys.push_back ("slow"); - keys.push_back ("slower"); - keys.push_back ("test"); - keys.push_back ("team"); - keys.push_back ("toast"); - - for (size_t i = 0; i < keys.size (); ++i) - tree_add (tree, keys[i]); - - tree.print (); -} - int main (void) { setup_test_environment (); @@ -306,7 +286,5 @@ int main (void) RUN_TEST (test_apply); - RUN_TEST (test_print); - return UNITY_END (); }