├── .gitignore ├── libcuckoo-gdb-printers ├── libcuckoo │ ├── __init__.py │ └── printers.py └── README.md ├── tests ├── unit-tests │ ├── test_user_exceptions.h │ ├── test_heterogeneous_compare.h │ ├── test_c_interface.h │ ├── int_int_table.cc │ ├── unit_test_util.cc │ ├── test_maximum_hashpower.h │ ├── test_minimum_load_factor.h │ ├── test_resize.h │ ├── test_hash_properties.h │ ├── int_int_table.h │ ├── test_iterator.h │ ├── test_noncopyable_types.h │ ├── CMakeLists.txt │ ├── test_constructor.h │ ├── test_locked_table.h │ ├── test_bucket_container.h │ ├── test_minimum_load_factor.cc │ ├── test_maximum_hashpower.cc │ ├── test_hash_properties.cc │ ├── test_resize.cc │ ├── unit_test_util.hh │ ├── test_iterator.cc │ ├── test_noncopyable_types.cc │ ├── test_runner.cc │ ├── test_heterogeneous_compare.cc │ ├── test_constructor.cc │ ├── test_user_exceptions.cc │ ├── test_bucket_container.cc │ └── test_c_interface.cc ├── pcg │ └── CMakeLists.txt ├── stress-tests │ ├── CMakeLists.txt │ ├── stress_unchecked.cc │ └── stress_checked.cc ├── CMakeLists.txt ├── universal-benchmark │ ├── CMakeLists.txt │ ├── universal_gen.hh │ ├── README.md │ ├── universal_table_wrapper.hh │ └── universal_benchmark.cc └── test_util.hh ├── libcuckoo-c ├── CMakeLists.txt └── cuckoo_table_template.h ├── libcuckoo ├── libcuckoo-config.cmake ├── mainpage.dox ├── cuckoohash_config.hh ├── CMakeLists.txt └── cuckoohash_util.hh ├── examples ├── int_str_table.cc ├── hellohash.cc ├── CMakeLists.txt ├── int_str_table.h ├── blob_blob_table.h ├── blob_blob_table.cc ├── nested_table.cc ├── count_freq.cc └── c_hash.c ├── CMakeLists.txt ├── hooks └── pre-commit ├── LICENSE ├── .clang-format └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | build/ 3 | -------------------------------------------------------------------------------- /libcuckoo-gdb-printers/libcuckoo/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit-tests/test_user_exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_user_exceptions(); 4 | -------------------------------------------------------------------------------- /tests/unit-tests/test_heterogeneous_compare.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_heterogeneous_compare(); 4 | -------------------------------------------------------------------------------- /tests/unit-tests/test_c_interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_c_interface_basic(); 4 | void test_c_interface_locked_table(); 5 | -------------------------------------------------------------------------------- /tests/unit-tests/int_int_table.cc: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include "int_int_table.h" 3 | } 4 | 5 | #include 6 | -------------------------------------------------------------------------------- /libcuckoo-c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install( 2 | FILES 3 | cuckoo_table_template.h 4 | cuckoo_table_template.cc 5 | DESTINATION 6 | ${CMAKE_INSTALL_PREFIX}/include/libcuckoo-c 7 | ) 8 | -------------------------------------------------------------------------------- /tests/pcg/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(pcg INTERFACE) 2 | 3 | # Include relative to the base directory 4 | target_include_directories(pcg INTERFACE 5 | $ 6 | ) 7 | 8 | -------------------------------------------------------------------------------- /tests/unit-tests/unit_test_util.cc: -------------------------------------------------------------------------------- 1 | #include "unit_test_util.hh" 2 | 3 | std::atomic &get_unfreed_bytes() { 4 | static std::atomic unfreed_bytes(0L); 5 | return unfreed_bytes; 6 | } 7 | -------------------------------------------------------------------------------- /tests/unit-tests/test_maximum_hashpower.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_maximum_hashpower_initialized_to_default(); 4 | void test_maximum_hashpower_caps_any_expansion(); 5 | void test_maximum_hash_power_none(); 6 | -------------------------------------------------------------------------------- /tests/unit-tests/test_minimum_load_factor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_minimum_load_factor_initialized_to_default(); 4 | void test_minimum_load_factor_caps_automatic_expansion(); 5 | void test_minimum_load_factor_invalid(); 6 | -------------------------------------------------------------------------------- /tests/unit-tests/test_resize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_resize_rehash_empty_table(); 4 | void test_resize_reserve_empty_table(); 5 | void test_resize_reserve_calc(); 6 | void test_resize_number_of_frees(); 7 | void test_resize_on_non_relocatable_type(); 8 | -------------------------------------------------------------------------------- /libcuckoo/libcuckoo-config.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # libcuckoo-config.cmake 3 | # 4 | 5 | include (CMakeFindDependencyMacro) 6 | 7 | set(THREADS_PREFER_PTHREAD_FLAG ON) 8 | find_dependency(Threads) 9 | 10 | include ("${CMAKE_CURRENT_LIST_DIR}/libcuckoo-targets.cmake") 11 | -------------------------------------------------------------------------------- /tests/unit-tests/test_hash_properties.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_hash_properties_int_alt_index_works_correctly(); 4 | void test_hash_properties_string_alt_index_works_correctly(); 5 | void test_hash_properties_hash_with_larger_hashpower_only_adds_top_bits(); 6 | -------------------------------------------------------------------------------- /tests/unit-tests/int_int_table.h: -------------------------------------------------------------------------------- 1 | #ifndef INT_INT_TABLE_H 2 | #define INT_INT_TABLE_H 3 | 4 | #define CUCKOO_TABLE_NAME int_int_table 5 | #define CUCKOO_KEY_TYPE int 6 | #define CUCKOO_MAPPED_TYPE int 7 | 8 | #include 9 | 10 | #endif // INT_INT_TABLE_H 11 | -------------------------------------------------------------------------------- /tests/unit-tests/test_iterator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_iterator_types(); 4 | void test_iterator_empty_table_iteration(); 5 | void test_iterator_walkthrough(); 6 | void test_iterator_modification(); 7 | void test_iterator_lock_table_blocks_inserts(); 8 | void test_iterator_cast_iterator_to_const_iterator(); 9 | -------------------------------------------------------------------------------- /tests/unit-tests/test_noncopyable_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_noncopyable_types_insert_and_update(); 4 | void test_noncopyable_types_insert_or_assign(); 5 | void test_noncopyable_types_upsert(); 6 | void test_noncopyable_types_iteration(); 7 | void test_noncopyable_types_nested_table(); 8 | void test_noncopyable_types_insert_lifetime(); 9 | void test_noncopyable_types_erase_fn(); 10 | void test_noncopyable_types_uprase_fn(); 11 | -------------------------------------------------------------------------------- /libcuckoo-gdb-printers/README.md: -------------------------------------------------------------------------------- 1 | # GDB Printers 2 | 3 | This directory contains a python module that allows pretty-printing a 4 | cuckoohash map. 5 | 6 | ## Usage 7 | 8 | In order to use the libcuckoo pretty printers, add the following to your 9 | `~/.gdbinit`: 10 | 11 | ``` 12 | python 13 | import sys 14 | sys.path.insert(0, '/path/to/libcuckoo/libcuckoo-gdb-printers') 15 | from libcuckoo.printers import register_libcuckoo_printers 16 | register_libcuckoo_printers (None) 17 | end 18 | ``` 19 | -------------------------------------------------------------------------------- /tests/stress-tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(stress_checked stress_checked.cc) 2 | target_link_libraries(stress_checked 3 | PRIVATE test_util 4 | PRIVATE pcg 5 | PRIVATE libcuckoo 6 | ) 7 | 8 | add_executable(stress_unchecked stress_unchecked.cc) 9 | target_link_libraries(stress_unchecked 10 | PRIVATE test_util 11 | PRIVATE pcg 12 | PRIVATE libcuckoo 13 | ) 14 | 15 | add_test(NAME stress_checked COMMAND stress_checked) 16 | add_test(NAME stress_unchecked COMMAND stress_unchecked) 17 | -------------------------------------------------------------------------------- /examples/int_str_table.cc: -------------------------------------------------------------------------------- 1 | // Include the header file with "C" linkage 2 | extern "C" { 3 | #include "int_str_table.h" 4 | } 5 | 6 | // Include the implementation template, which uses the constants defined in 7 | // `int_str_table.h`. The implementation file defines all functions under "C" 8 | // linkage, and should be linked with the corresponding header to generate a 9 | // linkable library. See `CMakeLists.txt` for an example of how this is done to 10 | // create `c_hash`. 11 | #include 12 | -------------------------------------------------------------------------------- /examples/hellohash.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main() { 7 | libcuckoo::cuckoohash_map Table; 8 | 9 | for (int i = 0; i < 100; i++) { 10 | Table.insert(i, "hello"); 11 | } 12 | 13 | for (int i = 0; i < 101; i++) { 14 | std::string out; 15 | 16 | if (Table.find(i, out)) { 17 | std::cout << i << " " << out << std::endl; 18 | } else { 19 | std::cout << i << " NOT FOUND" << std::endl; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.0) 2 | project(libcuckoo 3 | VERSION 0.3.1 4 | LANGUAGES C CXX) 5 | 6 | # put these in the cache so they show up in ccmake 7 | option (BUILD_EXAMPLES "build example libcuckoo programs") 8 | 9 | # Add the libcuckoo interface target 10 | add_subdirectory(libcuckoo) 11 | 12 | # Add C interface target 13 | add_subdirectory(libcuckoo-c) 14 | 15 | # Build examples 16 | if(BUILD_EXAMPLES) 17 | add_subdirectory(examples) 18 | endif() 19 | 20 | # Build tests -- this only builds tests that were specified 21 | enable_testing() 22 | add_subdirectory(tests) 23 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # clang-format pre-commit hook 3 | # 4 | # To use, store as .git/hooks/pre-commit inside your repository and make sure 5 | # it has execute permissions. 6 | # 7 | # This script does not handle file names that contain spaces. 8 | 9 | cfiles=$(git diff --name-only HEAD --diff-filter=d | grep '\.\(cc\|hh\|c\|h\)$') 10 | numerrors=0 11 | for f in $cfiles; do 12 | diffoutput=$(clang-format $f | diff $f -) 13 | if [ -n "$diffoutput" ]; then 14 | [ $numerrors -eq 0 ] && echo >&2 "Unformatted files:"; 15 | echo >&2 "$f"; 16 | ((numerrors++)) 17 | fi 18 | done 19 | exit $numerrors 20 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(nested_table nested_table.cc) 2 | target_link_libraries(nested_table libcuckoo) 3 | 4 | add_executable(hellohash hellohash.cc) 5 | target_link_libraries(hellohash libcuckoo) 6 | 7 | add_executable(count_freq count_freq.cc) 8 | target_link_libraries(count_freq libcuckoo) 9 | 10 | add_library(int_str_table STATIC int_str_table.cc) 11 | target_link_libraries(int_str_table libcuckoo) 12 | 13 | add_library(blob_blob_table STATIC blob_blob_table.cc) 14 | target_link_libraries(blob_blob_table libcuckoo) 15 | 16 | add_executable(c_hash c_hash.c) 17 | target_link_libraries(c_hash int_str_table blob_blob_table) 18 | 19 | set_property(TARGET c_hash PROPERTY C_STANDARD 99) 20 | -------------------------------------------------------------------------------- /libcuckoo/mainpage.dox: -------------------------------------------------------------------------------- 1 | /*! \mainpage libcuckoo Documentation 2 | * 3 | * libcuckoo is a high-performance, memory efficient hash table that 4 | * supports concurrent reads and writes. 5 | * 6 | * \ref cuckoohash_map is the class of the hash table. Its interface 7 | * resembles that of STL's unordered_map but does contain some 8 | * important differences. 9 | * 10 | * Internally, the hash table is partitioned into an array of 11 | * buckets, each of which contains \c SLOT_PER_BUCKET slots to 12 | * store items. 13 | * 14 | * Each bucket has a lock to ensure multiple threads don't modify the 15 | * same elements. Most operations will lock no more than two buckets 16 | * at a time, thereby allowing for concurrent reads and writes. 17 | */ 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013, Carnegie Mellon University and Intel Corporation 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | --------------------------- 16 | 17 | The third-party libraries have their own licenses, as detailed in their source 18 | files. 19 | -------------------------------------------------------------------------------- /examples/int_str_table.h: -------------------------------------------------------------------------------- 1 | // Include guard 2 | #ifndef INT_STR_TABLE_H 3 | #define INT_STR_TABLE_H 4 | 5 | // Below we define the constants the table template uses to fill in the 6 | // interface. 7 | // 8 | // All table functions will be prefixed with `int_str_table` 9 | #define CUCKOO_TABLE_NAME int_str_table 10 | // The type of the key is `int` 11 | #define CUCKOO_KEY_TYPE int 12 | // The type of the mapped value is `const char *` 13 | #define CUCKOO_MAPPED_TYPE const char * 14 | 15 | // Including the header after filling in the constants will populate the 16 | // interface. See the template file itself for specific function names; most of 17 | // them correspond to methods in the C++ implementation. 18 | #include 19 | 20 | #endif // INT_STR_TABLE_H 21 | -------------------------------------------------------------------------------- /tests/unit-tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(int_int_table STATIC int_int_table.cc) 2 | target_link_libraries(int_int_table libcuckoo) 3 | 4 | add_executable(unit_tests 5 | test_bucket_container.cc 6 | test_c_interface.cc 7 | test_constructor.cc 8 | test_hash_properties.cc 9 | test_heterogeneous_compare.cc 10 | test_iterator.cc 11 | test_locked_table.cc 12 | test_maximum_hashpower.cc 13 | test_minimum_load_factor.cc 14 | test_noncopyable_types.cc 15 | test_resize.cc 16 | test_user_exceptions.cc 17 | 18 | acutest.h 19 | test_runner.cc 20 | unit_test_util.cc 21 | unit_test_util.hh 22 | ) 23 | 24 | target_link_libraries(unit_tests 25 | PRIVATE libcuckoo 26 | PRIVATE int_int_table 27 | ) 28 | 29 | add_test(NAME unit_tests COMMAND unit_tests) 30 | -------------------------------------------------------------------------------- /tests/unit-tests/test_constructor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_constructor_default_size(); 4 | void test_constructor_given_size(); 5 | void test_constructor_frees_even_with_exceptions(); 6 | void test_constructor_stateful_components(); 7 | void test_constructor_range_constructor(); 8 | void test_constructor_copy_constructor(); 9 | void test_constructor_copy_constructor_other_allocator(); 10 | void test_constructor_move_constructor(); 11 | void test_constructor_move_constructor_different_allocator(); 12 | void test_constructor_initializer_list_constructor(); 13 | void test_constructor_swap_maps(); 14 | void test_constructor_copy_assign_different_allocators(); 15 | void test_constructor_move_assign_different_allocators(); 16 | void test_constructor_move_assign_same_allocators(); 17 | void test_constructor_initializer_list_assignment(); 18 | -------------------------------------------------------------------------------- /tests/unit-tests/test_locked_table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_locked_table_typedefs(); 4 | void test_locked_table_move(); 5 | void test_locked_table_unlock(); 6 | void test_locked_table_info(); 7 | void test_locked_table_clear(); 8 | void test_locked_table_insert_duplicate(); 9 | void test_locked_table_insert_new_key(); 10 | void test_locked_table_insert_lifetime(); 11 | void test_locked_table_erase(); 12 | void test_locked_table_find(); 13 | void test_locked_table_at(); 14 | void test_locked_table_operator_brackets(); 15 | void test_locked_table_count(); 16 | void test_locked_table_equal_range(); 17 | void test_locked_table_rehash(); 18 | void test_locked_table_reserve(); 19 | void test_locked_table_equality(); 20 | void test_locked_table_holds_locks_after_resize(); 21 | void test_locked_table_io(); 22 | void test_empty_locked_table_io(); 23 | -------------------------------------------------------------------------------- /examples/blob_blob_table.h: -------------------------------------------------------------------------------- 1 | // This table was created to illustrate usage of the table with untyped blobs. 2 | // See `int_str_table.h` for more comments regarding the semantics of creating 3 | // header files for use with C programs. 4 | 5 | #ifndef BLOB_BLOB_TABLE_H 6 | #define BLOB_BLOB_TABLE_H 7 | 8 | // In C, assignment is not defined for raw arrays, but it is defined for all 9 | // structs, including those containing arrays. Thus, since the hashtable 10 | // requires that assignment is defined for key and mapped types, we wrap the 11 | // blobs in a struct. 12 | typedef struct { char blob[8]; } key_blob; 13 | 14 | typedef struct { char blob[255]; } mapped_blob; 15 | 16 | #define CUCKOO_TABLE_NAME blob_blob_table 17 | #define CUCKOO_KEY_TYPE key_blob 18 | #define CUCKOO_MAPPED_TYPE mapped_blob 19 | 20 | #include 21 | 22 | #endif // BLOB_BLOB_TABLE_H 23 | -------------------------------------------------------------------------------- /tests/unit-tests/test_bucket_container.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test_bucket_container_default_constructor(); 4 | void test_bucket_container_simple_stateful_allocator(); 5 | void test_bucket_container_copy_construction(); 6 | void test_bucket_container_move_construction(); 7 | void test_bucket_container_copy_assignment_with_propagate(); 8 | void test_bucket_container_copy_assignment_no_propagate(); 9 | void test_bucket_container_move_assignment_with_propagate(); 10 | void test_bucket_container_move_assignment_no_propagate_equal(); 11 | void test_bucket_container_move_assignment_no_propagate_unequal(); 12 | void test_bucket_container_swap_no_propagate(); 13 | void test_bucket_container_swap_propagate(); 14 | void test_bucket_container_setKV_with_throwing_type_maintains_strong_guarantee(); 15 | void test_bucket_container_copy_assignment_with_throwing_type_is_destroyed_properly(); 16 | void test_bucket_container_copy_destroyed_buckets_container(); 17 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(test_util INTERFACE) 2 | target_include_directories(test_util INTERFACE 3 | $ 4 | $ 5 | ) 6 | 7 | # put these in the cache so they show up in ccmake 8 | option (BUILD_TESTS "build all libcuckoo tests") 9 | option (BUILD_STRESS_TESTS "build the stress tests") 10 | option (BUILD_UNIT_TESTS "build the unit tests") 11 | option (BUILD_UNIVERSAL_BENCHMARK "build the universal benchmark and associated tests") 12 | 13 | # Add pcg if we're doing stress tests or universal benchmark 14 | if (BUILD_TESTS OR 15 | BUILD_STRESS_TESTS OR 16 | BUILD_UNIVERSAL_BENCHMARK) 17 | add_subdirectory(pcg) 18 | endif() 19 | 20 | if (BUILD_TESTS OR BUILD_UNIT_TESTS) 21 | add_subdirectory(unit-tests) 22 | endif() 23 | 24 | if (BUILD_TESTS OR BUILD_STRESS_TESTS) 25 | add_subdirectory(stress-tests) 26 | endif() 27 | 28 | if (BUILD_TESTS OR BUILD_UNIVERSAL_BENCHMARK) 29 | add_subdirectory(universal-benchmark) 30 | endif() 31 | -------------------------------------------------------------------------------- /examples/blob_blob_table.cc: -------------------------------------------------------------------------------- 1 | // This table was created to illustrate usage of the table with untyped blobs. 2 | // See `int_str_table.cc` for more comments regarding the semantics of creating 3 | // implementation files for use with C programs. 4 | 5 | extern "C" { 6 | #include "blob_blob_table.h" 7 | } 8 | 9 | // The hashtable uses the generic std::hash template wrapper as the hash 10 | // function and std::equal_to as the equality function for key hashing and 11 | // comparison. Since neither of these templates are specialized for our custom 12 | // key_blob type, we must create our own specializations. 13 | 14 | #include 15 | #include 16 | 17 | namespace std { 18 | template <> struct hash { 19 | size_t operator()(const key_blob &kb) const { return *(size_t *)kb.blob; } 20 | }; 21 | 22 | template <> struct equal_to { 23 | bool operator()(const key_blob &lhs, const key_blob &rhs) const { 24 | return memcmp(lhs.blob, rhs.blob, sizeof(lhs.blob)) == 0; 25 | } 26 | }; 27 | } 28 | 29 | #include 30 | -------------------------------------------------------------------------------- /libcuckoo/cuckoohash_config.hh: -------------------------------------------------------------------------------- 1 | /** \file */ 2 | 3 | #ifndef _CUCKOOHASH_CONFIG_HH 4 | #define _CUCKOOHASH_CONFIG_HH 5 | 6 | #include 7 | #include 8 | 9 | namespace libcuckoo { 10 | 11 | //! The default maximum number of keys per bucket 12 | constexpr size_t DEFAULT_SLOT_PER_BUCKET = 4; 13 | 14 | //! The default number of elements in an empty hash table 15 | constexpr size_t DEFAULT_SIZE = (1U << 16) * DEFAULT_SLOT_PER_BUCKET; 16 | 17 | //! The default minimum load factor that the table allows for automatic 18 | //! expansion. It must be a number between 0.0 and 1.0. The table will throw 19 | //! load_factor_too_low if the load factor falls below this value 20 | //! during an automatic expansion. 21 | constexpr double DEFAULT_MINIMUM_LOAD_FACTOR = 0.05; 22 | 23 | //! An alias for the value that sets no limit on the maximum hashpower. If this 24 | //! value is set as the maximum hashpower limit, there will be no limit. This 25 | //! is also the default initial value for the maximum hashpower in a table. 26 | constexpr size_t NO_MAXIMUM_HASHPOWER = std::numeric_limits::max(); 27 | 28 | //! set LIBCUCKOO_DEBUG to 1 to enable debug output 29 | #define LIBCUCKOO_DEBUG 0 30 | 31 | } // namespace libcuckoo 32 | 33 | #endif // _CUCKOOHASH_CONFIG_HH 34 | -------------------------------------------------------------------------------- /tests/unit-tests/test_minimum_load_factor.cc: -------------------------------------------------------------------------------- 1 | #include "test_minimum_load_factor.h" 2 | 3 | #define TEST_NO_MAIN 4 | #include "acutest.h" 5 | 6 | #include 7 | 8 | #include "unit_test_util.hh" 9 | #include 10 | #include 11 | 12 | void test_minimum_load_factor_initialized_to_default() { 13 | IntIntTable tbl; 14 | TEST_CHECK(tbl.minimum_load_factor() == 15 | libcuckoo::DEFAULT_MINIMUM_LOAD_FACTOR); 16 | } 17 | 18 | class BadHashFunction { 19 | public: 20 | size_t operator()(int) { return 0; } 21 | }; 22 | 23 | void test_minimum_load_factor_caps_automatic_expansion() { 24 | const size_t slot_per_bucket = 4; 25 | libcuckoo::cuckoohash_map, 26 | std::allocator>, 27 | slot_per_bucket> 28 | tbl(16); 29 | tbl.minimum_load_factor(0.6); 30 | 31 | for (size_t i = 0; i < 2 * slot_per_bucket; ++i) { 32 | tbl.insert(i, i); 33 | } 34 | 35 | TEST_EXCEPTION(tbl.insert(2 * slot_per_bucket, 0), 36 | libcuckoo::load_factor_too_low); 37 | } 38 | 39 | void test_minimum_load_factor_invalid() { 40 | IntIntTable tbl; 41 | TEST_EXCEPTION(tbl.minimum_load_factor(-0.01), std::invalid_argument); 42 | TEST_EXCEPTION(tbl.minimum_load_factor(1.01), std::invalid_argument); 43 | } 44 | -------------------------------------------------------------------------------- /libcuckoo-gdb-printers/libcuckoo/printers.py: -------------------------------------------------------------------------------- 1 | import gdb 2 | import gdb.printing 3 | 4 | class CuckoohashMapPrinter: 5 | """Print a cuckoohash_map object""" 6 | 7 | def __init__(self, val): 8 | self.val = val 9 | self.slot_per_bucket = int(self.val.type.template_argument(5)) 10 | 11 | def _iterator(self): 12 | buckets_obj = self.val['buckets_'] 13 | hashpower = buckets_obj['hashpower_']['_M_i'] 14 | buckets_ptr = buckets_obj['buckets_'] 15 | if not buckets_ptr: 16 | return 17 | num_buckets = int(2**hashpower) 18 | for i in range(num_buckets): 19 | bucket = (buckets_ptr + i).dereference() 20 | storage_value_type = gdb.lookup_type(str(bucket.type) + '::storage_value_type') 21 | for j in range(self.slot_per_bucket): 22 | if bucket['occupied_']['_M_elems'][j]: 23 | value_blob = bucket['values_']['_M_elems'][j] 24 | storage_value = value_blob.cast(storage_value_type) 25 | yield ('key', storage_value['first']) 26 | yield ('value', storage_value['second']) 27 | 28 | def children(self): 29 | return self._iterator() 30 | 31 | def to_string(self): 32 | return 'cuckoohash_map' 33 | 34 | def display_hint(self): 35 | return 'map' 36 | 37 | def build_pretty_printer(): 38 | pp = gdb.printing.RegexpCollectionPrettyPrinter("libcuckoo") 39 | pp.add_printer('cuckoohash_map', '^cuckoohash_map<.*>$', CuckoohashMapPrinter) 40 | return pp 41 | 42 | def register_libcuckoo_printers(objfile): 43 | gdb.printing.register_pretty_printer(objfile, build_pretty_printer()) 44 | -------------------------------------------------------------------------------- /examples/nested_table.cc: -------------------------------------------------------------------------------- 1 | /* We demonstrate how to nest hash tables within one another, to store 2 | * unstructured data, kind of like JSON. There's still the limitation that it's 3 | * statically typed. */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | typedef libcuckoo::cuckoohash_map InnerTable; 13 | typedef libcuckoo::cuckoohash_map> OuterTable; 14 | 15 | int main() { 16 | OuterTable tbl; 17 | 18 | tbl.insert("bob", std::unique_ptr(new InnerTable)); 19 | tbl.update_fn("bob", [](std::unique_ptr &innerTbl) { 20 | innerTbl->insert("nickname", "jimmy"); 21 | innerTbl->insert("pet", "dog"); 22 | innerTbl->insert("food", "bagels"); 23 | }); 24 | 25 | tbl.insert("jack", std::unique_ptr(new InnerTable)); 26 | tbl.update_fn("jack", [](std::unique_ptr &innerTbl) { 27 | innerTbl->insert("friend", "bob"); 28 | innerTbl->insert("activity", "sleeping"); 29 | innerTbl->insert("language", "javascript"); 30 | }); 31 | 32 | { 33 | auto lt = tbl.lock_table(); 34 | for (const auto &item : lt) { 35 | std::cout << "Properties for " << item.first << std::endl; 36 | auto innerLt = item.second->lock_table(); 37 | for (auto innerItem : innerLt) { 38 | std::cout << "\t" << innerItem.first << " = " << innerItem.second 39 | << std::endl; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/unit-tests/test_maximum_hashpower.cc: -------------------------------------------------------------------------------- 1 | #include "test_maximum_hashpower.h" 2 | 3 | #define TEST_NO_MAIN 4 | #include "acutest.h" 5 | 6 | #include 7 | 8 | #include "unit_test_util.hh" 9 | #include 10 | #include 11 | 12 | void test_maximum_hashpower_initialized_to_default() { 13 | IntIntTable tbl; 14 | TEST_CHECK(tbl.maximum_hashpower() == libcuckoo::NO_MAXIMUM_HASHPOWER); 15 | } 16 | 17 | void test_maximum_hashpower_caps_any_expansion() { 18 | IntIntTable tbl(1); 19 | tbl.maximum_hashpower(1); 20 | for (size_t i = 0; i < 2 * tbl.slot_per_bucket(); ++i) { 21 | tbl.insert(i, i); 22 | } 23 | 24 | TEST_CHECK(tbl.hashpower() == 1); 25 | TEST_EXCEPTION(tbl.insert(2 * tbl.slot_per_bucket(), 0), 26 | libcuckoo::maximum_hashpower_exceeded); 27 | TEST_EXCEPTION(tbl.rehash(2), libcuckoo::maximum_hashpower_exceeded); 28 | TEST_EXCEPTION(tbl.reserve(4 * tbl.slot_per_bucket()), 29 | libcuckoo::maximum_hashpower_exceeded); 30 | } 31 | 32 | void test_maximum_hash_power_none() { 33 | // It's difficult to check that we actually don't ever set a maximum hash 34 | // power, but if we explicitly unset it, we should be able to expand beyond 35 | // the limit that we had previously set. 36 | IntIntTable tbl(1); 37 | tbl.maximum_hashpower(1); 38 | TEST_EXCEPTION(tbl.rehash(2), libcuckoo::maximum_hashpower_exceeded); 39 | 40 | tbl.maximum_hashpower(2); 41 | tbl.rehash(2); 42 | TEST_CHECK(tbl.hashpower() == 2); 43 | TEST_EXCEPTION(tbl.rehash(3), libcuckoo::maximum_hashpower_exceeded); 44 | 45 | tbl.maximum_hashpower(libcuckoo::NO_MAXIMUM_HASHPOWER); 46 | tbl.rehash(10); 47 | TEST_CHECK(tbl.hashpower() == 10); 48 | } 49 | -------------------------------------------------------------------------------- /tests/universal-benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(universal_benchmark universal_benchmark.cc) 2 | 3 | # put these in the cache so they show up in ccmake 4 | set (UNIVERSAL_KEY uint64_t CACHE STRING "set the key type used by the universal benchmark") 5 | set (UNIVERSAL_VALUE uint64_t CACHE STRING "set the value type used by the universal benchmark") 6 | set (UNIVERSAL_TABLE LIBCUCKOO CACHE STRING "set the table type used by the universal benchmark") 7 | option (UNIVERSAL_TRACKING_ALLOCATOR "if on, also sample memory usage in the universal benchmark") 8 | 9 | target_link_libraries(universal_benchmark 10 | PRIVATE test_util 11 | PRIVATE libcuckoo 12 | PRIVATE pcg 13 | ) 14 | 15 | target_compile_options(universal_benchmark 16 | PRIVATE -DKEY=${UNIVERSAL_KEY} 17 | PRIVATE -DVALUE=${UNIVERSAL_VALUE} 18 | PRIVATE -D${UNIVERSAL_TABLE} 19 | ) 20 | 21 | if(UNIVERSAL_TRACKING_ALLOCATOR) 22 | target_compile_options(universal_benchmark PRIVATE -DTRACKING_ALLOCATOR) 23 | endif() 24 | 25 | add_test(NAME pure_read 26 | COMMAND universal_benchmark --reads 100 --prefill 75 --total-ops 500 --initial-capacity 23) 27 | add_test(NAME pure_insert 28 | COMMAND universal_benchmark --inserts 100 --total-ops 75 --initial-capacity 23) 29 | add_test(NAME pure_erase 30 | COMMAND universal_benchmark --erases 100 --prefill 75 --total-ops 75 --initial-capacity 23) 31 | add_test(NAME pure_update 32 | COMMAND universal_benchmark --updates 100 --prefill 75 --total-ops 500 --initial-capacity 23) 33 | add_test(NAME pure_upsert 34 | COMMAND universal_benchmark --upserts 100 --prefill 25 --total-ops 200 --initial-capacity 23) 35 | 36 | add_test(NAME insert_expansion 37 | COMMAND universal_benchmark --inserts 100 --initial-capacity 4 --total-ops 13107200) 38 | add_test(NAME read_insert_expansion 39 | COMMAND universal_benchmark --reads 80 --inserts 20 --initial-capacity 10 --total-ops 4096000) 40 | -------------------------------------------------------------------------------- /libcuckoo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # for write_basic_package_version_file() 2 | include(CMakePackageConfigHelpers) 3 | 4 | # we require the use of threads 5 | set(THREADS_PREFER_PTHREAD_FLAG ON) 6 | find_package(Threads REQUIRED) 7 | 8 | # generate a version for cmake to use with find_package(libcuckoo) 9 | set (libcuckoo_VERSION "${libcuckoo_VERSION_MAJOR}.${libcuckoo_VERSION_MINOR}") 10 | set (libcuckoo_VERSION "${libcuckoo_VERSION}.${libcuckoo_VERSION_PATCH}") 11 | 12 | # libcuckoo is an interface (all headers) library target 13 | add_library(libcuckoo INTERFACE) 14 | add_library(libcuckoo::libcuckoo ALIAS libcuckoo) 15 | 16 | # tag libcuckoo target with a c++11 feature so that libcuckoo users 17 | # will have c++11 turned on in their compile when they use this target. 18 | # XXX: newer cmakes have a "cxx_std_11" feature that could be used 19 | target_compile_features (libcuckoo INTERFACE cxx_constexpr) 20 | 21 | # Include relative to the base directory 22 | target_include_directories(libcuckoo INTERFACE 23 | $ 24 | $ 25 | ) 26 | 27 | # switch on threading for all targets that link with libcuckoo 28 | target_link_libraries(libcuckoo INTERFACE Threads::Threads) 29 | 30 | # cmake packaging 31 | set (libcuckoo_pkgloc "share/cmake/libcuckoo") 32 | 33 | write_basic_package_version_file( 34 | "libcuckoo-config-version.cmake" VERSION ${libcuckoo_VERSION} 35 | COMPATIBILITY AnyNewerVersion) 36 | 37 | install(TARGETS libcuckoo EXPORT libcuckoo-targets) 38 | install(EXPORT libcuckoo-targets 39 | NAMESPACE libcuckoo:: 40 | DESTINATION ${libcuckoo_pkgloc} 41 | FILE "libcuckoo-targets.cmake") 42 | install(FILES libcuckoo-config.cmake 43 | ${CMAKE_CURRENT_BINARY_DIR}/libcuckoo-config-version.cmake 44 | DESTINATION ${libcuckoo_pkgloc}) 45 | install( 46 | FILES 47 | cuckoohash_config.hh 48 | cuckoohash_map.hh 49 | cuckoohash_util.hh 50 | bucket_container.hh 51 | DESTINATION 52 | ${CMAKE_INSTALL_PREFIX}/include/libcuckoo 53 | ) 54 | -------------------------------------------------------------------------------- /examples/count_freq.cc: -------------------------------------------------------------------------------- 1 | /* A simple example of using the hash table that counts the 2 | * frequencies of a sequence of random numbers. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | typedef uint32_t KeyType; 17 | typedef libcuckoo::cuckoohash_map Table; 18 | const size_t thread_num = 8; 19 | const size_t total_inserts = 10000000; 20 | 21 | void do_inserts(Table &freq_map) { 22 | std::mt19937_64 gen( 23 | std::chrono::system_clock::now().time_since_epoch().count()); 24 | std::uniform_int_distribution dist( 25 | std::numeric_limits::min(), std::numeric_limits::max()); 26 | auto updatefn = [](size_t &num) { ++num; }; 27 | for (size_t i = 0; i < total_inserts / thread_num; i++) { 28 | KeyType num = dist(gen); 29 | // If the number is already in the table, it will increment 30 | // its count by one. Otherwise it will insert a new entry in 31 | // the table with count one. 32 | freq_map.upsert(num, updatefn, 1); 33 | } 34 | } 35 | 36 | int main() { 37 | Table freq_map; 38 | freq_map.reserve(total_inserts); 39 | // Run the inserts in thread_num threads 40 | std::vector threads; 41 | for (size_t i = 0; i < thread_num; i++) { 42 | threads.emplace_back(do_inserts, std::ref(freq_map)); 43 | } 44 | for (size_t i = 0; i < thread_num; i++) { 45 | threads[i].join(); 46 | } 47 | 48 | // We iterate through the table and print out the element with the 49 | // maximum number of occurrences. 50 | KeyType maxkey; 51 | size_t maxval = 0; 52 | { 53 | auto lt = freq_map.lock_table(); 54 | for (const auto &it : lt) { 55 | if (it.second > maxval) { 56 | maxkey = it.first; 57 | maxval = it.second; 58 | } 59 | } 60 | } 61 | 62 | std::cout << maxkey << " occurred " << maxval << " times." << std::endl; 63 | 64 | // Print some information about the table 65 | std::cout << "Table size: " << freq_map.size() << std::endl; 66 | std::cout << "Bucket count: " << freq_map.bucket_count() << std::endl; 67 | std::cout << "Load factor: " << freq_map.load_factor() << std::endl; 68 | } 69 | -------------------------------------------------------------------------------- /tests/unit-tests/test_hash_properties.cc: -------------------------------------------------------------------------------- 1 | #include "test_hash_properties.h" 2 | 3 | #define TEST_NO_MAIN 4 | #include "acutest.h" 5 | 6 | #include "unit_test_util.hh" 7 | #include 8 | 9 | using libcuckoo::UnitTestInternalAccess; 10 | 11 | // Checks that the alt index function returns a different bucket, and can 12 | // recover the old bucket when called with the alternate bucket as the index. 13 | template 14 | void check_key(size_t hashpower, const typename CuckoohashMap::key_type &key) { 15 | auto hashfn = typename CuckoohashMap::hasher(); 16 | size_t hv = hashfn(key); 17 | auto partial = UnitTestInternalAccess::partial_key(hv); 18 | size_t bucket = 19 | UnitTestInternalAccess::index_hash(hashpower, hv); 20 | size_t alt_bucket = UnitTestInternalAccess::alt_index( 21 | hashpower, partial, bucket); 22 | size_t orig_bucket = UnitTestInternalAccess::alt_index( 23 | hashpower, partial, alt_bucket); 24 | 25 | TEST_CHECK(bucket != alt_bucket); 26 | TEST_CHECK(bucket == orig_bucket); 27 | } 28 | 29 | void test_hash_properties_int_alt_index_works_correctly() { 30 | for (size_t hashpower = 10; hashpower < 15; ++hashpower) { 31 | for (int key = 0; key < 10000; ++key) { 32 | check_key(hashpower, key); 33 | } 34 | } 35 | } 36 | 37 | void test_hash_properties_string_alt_index_works_correctly() { 38 | for (size_t hashpower = 10; hashpower < 15; ++hashpower) { 39 | for (int key = 0; key < 10000; ++key) { 40 | check_key(hashpower, std::to_string(key)); 41 | } 42 | } 43 | } 44 | 45 | void test_hash_properties_hash_with_larger_hashpower_only_adds_top_bits() { 46 | std::string key = "abc"; 47 | size_t hv = StringIntTable::hasher()(key); 48 | for (size_t hashpower = 1; hashpower < 30; ++hashpower) { 49 | auto partial = UnitTestInternalAccess::partial_key(hv); 50 | size_t index_bucket1 = 51 | UnitTestInternalAccess::index_hash(hashpower, hv); 52 | size_t index_bucket2 = 53 | UnitTestInternalAccess::index_hash(hashpower + 1, hv); 54 | TEST_CHECK((index_bucket2 & ~(1L << hashpower)) == index_bucket1); 55 | 56 | size_t alt_bucket1 = UnitTestInternalAccess::alt_index( 57 | hashpower, partial, index_bucket1); 58 | size_t alt_bucket2 = UnitTestInternalAccess::alt_index( 59 | hashpower, partial, index_bucket2); 60 | 61 | TEST_CHECK((alt_bucket2 & ~(1L << hashpower)) == alt_bucket1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: false 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Attach 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | BreakAfterJavaFieldAnnotations: false 40 | BreakStringLiterals: true 41 | ColumnLimit: 80 42 | CommentPragmas: '^ IWYU pragma:' 43 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 44 | ConstructorInitializerIndentWidth: 4 45 | ContinuationIndentWidth: 4 46 | Cpp11BracedListStyle: true 47 | DerivePointerAlignment: false 48 | DisableFormat: false 49 | ExperimentalAutoDetectBinPacking: false 50 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 51 | IncludeCategories: 52 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 53 | Priority: 2 54 | - Regex: '^(<|"(gtest|isl|json)/)' 55 | Priority: 3 56 | - Regex: '.*' 57 | Priority: 1 58 | IncludeIsMainRegex: '$' 59 | IndentCaseLabels: false 60 | IndentWidth: 2 61 | IndentWrappedFunctionNames: false 62 | JavaScriptQuotes: Leave 63 | JavaScriptWrapImports: true 64 | KeepEmptyLinesAtTheStartOfBlocks: true 65 | MacroBlockBegin: '' 66 | MacroBlockEnd: '' 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | ObjCBlockIndentWidth: 2 70 | ObjCSpaceAfterProperty: false 71 | ObjCSpaceBeforeProtocolList: true 72 | PenaltyBreakBeforeFirstCallParameter: 19 73 | PenaltyBreakComment: 300 74 | PenaltyBreakFirstLessLess: 120 75 | PenaltyBreakString: 1000 76 | PenaltyExcessCharacter: 1000000 77 | PenaltyReturnTypeOnItsOwnLine: 60 78 | PointerAlignment: Right 79 | ReflowComments: true 80 | SortIncludes: true 81 | SpaceAfterCStyleCast: false 82 | SpaceBeforeAssignmentOperators: true 83 | SpaceBeforeParens: ControlStatements 84 | SpaceInEmptyParentheses: false 85 | SpacesBeforeTrailingComments: 1 86 | SpacesInAngles: false 87 | SpacesInContainerLiterals: true 88 | SpacesInCStyleCastParentheses: false 89 | SpacesInParentheses: false 90 | SpacesInSquareBrackets: false 91 | Standard: Cpp11 92 | TabWidth: 8 93 | UseTab: Never 94 | ... 95 | 96 | -------------------------------------------------------------------------------- /tests/universal-benchmark/universal_gen.hh: -------------------------------------------------------------------------------- 1 | #ifndef _UNIVERSAL_GEN_HH 2 | #define _UNIVERSAL_GEN_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* A specialized functor for generating unique keys and values for various 10 | * types. Must define one for each type we want to use. These keys and values 11 | * are meant to be copied into the table (not moved). */ 12 | 13 | template class Gen { 14 | // using storage_type = ... 15 | // static storage_type storage_key(uint64_t num) 16 | // static storage_type storage_value() 17 | // static T get(storage_type&) 18 | // static constexpr size_t key_size 19 | // static constexpr size_t value_size 20 | }; 21 | 22 | template <> class Gen { 23 | public: 24 | using storage_type = uint64_t; 25 | 26 | static storage_type storage_key(uint64_t num) { return num; } 27 | 28 | static storage_type storage_value() { return 0; } 29 | 30 | static uint64_t get(const storage_type &st) { return st; } 31 | 32 | static constexpr size_t key_size = sizeof(uint64_t); 33 | static constexpr size_t value_size = sizeof(uint64_t); 34 | }; 35 | 36 | template <> class Gen { 37 | static constexpr size_t STRING_SIZE = 100; 38 | 39 | public: 40 | using storage_type = std::string; 41 | 42 | static storage_type storage_key(uint64_t num) { 43 | return std::string( 44 | static_cast(static_cast(&num)), 45 | sizeof(num)); 46 | } 47 | 48 | static storage_type storage_value() { return std::string(STRING_SIZE, '0'); } 49 | 50 | static std::string get(const storage_type &st) { return st; } 51 | 52 | static constexpr size_t key_size = sizeof(uint64_t); 53 | static constexpr size_t value_size = 100; 54 | }; 55 | 56 | // Should be 256B. Bitset is nice since it already has std::hash specialized. 57 | using MediumBlob = std::bitset<2048>; 58 | 59 | template <> class Gen { 60 | public: 61 | using storage_type = MediumBlob; 62 | 63 | static storage_type storage_key(uint64_t num) { 64 | return MediumBlob(Gen::storage_key(num)); 65 | } 66 | 67 | static storage_type storage_value() { return MediumBlob(); } 68 | 69 | static MediumBlob get(const storage_type &st) { return st; } 70 | 71 | static constexpr size_t key_size = sizeof(MediumBlob); 72 | static constexpr size_t value_size = sizeof(MediumBlob); 73 | }; 74 | 75 | // Should be 512B 76 | using BigBlob = std::bitset<4096>; 77 | 78 | template <> class Gen { 79 | public: 80 | using storage_type = BigBlob; 81 | 82 | static storage_type storage_key(uint64_t num) { 83 | return BigBlob(Gen::storage_key(num)); 84 | } 85 | 86 | static storage_type storage_value() { return BigBlob(); } 87 | 88 | static BigBlob get(const storage_type &st) { return st; } 89 | 90 | static constexpr size_t key_size = sizeof(BigBlob); 91 | static constexpr size_t value_size = sizeof(BigBlob); 92 | }; 93 | 94 | template class Gen { 95 | public: 96 | using storage_type = std::unique_ptr; 97 | 98 | static storage_type storage_key(uint64_t num) { 99 | return storage_type(new T(Gen::storage_key(num))); 100 | } 101 | 102 | static storage_type storage_value() { 103 | return storage_type(new T(Gen::storage_value())); 104 | } 105 | 106 | static T *get(const storage_type &st) { return st.get(); } 107 | 108 | static constexpr size_t key_size = Gen::key_size; 109 | static constexpr size_t value_size = Gen::value_size; 110 | }; 111 | 112 | #endif // _UNIVERSAL_GEN_HH 113 | -------------------------------------------------------------------------------- /tests/universal-benchmark/README.md: -------------------------------------------------------------------------------- 1 | # Universal Benchmark 2 | 3 | This walkthrough explains how the `universal_benchmark` executable operates and 4 | what each flag means. 5 | 6 | ## Step-by-Step Operation 7 | 8 | 1. Generate the mixture of operations you will be running, which consists of a 9 | certain percentage of reads, inserts, erases, updates, and upserts. We 10 | pre-generate the mixture to avoid having to compute which operation to run 11 | while timing the workload 12 | 2. Pre-generate all keys we will be inserting into the table. We can calculate 13 | an upper bound on the number of keys being inserted based on the prefill 14 | percentage (`--prefill`) and the number of inserts and upserts we’ll be 15 | performing. Again, pre-generating the keys avoids doing it while timing the 16 | workload. We don’t need to pre-generate the values, since the values are the 17 | same for all operations 18 | 3. Initialize the table, pre-sized to a specified capacity (`--initial-capacity`) 19 | 4. Fill up the table in advance to the specified prefill percentage 20 | 5. Run the pre-generated mixture of operations (`--total-ops`) and time how long 21 | it takes to complete all of them 22 | 6. Report the details of the benchmark configuration and the quantities 23 | measured, including time elapsed, throughput, and (optionally) memory usage 24 | samples. 25 | 26 | ## Flags 27 | 28 | These flags are passed to CMake and set various compile-time parameters for the 29 | benchmark: 30 | 31 | `-DUNIVERSAL_KEY` 32 | : sets the type of the table Key (by default, this is `uint64_t`). Support for 33 | new keys can be added in `universal_gen.hh` 34 | 35 | `-DUNIVERSAL_VALUE` 36 | : sets the type of the table Value. Support for new values can be added in 37 | `universal_gen.hh` 38 | 39 | `-DUNIVERSAL_TABLE` 40 | : sets the type of the hashmap being benchmarked (by default, this is 41 | `LIBCUCKOO`). Support for new maps can be added in 42 | `universal_table_wrapper.hh` 43 | 44 | `-DUNIVERSAL_TRACKING_ALLOCATOR` 45 | : enables memory usage sampling 46 | 47 | These flags control the mixture of operations that will be run in the 48 | benchmark. They are interpreted as whole number percentages, and must sum to 49 | 100: 50 | 51 | `--reads` 52 | : percentage of operations that are reads 53 | 54 | `--inserts` 55 | : percentage of operations that are inserts 56 | 57 | `--erases` 58 | : percentage of operations that are erases 59 | 60 | `--updates` 61 | : percentage of operations that are updates 62 | 63 | `--upserts` 64 | : percentage of operations that are upserts 65 | 66 | These flags control some parameters about what the table will look like before 67 | the operation mixture is run: 68 | 69 | `--initial-capacity` 70 | : sets the initial number of elements the table is pre-sized 71 | to hold, as a power of 2. So if you pass 25 as the value of this flag, the 72 | table will be pre-sized to hold 2^25 elements 73 | 74 | `-–prefill` 75 | : sets the percentage to fill the pre-sized table to before running 76 | the operations. 77 | 78 | These flags control a few other details of the benchmark: 79 | 80 | `--total-ops` 81 | : the total number of operations to run (in the timed phase), as a percentage 82 | of the initial table capacity. So specifying `--initial-capacity 25 --total-ops 83 | 90` means run `90%` of `2^25`, or about `30198988`, operations. Specifying it 84 | as a percentage makes it easy to specify the final table capacity without 85 | having to do too much calculation or write a large number. 86 | 87 | `--num-threads` 88 | : the number of threads to use in all phases of the benchmark 89 | 90 | `--seed` 91 | : the seed to use for the rng, or 0 if you want to use a randomly 92 | generated seed. If `--num-threads` is 1 and you specify a specific seed, the test 93 | should be repeatable. 94 | -------------------------------------------------------------------------------- /examples/c_hash.c: -------------------------------------------------------------------------------- 1 | // This is a C program which uses some basic features of the C wrapper around 2 | // libcuckoo. The C wrapper includes nearly all the features of the C++ 3 | // version, and most of the C functions have direct equivalents to C++ methods. 4 | #include 5 | #include 6 | #include 7 | 8 | // Includes the interface for the int->string table 9 | #include "int_str_table.h" 10 | // Includes the interface for the key_blob->mapped_blob table. Since both 11 | // interface files define the same three macros, we must first undefine them 12 | // before including the next interface. This undefining will have no effect on 13 | // the names generated by the interface templates. 14 | #undef CUCKOO_TABLE_NAME 15 | #undef CUCKOO_KEY_TYPE 16 | #undef CUCKOO_MAPPED_TYPE 17 | #include "blob_blob_table.h" 18 | 19 | int main() { 20 | // Allocate a new int->str table, which must be freed at the end of the 21 | // program 22 | int_str_table *tbl = int_str_table_init(0); 23 | // Since the insert function only accepts a pointer to the key and mapped 24 | // values, we must make sure they exist as lvalues 25 | const char *insert_item = "hello"; 26 | for (int i = 0; i < 10; i++) { 27 | int_str_table_insert(tbl, &i, &insert_item); 28 | } 29 | 30 | // This loop will search for keys 1-10 (of which 10 will not be found), and 31 | // print out what it finds 32 | printf("int -> str table:\n"); 33 | for (int i = 0; i < 11; i++) { 34 | // We need to store the found value in some variable 35 | const char *find_item; 36 | // The find function will return true if the key was found, and false 37 | // otherwise 38 | if (int_str_table_find(tbl, &i, &find_item)) { 39 | printf("%d %s\n", i, find_item); 40 | } else { 41 | printf("%d NOT FOUND\n", i); 42 | } 43 | } 44 | 45 | // Allocate a new blob->blob table, which must be freed at the end of the 46 | // program 47 | blob_blob_table *tbl2 = blob_blob_table_init(0); 48 | // The table functions as a fully-functional single-threaded hashtable in 49 | // locked_table mode, so we lock the table now in order to showcase some of 50 | // that functionality. 51 | blob_blob_table_locked_table *ltbl = blob_blob_table_lock_table(tbl2); 52 | key_blob k; 53 | mapped_blob v; 54 | for (int i = 0; i < 10; ++i) { 55 | // The key we store is a binary representation of the integer 56 | memcpy(k.blob, &i, sizeof(i)); 57 | // The mapped value we store is the integer converted to ascii characters 58 | int num = i; 59 | int j = 0; 60 | if (num == 0) { 61 | v.blob[j++] = '0'; 62 | } else { 63 | while (num > 0) { 64 | v.blob[j++] = (num % 10) + '0'; 65 | num /= 10; 66 | } 67 | } 68 | v.blob[j] = '\0'; 69 | // To insert into a locked_table, we pass the table, address of key and 70 | // value, and, optionally, an iterator, which will be set to the location 71 | // of the inserted key-value pair. We pass NULL here, indicating we don't 72 | // have an iterator to set to the insert location. 73 | blob_blob_table_locked_table_insert(ltbl, &k, &v, NULL); 74 | } 75 | 76 | // We must allocate iterator objects for the beginning and end of the table 77 | // to iterate through it. Since we are not modifying the elements of the 78 | // table, we use const_iterators, though regular iterators would also work 79 | // here. 80 | blob_blob_table_const_iterator *it = 81 | blob_blob_table_locked_table_cbegin(ltbl); 82 | blob_blob_table_const_iterator *end = blob_blob_table_locked_table_cend(ltbl); 83 | 84 | // This loop walks through the table and prints out each key-value pair. 85 | // Since `it` is already set to the beginning, there is no need for an 86 | // initialization statement. The condition tests whether `it` equals `end`, 87 | // which, when it does, will mean we've moved past the end of the table. The 88 | // step statement advances `it` forward to the next key-value pair. 89 | printf("blob -> blob table:\n"); 90 | for (; !blob_blob_table_const_iterator_equal(it, end); 91 | blob_blob_table_const_iterator_increment(it)) { 92 | // Here we dereference the key and mapped values from the iterator and 93 | // store them into our previously-declared key_blob and mapped_blob 94 | // variables. 95 | k = *blob_blob_table_const_iterator_key(it); 96 | v = *blob_blob_table_const_iterator_mapped(it); 97 | printf("%d -> %s\n", *(int *)k.blob, v.blob); 98 | } 99 | 100 | // We must free all the allocated objects, which are the iterators, locked 101 | // table, and two table objects. 102 | blob_blob_table_const_iterator_free(end); 103 | blob_blob_table_const_iterator_free(it); 104 | blob_blob_table_locked_table_free(ltbl); 105 | blob_blob_table_free(tbl2); 106 | int_str_table_free(tbl); 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libcuckoo 2 | ========= 3 | 4 | libcuckoo provides a high-performance, compact hash table that allows 5 | multiple concurrent reader and writer threads. 6 | 7 | The Doxygen-generated documentation is available at the 8 | [project page](http://efficient.github.io/libcuckoo/). 9 | 10 | Authors: Manu Goyal, Bin Fan, Xiaozhou Li, David G. Andersen, and Michael Kaminsky 11 | 12 | For details about this algorithm and citations, please refer to 13 | our papers in [NSDI 2013][1] and [EuroSys 2014][2]. Some of the details of the hashing 14 | algorithm have been improved since that work (e.g., the previous algorithm 15 | in [1] serializes all writer threads, while our current 16 | implementation supports multiple concurrent writers), however, and this source 17 | code is now the definitive reference. 18 | 19 | [1]: http://www.cs.cmu.edu/~dga/papers/memc3-nsdi2013.pdf "MemC3: Compact and Concurrent Memcache with Dumber Caching and Smarter Hashing" 20 | [2]: http://www.cs.princeton.edu/~mfreed/docs/cuckoo-eurosys14.pdf "Algorithmic Improvements for Fast Concurrent Cuckoo Hashing" 21 | 22 | Requirements 23 | ================ 24 | 25 | This library has been tested on Mac OSX >= 10.8 and Ubuntu >= 12.04. 26 | 27 | It compiles with clang++ >= 3.3 and g++ >= 4.8, however we strongly suggest 28 | using the latest versions of both compilers, as they have greatly improved 29 | support for atomic operations. Building the library requires CMake version >= 30 | 3.1.0. To install it on Ubuntu 31 | 32 | $ sudo apt-get update && sudo apt-get install cmake 33 | 34 | Building 35 | ========== 36 | 37 | libcuckoo is a header-only library, so in order to get going, just add the 38 | `libcuckoo` subdirectory to your include path. These directions cover 39 | installing the library to a particular location on your system, and also 40 | building any the examples and tests included in the repository. 41 | 42 | We suggest you build out of source, in a separate `build` directory: 43 | 44 | $ mkdir build 45 | $ cd build 46 | 47 | There are numerous flags you can pass to `CMake` to set which parts of the 48 | repository it builds. 49 | 50 | `-DCMAKE_INSTALL_PREFIX` 51 | : set the location where the libcuckoo header files are installed 52 | 53 | `-DCMAKE_BUILD_TYPE` 54 | : enable different types of build flags for different purposes 55 | 56 | `-DBUILD_EXAMPLES=1` 57 | : tell `CMake` to build the `examples` directory 58 | 59 | `-DBUILD_TESTS=1` 60 | : build all tests in the `tests` directory 61 | 62 | `-DBUILD_STRESS_TESTS=1` 63 | : build all tests in the `tests/stress-tests` directory 64 | 65 | `-DBUILD_UNIT_TESTS=1` 66 | : build all tests in the `tests/unit-tests` directory 67 | 68 | `-DBUILD_UNIVERSAL_BENCHMARK=1` 69 | : build the universal benchmark in the `tests/universal-benchmark` directory. 70 | This benchmark allows you to test a variety of operations in arbitrary 71 | percentages, with specific keys and values. Consult the `README` in the 72 | benchmark directory for more details. 73 | 74 | So, if, for example, we want to build all examples and all tests into a local 75 | installation directory, we'd run the following command from the `build` 76 | directory. 77 | 78 | $ cmake -DCMAKE_INSTALL_PREFIX=../install -DBUILD_EXAMPLES=1 -DBUILD_TESTS=1 .. 79 | $ make all 80 | $ make install 81 | 82 | Usage 83 | ========== 84 | 85 | When compiling your own files with `libcuckoo`, always remember to enable C++11 86 | features on your compiler. On `g++`, this would be `-std=c++11`, and on 87 | `clang++`, this would be `-std=c++11 -stdlib=libc++`. 88 | 89 | Once you have installed the header files and the install location has been added 90 | to your search path, you can include ``, and any of 91 | the other headers you installed, into your source file. 92 | 93 | There is also a C wrapper around the table that can be leveraged to use 94 | `libcuckoo` in a C program. The interface consists of a template header and 95 | implementation file that can be used to generate instances of the hashtable for 96 | different key-value types. 97 | 98 | See the `examples` directory for a demonstration of all of these features. 99 | 100 | Tests 101 | ========== 102 | 103 | The `tests` directory contains a number of tests and benchmarks of the hash 104 | table, which also can serve as useful examples of how to use the table's various 105 | features. Make sure to enable the tests you want to build with the corresponding 106 | `CMake` flags. The test suite can be run with the `make test` command. The test 107 | executables can be run individually as well. 108 | 109 | Issue Report 110 | ============ 111 | 112 | To let us know your questions or issues, we recommend you 113 | [report an issue](https://github.com/efficient/libcuckoo/issues) on 114 | github. You can also email us at 115 | [libcuckoo-dev@googlegroups.com](mailto:libcuckoo-dev@googlegroups.com). 116 | 117 | Licence 118 | =========== 119 | Copyright (C) 2013, Carnegie Mellon University and Intel Corporation 120 | 121 | Licensed under the Apache License, Version 2.0 (the "License"); 122 | you may not use this file except in compliance with the License. 123 | You may obtain a copy of the License at 124 | 125 | http://www.apache.org/licenses/LICENSE-2.0 126 | 127 | Unless required by applicable law or agreed to in writing, software 128 | distributed under the License is distributed on an "AS IS" BASIS, 129 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130 | See the License for the specific language governing permissions and 131 | limitations under the License. 132 | 133 | --------------------------- 134 | 135 | The third-party libraries have their own licenses, as detailed in their source 136 | files. 137 | -------------------------------------------------------------------------------- /tests/unit-tests/test_resize.cc: -------------------------------------------------------------------------------- 1 | #include "test_resize.h" 2 | 3 | #define TEST_NO_MAIN 4 | #include "acutest.h" 5 | 6 | #include 7 | #include 8 | 9 | #include "unit_test_util.hh" 10 | #include 11 | 12 | using libcuckoo::UnitTestInternalAccess; 13 | 14 | void test_resize_rehash_empty_table() { 15 | IntIntTable table(1); 16 | TEST_CHECK(table.hashpower() == 0); 17 | 18 | table.rehash(20); 19 | TEST_CHECK(table.hashpower() == 20); 20 | 21 | table.rehash(1); 22 | TEST_CHECK(table.hashpower() == 1); 23 | } 24 | 25 | void test_resize_reserve_empty_table() { 26 | IntIntTable table(1); 27 | table.reserve(100); 28 | TEST_CHECK(table.hashpower() == 5); 29 | 30 | table.reserve(1); 31 | TEST_CHECK(table.hashpower() == 0); 32 | 33 | table.reserve(2); 34 | TEST_CHECK(table.hashpower() == 0); 35 | } 36 | 37 | void test_resize_reserve_calc() { 38 | const size_t slot_per_bucket = IntIntTable::slot_per_bucket(); 39 | TEST_CHECK(UnitTestInternalAccess::reserve_calc(0) == 0); 40 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 41 | 1 * slot_per_bucket) == 0); 42 | 43 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 44 | 2 * slot_per_bucket) == 1); 45 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 46 | 3 * slot_per_bucket) == 2); 47 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 48 | 4 * slot_per_bucket) == 2); 49 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 50 | 2500000 * slot_per_bucket) == 22); 51 | 52 | // The maximum number of elements we can ask to reserve without incurring 53 | // rounding error when computing a number of buckets is 54 | // SIZE_T_MAX-slot_per_bucket(), which will come out to int_div(SIZE_T_MAX - 55 | // 1, slot_per_bucket()) buckets. 56 | const size_t max_buckets = 57 | (std::numeric_limits::max() - 1) / slot_per_bucket; 58 | // Since the table is always sized in powers of two, our maximum hashpower 59 | // comes out to max_hashpower = floor(log2(max_buckets)). We compute this in 60 | // a numerically-stable fashion. 61 | size_t max_hashpower = 0; 62 | for (; (static_cast(1) << (max_hashpower + 1)) <= max_buckets; 63 | ++max_hashpower) 64 | ; 65 | // Test the boundary between max_hashpower-1 and max_hashpower. 66 | const size_t max_elems_before_max_hashpower = 67 | (static_cast(1) << (max_hashpower - 1)) * slot_per_bucket; 68 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 69 | max_elems_before_max_hashpower) == (max_hashpower - 1)); 70 | TEST_CHECK(UnitTestInternalAccess::reserve_calc( 71 | max_elems_before_max_hashpower + 1) == max_hashpower); 72 | // Test the maximum number of elements. 73 | const size_t max_elems = 74 | (static_cast(1) << max_hashpower) * slot_per_bucket; 75 | TEST_CHECK(UnitTestInternalAccess::reserve_calc(max_elems) == 76 | max_hashpower); 77 | } 78 | 79 | struct my_type { 80 | int x; 81 | my_type(int v) : x(v) {} 82 | my_type(const my_type &other) { x = other.x; } 83 | my_type(my_type &&other) { 84 | x = other.x; 85 | ++num_moves; 86 | } 87 | ~my_type() { ++num_deletes; } 88 | static size_t num_deletes; 89 | static size_t num_moves; 90 | }; 91 | 92 | size_t my_type::num_deletes = 0; 93 | size_t my_type::num_moves = 0; 94 | 95 | void test_resize_number_of_frees() { 96 | my_type val(0); 97 | size_t num_deletes_after_resize; 98 | { 99 | // Should allocate 2 buckets of 4 slots 100 | libcuckoo::cuckoohash_map, std::equal_to, 101 | std::allocator>, 4> 102 | map(8); 103 | for (int i = 0; i < 9; ++i) { 104 | map.insert(i, val); 105 | } 106 | // All of the items should be moved during resize to the new region of 107 | // memory. They should be deleted from the old container. 108 | TEST_CHECK(my_type::num_deletes == 8); 109 | TEST_CHECK(my_type::num_moves == 8); 110 | } 111 | TEST_CHECK(my_type::num_deletes == 17); 112 | } 113 | 114 | // Taken from https://github.com/facebook/folly/blob/master/folly/docs/Traits.md 115 | class NonRelocatableType { 116 | public: 117 | std::array buffer; 118 | char *pointerToBuffer; 119 | NonRelocatableType() : pointerToBuffer(buffer.data()) {} 120 | NonRelocatableType(char c) : pointerToBuffer(buffer.data()) { 121 | buffer.fill(c); 122 | } 123 | 124 | NonRelocatableType(const NonRelocatableType &x) noexcept 125 | : buffer(x.buffer), pointerToBuffer(buffer.data()) {} 126 | 127 | NonRelocatableType &operator=(const NonRelocatableType &x) { 128 | buffer = x.buffer; 129 | return *this; 130 | } 131 | }; 132 | 133 | void test_resize_on_non_relocatable_type() { 134 | libcuckoo::cuckoohash_map< 135 | int, NonRelocatableType, std::hash, std::equal_to, 136 | std::allocator>, 1> 137 | map(0); 138 | TEST_CHECK(map.hashpower() == 0); 139 | // Make it resize a few times to ensure the vector capacity has to actually 140 | // change when we resize the buckets 141 | const size_t num_elems = 16; 142 | for (int i = 0; i < num_elems; ++i) { 143 | map.insert(i, 'a'); 144 | } 145 | // Make sure each pointer actually points to its buffer 146 | NonRelocatableType value; 147 | std::array ref; 148 | ref.fill('a'); 149 | auto lt = map.lock_table(); 150 | for (const auto &kvpair : lt) { 151 | TEST_CHECK(ref == kvpair.second.buffer); 152 | TEST_CHECK(kvpair.second.pointerToBuffer == kvpair.second.buffer.data()); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tests/unit-tests/unit_test_util.hh: -------------------------------------------------------------------------------- 1 | // Utilities for unit testing 2 | #ifndef UNIT_TEST_UTIL_HH_ 3 | #define UNIT_TEST_UTIL_HH_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | // Returns a statically allocated value used to keep track of how many unfreed 17 | // bytes have been allocated. This value is shared across all threads. 18 | std::atomic &get_unfreed_bytes(); 19 | 20 | // We define a a allocator class that keeps track of how many unfreed bytes have 21 | // been allocated. Users can specify an optional bound for how many bytes can be 22 | // unfreed, and the allocator will fail if asked to allocate above that bound 23 | // (note that behavior with this bound with concurrent allocations will be hard 24 | // to deal with). A bound below 0 is inactive (the default is -1). 25 | template struct TrackingAllocator { 26 | using value_type = T; 27 | using pointer = T *; 28 | using const_pointer = const T *; 29 | using reference = T &; 30 | using const_reference = const T &; 31 | using size_type = size_t; 32 | using difference_type = ptrdiff_t; 33 | 34 | template struct rebind { 35 | using other = TrackingAllocator; 36 | }; 37 | 38 | TrackingAllocator() {} 39 | template 40 | TrackingAllocator(const TrackingAllocator &) {} 41 | 42 | T *allocate(size_t n) { 43 | const size_t bytes_to_allocate = sizeof(T) * n; 44 | if (BOUND >= 0 && get_unfreed_bytes() + bytes_to_allocate > BOUND) { 45 | throw std::bad_alloc(); 46 | } 47 | get_unfreed_bytes() += bytes_to_allocate; 48 | return std::allocator().allocate(n); 49 | } 50 | 51 | void deallocate(T *p, size_t n) { 52 | get_unfreed_bytes() -= (sizeof(T) * n); 53 | std::allocator().deallocate(p, n); 54 | } 55 | 56 | template void construct(U *p, Args &&... args) { 57 | new ((void *)p) U(std::forward(args)...); 58 | } 59 | 60 | template void destroy(U *p) { p->~U(); } 61 | }; 62 | 63 | template 64 | bool operator==(const TrackingAllocator &a1, 65 | const TrackingAllocator &a2) { 66 | return true; 67 | } 68 | 69 | template 70 | bool operator!=(const TrackingAllocator &a1, 71 | const TrackingAllocator &a2) { 72 | return false; 73 | } 74 | 75 | using IntIntTable = 76 | libcuckoo::cuckoohash_map, std::equal_to, 77 | std::allocator>, 4>; 78 | 79 | template 80 | using IntIntTableWithAlloc = 81 | libcuckoo::cuckoohash_map, std::equal_to, Alloc, 4>; 82 | 83 | using StringIntTable = 84 | libcuckoo::cuckoohash_map, 85 | std::equal_to, 86 | std::allocator>, 4>; 87 | 88 | namespace std { 89 | template struct hash> { 90 | size_t operator()(const unique_ptr &ptr) const { 91 | return std::hash()(*ptr); 92 | } 93 | 94 | size_t operator()(const T *ptr) const { return std::hash()(*ptr); } 95 | }; 96 | 97 | template struct equal_to> { 98 | bool operator()(const unique_ptr &ptr1, const unique_ptr &ptr2) const { 99 | return *ptr1 == *ptr2; 100 | } 101 | 102 | bool operator()(const T *ptr1, const unique_ptr &ptr2) const { 103 | return *ptr1 == *ptr2; 104 | } 105 | 106 | bool operator()(const unique_ptr &ptr1, const T *ptr2) const { 107 | return *ptr1 == *ptr2; 108 | } 109 | }; 110 | } 111 | 112 | template 113 | using UniquePtrTable = libcuckoo::cuckoohash_map< 114 | std::unique_ptr, std::unique_ptr, std::hash>, 115 | std::equal_to>, 116 | std::allocator, std::unique_ptr>>, 4>; 117 | 118 | // Some unit tests need access into certain private data members of the table. 119 | // This class is a friend of the table, so it can access those. 120 | namespace libcuckoo { 121 | 122 | class UnitTestInternalAccess { 123 | public: 124 | friend IntIntTable; 125 | static const size_t IntIntBucketSize = sizeof(IntIntTable::bucket); 126 | 127 | template 128 | static size_t old_table_info_size(const CuckoohashMap &table) { 129 | // This is not thread-safe 130 | return table.old_table_infos.size(); 131 | } 132 | 133 | template 134 | static typename CuckoohashMap::partial_t partial_key(const size_t hv) { 135 | return CuckoohashMap::partial_key(hv); 136 | } 137 | 138 | template 139 | static size_t index_hash(const size_t hashpower, const size_t hv) { 140 | return CuckoohashMap::index_hash(hashpower, hv); 141 | } 142 | 143 | template 144 | static size_t alt_index(const size_t hashpower, 145 | const typename CuckoohashMap::partial_t partial, 146 | const size_t index) { 147 | return CuckoohashMap::alt_index(hashpower, partial, index); 148 | } 149 | 150 | template static size_t reserve_calc(size_t n) { 151 | return CuckoohashMap::reserve_calc(n); 152 | } 153 | 154 | template 155 | static typename CuckoohashMap::locks_t & 156 | get_current_locks(const CuckoohashMap &table) { 157 | return table.get_current_locks(); 158 | } 159 | }; 160 | 161 | } // namespace libcuckoo 162 | 163 | #endif // UNIT_TEST_UTIL_HH_ 164 | -------------------------------------------------------------------------------- /tests/universal-benchmark/universal_table_wrapper.hh: -------------------------------------------------------------------------------- 1 | /* For each table to support, we define a wrapper class which holds the table 2 | * and implements all of the benchmarked operations. Below we list all the 3 | * methods each wrapper must implement. 4 | * 5 | * constructor(size_t n) // n is the initial capacity 6 | * template 7 | * bool read(const K& k, V& v) const 8 | * template 9 | * bool insert(const K& k, const V& v) 10 | * template 11 | * bool erase(const K& k) 12 | * template 13 | * bool update(const K& k, const V& v) 14 | * template 15 | * void upsert(const K& k, Updater fn, const V& v) 16 | */ 17 | 18 | #ifndef _UNIVERSAL_TABLE_WRAPPER_HH 19 | #define _UNIVERSAL_TABLE_WRAPPER_HH 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifndef KEY 26 | #error Must define KEY symbol as valid key type 27 | #endif 28 | 29 | #ifndef VALUE 30 | #error Must define VALUE symbol as valid value type 31 | #endif 32 | 33 | #ifdef TRACKING_ALLOCATOR 34 | std::atomic universal_benchmark_current_bytes_allocated = 35 | ATOMIC_VAR_INIT(0); 36 | 37 | template