├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── benchmarks ├── benchmark.hpp ├── console_output_formatter.hpp ├── csv_output_formatter.hpp ├── guard_ptr_benchmark.hpp ├── hash_map_benchmark.hpp ├── list_benchmark.hpp ├── main.cpp ├── marked_ptr.natvis ├── output_formatter.hpp ├── process_memory.cpp ├── process_memory.hpp ├── queue_benchmark.hpp ├── test_execution.cpp └── test_execution.hpp ├── include ├── debug_helper.hpp ├── emr │ ├── acquire_guard.hpp │ ├── debra.hpp │ ├── debra_impl.hpp │ ├── detail │ │ ├── aligned_object.hpp │ │ ├── allocation_tracker.hpp │ │ ├── backoff.hpp │ │ ├── concurrent_ptr.hpp │ │ ├── deletable_object.hpp │ │ ├── guard_ptr.hpp │ │ ├── marked_ptr.hpp │ │ ├── orphan.hpp │ │ ├── perf_counter.hpp │ │ ├── port.hpp │ │ └── thread_block_list.hpp │ ├── dummy.hpp │ ├── epoch_based.hpp │ ├── epoch_based_impl.hpp │ ├── hazard_pointer.hpp │ ├── hazard_pointer_impl.hpp │ ├── lock_free_ref_count.hpp │ ├── lock_free_ref_count_impl.hpp │ ├── new_epoch_based.hpp │ ├── new_epoch_based_impl.hpp │ ├── quiescent_state_based.hpp │ ├── quiescent_state_based_impl.hpp │ ├── stamp_it.hpp │ └── stamp_it_impl.hpp ├── hash_map.hpp ├── list.hpp └── queue.hpp └── test ├── concurrent_ptr_test.cpp ├── debra_test.cpp ├── epoch_based_test.cpp ├── hash_map_test.cpp ├── hazard_pointer_test.cpp ├── list_test.cpp ├── lock_free_ref_count_test.cpp ├── main.cpp ├── marked_ptr_test.cpp ├── new_epoch_based_test.cpp ├── queue_test.cpp ├── quiescent_state_based_test.cpp └── stamp_it_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | \.idea/ 3 | \cmake-*/ 4 | \bin/ 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(benchmark) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 5 | 6 | set(Boost_USE_STATIC_LIBS ON) 7 | set(Boost_USE_MULTITHREADED ON) 8 | set(Boost_USE_STATIC_RUNTIME ON) 9 | 10 | enable_testing() 11 | 12 | find_package(Boost 1.61.0 COMPONENTS program_options REQUIRED) 13 | find_package(GTest REQUIRED) 14 | find_package(Threads REQUIRED) 15 | 16 | set(EMR_FILES 17 | include/emr/detail/aligned_object.hpp 18 | include/emr/detail/allocation_tracker.hpp 19 | include/emr/detail/backoff.hpp 20 | include/emr/detail/concurrent_ptr.hpp 21 | include/emr/detail/deletable_object.hpp 22 | include/emr/detail/guard_ptr.hpp 23 | include/emr/detail/marked_ptr.hpp 24 | include/emr/detail/orphan.hpp 25 | include/emr/detail/perf_counter.hpp 26 | include/emr/detail/port.hpp 27 | include/emr/detail/thread_block_list.hpp 28 | include/emr/acquire_guard.hpp 29 | include/emr/debra.hpp 30 | include/emr/debra_impl.hpp 31 | include/emr/dummy.hpp 32 | include/emr/epoch_based.hpp 33 | include/emr/epoch_based_impl.hpp 34 | include/emr/hazard_pointer.hpp 35 | include/emr/hazard_pointer_impl.hpp 36 | include/emr/lock_free_ref_count.hpp 37 | include/emr/lock_free_ref_count_impl.hpp 38 | include/emr/new_epoch_based.hpp 39 | include/emr/new_epoch_based_impl.hpp 40 | include/emr/quiescent_state_based.hpp 41 | include/emr/quiescent_state_based_impl.hpp 42 | include/emr/stamp_it.hpp 43 | include/emr/stamp_it_impl.hpp 44 | include/debug_helper.hpp 45 | include/hash_map.hpp 46 | include/list.hpp 47 | include/queue.hpp) 48 | 49 | set(BENCHMARK_FILES 50 | benchmarks/benchmark.hpp 51 | benchmarks/console_output_formatter.hpp 52 | benchmarks/csv_output_formatter.hpp 53 | benchmarks/guard_ptr_benchmark.hpp 54 | benchmarks/hash_map_benchmark.hpp 55 | benchmarks/list_benchmark.hpp 56 | benchmarks/main.cpp 57 | benchmarks/output_formatter.hpp 58 | benchmarks/process_memory.hpp 59 | benchmarks/process_memory.cpp 60 | benchmarks/queue_benchmark.hpp 61 | benchmarks/test_execution.cpp 62 | benchmarks/test_execution.hpp) 63 | 64 | set(TEST_FILES 65 | test/concurrent_ptr_test.cpp 66 | test/epoch_based_test.cpp 67 | test/hash_map_test.cpp 68 | test/hazard_pointer_test.cpp 69 | test/list_test.cpp 70 | test/lock_free_ref_count_test.cpp 71 | test/main.cpp 72 | test/marked_ptr_test.cpp 73 | test/new_epoch_based_test.cpp 74 | test/queue_test.cpp 75 | test/quiescent_state_based_test.cpp 76 | test/stamp_it_test.cpp 77 | test/debra_test.cpp) 78 | 79 | include_directories(include) 80 | include_directories(${Boost_INCLUDE_DIR}) 81 | include_directories(${GTEST_INCLUDE_DIRS}) 82 | 83 | link_directories(${Boost_LIBRARY_DIR}) 84 | 85 | add_definitions(-DBOOST_NO_AUTO_PTR) 86 | 87 | add_executable(benchmark ${BENCHMARK_FILES} ${EMR_FILES}) 88 | add_executable(gtest ${TEST_FILES} ${EMR_FILES}) 89 | 90 | target_link_libraries(gtest ${GTEST_BOTH_LIBRARIES}) 91 | 92 | target_link_libraries(benchmark ${Boost_LIBRARIES}) 93 | if(CMAKE_THREAD_LIBS_INIT) 94 | target_link_libraries(benchmark "${CMAKE_THREAD_LIBS_INIT}") 95 | target_link_libraries(gtest "${CMAKE_THREAD_LIBS_INIT}") 96 | endif() 97 | 98 | add_test(AllTests gtest) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Manuel Pöter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### I have started a new project based on this work: https://github.com/mpoeter/xenium 2 | **All further development is done there!** 3 | 4 | --- 5 | 6 | # Effective Memory Reclamation 7 | This repository contains the C++ implementations of various 8 | lock-free reclamation schemes that I have implemented in 9 | the course of my [Master's Thesis](http://www.ub.tuwien.ac.at/dipl/VL/51367.pdf). 10 | 11 | The implemented schemes are: 12 | * Lock-Free Reference Counting 13 | * [Hazard Pointers](http://www.cs.otago.ac.nz/cosc440/readings/hazard-pointers.pdf) 14 | * Quiescent State Based Reclamation 15 | * [Epoch Based Reclamation](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf) 16 | * [New Epoch Based Reclamation](http://csng.cs.toronto.edu/publication_files/0000/0159/jpdc07.pdf) 17 | * [DEBRA](http://www.cs.utoronto.ca/~tabrown/debra/paper.podc15.pdf) 18 | * Stamp-it 19 | 20 | The last one (Stamp-it) is a new reclamation scheme that 21 | I have developed as part my thesis. All schemes and their 22 | implementations are described in detail in the thesis. 23 | 24 | The implementations are based on an adapted version 25 | of the interfaces proposed by Robison in [Policy-Based Design 26 | for Safe Destruction in Concurrent Containers](http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3712.pdf). 27 | A detailed list of how my version differs from the original proposal 28 | can be found in the Thesis. 29 | 30 | This repository also contains implementations of Michael and Scott's 31 | lock-free queue, as well as Michael and Harris' list based set 32 | and hash-map. These data structures are implemented using said 33 | interface and can therefore be used with the different reclamation 34 | schemes. 35 | 36 | The benchmark folder contains a set of benchmarks that were 37 | used to analyze the performance impact of the various reclamation 38 | schemes in different scenarios and on different architectures; 39 | the results can be found in https://github.com/mpoeter/emr-benchmarks 40 | -------------------------------------------------------------------------------- /benchmarks/benchmark.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "output_formatter.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | struct thread_local_data 14 | { 15 | std::mt19937 randomizer; 16 | size_t number_of_operations = 0; 17 | std::chrono::duration runtime; 18 | }; 19 | 20 | struct benchmark 21 | { 22 | virtual ~benchmark() {} 23 | virtual void setup(const boost::program_options::variables_map& vm) = 0; 24 | virtual void run(thread_local_data& data) = 0; 25 | virtual const std::type_info& reclaimer_type() const = 0; 26 | virtual std::string get_params() { return std::string(); } 27 | virtual void get_data(data_record& record) {} 28 | 29 | #ifdef TRACK_ALLOCATIONS 30 | virtual emr::detail::allocation_tracker& allocation_tracker() = 0; 31 | #endif 32 | }; 33 | 34 | template 35 | inline void add_performance_counters(data_record& record) {} 36 | 37 | template 38 | struct benchmark_with_reclaimer : benchmark 39 | { 40 | virtual const std::type_info& reclaimer_type() const override 41 | { 42 | return typeid(Reclaimer); 43 | } 44 | 45 | virtual void get_data(data_record& record) 46 | { 47 | add_performance_counters(record); 48 | } 49 | 50 | #ifdef TRACK_ALLOCATIONS 51 | virtual emr::detail::allocation_tracker& allocation_tracker() 52 | { 53 | return Reclaimer::allocation_tracker; 54 | } 55 | #endif 56 | }; 57 | 58 | #ifdef WITH_PERF_COUNTER 59 | template <> 60 | inline void add_performance_counters(data_record& record) 61 | { 62 | auto cnt = emr::stamp_it::get_performance_counters(); 63 | record.add("push_iterations", std::to_string(cnt.push_iterations / (double)cnt.push_calls)); 64 | record.add("remove_next_iterations", std::to_string(cnt.remove_next_iterations / (double)cnt.remove_calls)); 65 | record.add("remove_prev_iterations", std::to_string(cnt.remove_prev_iterations / (double)cnt.remove_calls)); 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /benchmarks/console_output_formatter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "output_formatter.hpp" 4 | 5 | #include 6 | 7 | struct console_output_formatter : output_formatter 8 | { 9 | virtual void write(const data_record& record) override 10 | { 11 | if (first_output) 12 | { 13 | write(record, false); 14 | std::cout << "====================" << std::endl; 15 | first_output = false; 16 | } 17 | write(record, true); 18 | } 19 | private: 20 | void write(const data_record& record, bool ops_only) 21 | { 22 | for (auto& entry : record.entries) 23 | if (entry.first != "ns/op" ^ ops_only) 24 | std::cout << entry.first << ": " << entry.second << std::endl; 25 | } 26 | 27 | bool first_output = true; 28 | }; -------------------------------------------------------------------------------- /benchmarks/csv_output_formatter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "output_formatter.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | struct csv_output_formatter : output_formatter 12 | { 13 | csv_output_formatter(const std::string& filename) 14 | { 15 | file.open(filename, std::fstream::in | std::fstream::out | std::fstream::app); 16 | if (!file.is_open()) 17 | throw std::runtime_error("Failed to open file " + filename); 18 | 19 | std::string line; 20 | if (std::getline(file, line)) 21 | parse_header(line); 22 | 23 | file.clear(); 24 | file.exceptions(std::ifstream::failbit | std::ifstream::badbit); 25 | file.seekp(0, std::ios_base::end); 26 | } 27 | 28 | virtual void write(const data_record& record) override 29 | { 30 | if (header.empty()) 31 | write_header(record); 32 | else 33 | validate_record(record); 34 | write_record(record); 35 | } 36 | 37 | private: 38 | void parse_header(const std::string& line) 39 | { 40 | boost::split(header, line, boost::is_any_of("\t")); 41 | } 42 | 43 | void write_header(const data_record& record) 44 | { 45 | for (size_t i = 0; i < record.entries.size(); ++i) 46 | { 47 | auto& entry = record.entries[i]; 48 | header.push_back(entry.first); 49 | file << entry.first; 50 | if (i < record.entries.size() - 1) 51 | file << '\t'; 52 | } 53 | file << std::endl; 54 | } 55 | 56 | void validate_record(const data_record& record) 57 | { 58 | if (header.size() != record.entries.size()) 59 | throw std::runtime_error("Size of record does not match that of other records in this CSV file."); 60 | 61 | for (size_t i = 0; i < record.entries.size(); ++i) 62 | if (header[i] != record.entries[i].first) 63 | throw std::runtime_error("Header at position " + std::to_string(i) + " are different - expected: " + 64 | header[i] + "; actual: " + record.entries[i].first); 65 | } 66 | 67 | void write_record(const data_record& record) 68 | { 69 | for (size_t i = 0; i < record.entries.size(); ++i) 70 | { 71 | file << record.entries[i].second; 72 | if (i < record.entries.size() - 1) 73 | file << '\t'; 74 | } 75 | file << std::endl; 76 | } 77 | 78 | std::fstream file; 79 | std::vector header; 80 | }; -------------------------------------------------------------------------------- /benchmarks/guard_ptr_benchmark.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "benchmark.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | template 10 | struct guard_ptr_benchmark : benchmark_with_reclaimer 11 | { 12 | virtual void setup(const boost::program_options::variables_map& vm) override; 13 | virtual void run(thread_local_data& data) override; 14 | 15 | private: 16 | struct node : Reclaimer::template enable_concurrent_ptr {}; 17 | using concurrent_ptr = typename Reclaimer::template concurrent_ptr; 18 | concurrent_ptr obj; 19 | }; 20 | 21 | template 22 | void guard_ptr_benchmark::setup(const boost::program_options::variables_map& vm) 23 | { 24 | obj.store(new node(), std::memory_order_relaxed); 25 | } 26 | 27 | template 28 | void guard_ptr_benchmark::run(thread_local_data& data) 29 | { 30 | const size_t n = 100; 31 | 32 | for (size_t i = 0; i < n; i++) 33 | { 34 | auto guard = emr::acquire_guard(obj, std::memory_order_relaxed); 35 | } 36 | 37 | // Record another n operations. 38 | data.number_of_operations += n; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /benchmarks/hash_map_benchmark.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "benchmark.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | template 12 | struct hash_map_benchmark : benchmark_with_reclaimer 13 | { 14 | virtual void setup(const boost::program_options::variables_map& vm) override; 15 | virtual void run(thread_local_data& data) override; 16 | virtual std::string get_params() override; 17 | 18 | private: 19 | using Key = size_t; 20 | using Value = std::unique_ptr; 21 | 22 | static const size_t BlockSize = 1024; 23 | static const size_t Buckets = 2048; 24 | static const size_t Blocks = 1000; 25 | static const size_t MaxCache = 10000; 26 | static const size_t DifferentBlockResults = 3 * MaxCache / Blocks; 27 | 28 | using hash_map = emr::hash_map; 29 | 30 | Value calc_block(size_t idx); 31 | void limit_cache_size(); 32 | 33 | std::atomic number_of_cached_entries; 34 | std::atomic cache_hits; 35 | std::atomic cache_queries; 36 | unsigned threads; 37 | hash_map cache; 38 | emr::queue keys; 39 | }; 40 | 41 | template 42 | void hash_map_benchmark::setup(const boost::program_options::variables_map& vm) 43 | { 44 | Key key; 45 | while (keys.try_dequeue(key)) 46 | cache.remove(key); 47 | 48 | cache_hits.store(0, std::memory_order_relaxed); 49 | cache_queries.store(0, std::memory_order_relaxed); 50 | number_of_cached_entries.store(0, std::memory_order_relaxed); 51 | } 52 | 53 | template 54 | auto hash_map_benchmark::calc_block(size_t idx) -> Value 55 | { 56 | Value result(new double[BlockSize]); 57 | // to simulate the cost of calculating a block we simply 58 | // perfom a bunch of meaningless but expensive computations 59 | for (size_t i = 0; i < BlockSize; ++i) 60 | result[i] = sqrt(sin(static_cast(i)) * cos(static_cast(i))); 61 | 62 | return result; 63 | } 64 | 65 | template 66 | void hash_map_benchmark::run(thread_local_data& data) 67 | { 68 | size_t added_entries = 0; 69 | size_t local_cache_hits = 0; 70 | 71 | auto get_or_calc_block = [this, &data, &added_entries](size_t idx) 72 | { 73 | auto r = data.randomizer() % DifferentBlockResults; 74 | auto key = (r << 32) | idx; 75 | 76 | auto result = cache.search(key); 77 | if (!result) 78 | { 79 | auto value = calc_block(r); 80 | if (cache.insert(key, std::move(value), result)) 81 | { 82 | keys.enqueue(key); 83 | ++added_entries; 84 | } 85 | } 86 | return result; 87 | }; 88 | 89 | { 90 | std::vector cache_entries(Blocks); 91 | 92 | typename Reclaimer::region_guard region_guard{}; 93 | added_entries = 0; 94 | 95 | for (size_t block = 0; block < Blocks; ++block) 96 | cache_entries[block] = get_or_calc_block(block); 97 | 98 | number_of_cached_entries.fetch_add(added_entries, std::memory_order_relaxed); 99 | local_cache_hits += Blocks - added_entries; 100 | 101 | Value total_result(new double[BlockSize]); 102 | memset(total_result.get(), 0, sizeof(double) * BlockSize); 103 | for (auto& block : cache_entries) 104 | { 105 | auto block_result = block->value.get(); 106 | for (size_t i = 0; i < BlockSize; ++i) 107 | total_result[i] += block_result[i]; 108 | block.reset(); 109 | } 110 | } 111 | { 112 | typename Reclaimer::region_guard region_guard{}; 113 | limit_cache_size(); 114 | } 115 | 116 | cache_hits.fetch_add(local_cache_hits, std::memory_order_relaxed); 117 | cache_queries.fetch_add(Blocks, std::memory_order_relaxed); 118 | data.number_of_operations += 1; 119 | } 120 | 121 | template 122 | void hash_map_benchmark::limit_cache_size() 123 | { 124 | auto cache_size = number_of_cached_entries.load(std::memory_order_relaxed); 125 | while (cache_size > MaxCache) 126 | { 127 | Key key; 128 | if (!keys.try_dequeue(key)) 129 | return; 130 | 131 | if (cache.remove(key)) 132 | cache_size = --number_of_cached_entries; 133 | else 134 | assert(false); // this should never happen as every key exists only once in the queue 135 | } 136 | } 137 | 138 | template 139 | std::string hash_map_benchmark::get_params() 140 | { 141 | return ""; //"cache-hit-rate: " + std::to_string(cache_hits.load() / (double)cache_queries.load()); 142 | } 143 | -------------------------------------------------------------------------------- /benchmarks/list_benchmark.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "benchmark.hpp" 4 | 5 | #include 6 | #include 7 | 8 | template 9 | struct list_benchmark : benchmark_with_reclaimer 10 | { 11 | virtual void setup(const boost::program_options::variables_map& vm) override; 12 | virtual void run(thread_local_data& data) override; 13 | virtual std::string get_params() override; 14 | 15 | private: 16 | unsigned number_of_elements; 17 | unsigned search_operations; 18 | unsigned modify_operations; 19 | 20 | emr::list list; 21 | 22 | const size_t bits_for_operation_count = 10; 23 | 24 | void populate_list() 25 | { 26 | // we are population the list in a separate thread to avoid having the main thread 27 | // in the global threadlists of the reclaimers. 28 | // this is especially important in case of QSBR since the main thread never explicitly 29 | // goes through a quiescent state. 30 | std::thread initializer([this]() 31 | { 32 | typename Reclaimer::region_guard rg{}; 33 | for (unsigned i = 0, j = 0; i < number_of_elements; ++i, j+= 2) 34 | list.insert(j); 35 | }); 36 | initializer.join(); 37 | } 38 | }; 39 | 40 | template 41 | void list_benchmark::setup(const boost::program_options::variables_map& vm) 42 | { 43 | number_of_elements = vm["elements"].as(); 44 | 45 | auto operations = (1 << bits_for_operation_count); 46 | if (vm.count("modify-fraction")) 47 | { 48 | auto fraction = vm["modify-fraction"].as(); 49 | if (fraction < 0.0 || fraction > 1.0) 50 | throw std::runtime_error("modify-fraction must be >= 0 and <= 1"); 51 | 52 | modify_operations = static_cast(operations * fraction); 53 | } 54 | else 55 | modify_operations = operations / 2; 56 | 57 | search_operations = operations - modify_operations; 58 | 59 | populate_list(); 60 | } 61 | 62 | template 63 | void list_benchmark::run(thread_local_data& data) 64 | { 65 | const size_t n = 100; 66 | 67 | const auto operations = 1 << bits_for_operation_count; 68 | const unsigned range = operations + 1; 69 | const unsigned mask = operations - 1; 70 | const unsigned number_of_keys = std::max(1u, number_of_elements * 2); 71 | 72 | typename Reclaimer::region_guard region_guard{}; 73 | for (size_t i = 0; i < n; i++) 74 | { 75 | auto r = data.randomizer(); 76 | auto insertOp = r & 1; 77 | r >>= 1; 78 | auto action = ((r & mask) % range); 79 | r >>= bits_for_operation_count; 80 | auto key = r % number_of_keys; 81 | 82 | if (action <= search_operations) 83 | list.search(key); 84 | else if (insertOp) 85 | list.insert(key); 86 | else 87 | list.remove(key); 88 | } 89 | 90 | // Record another n operations. 91 | data.number_of_operations += n; 92 | } 93 | 94 | template 95 | std::string list_benchmark::get_params() 96 | { 97 | return "elements: " + std::to_string(number_of_elements) + 98 | "; modify-fraction: " + 99 | std::to_string(modify_operations / static_cast(search_operations + modify_operations)); 100 | } 101 | -------------------------------------------------------------------------------- /benchmarks/main.cpp: -------------------------------------------------------------------------------- 1 | #include "guard_ptr_benchmark.hpp" 2 | #include "list_benchmark.hpp" 3 | #include "queue_benchmark.hpp" 4 | #include "hash_map_benchmark.hpp" 5 | #include "test_execution.hpp" 6 | #include "output_formatter.hpp" 7 | #include "console_output_formatter.hpp" 8 | #include "csv_output_formatter.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | template