From cead76750d9f180ff1a9827009af1bfc1755563c Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Wed, 30 Mar 2022 08:39:02 -0400 Subject: [PATCH 01/10] Initial infrastructure with failing test --- .gitmodules | 3 ++ external/CMakeLists.txt | 5 ++ external/abseil-cpp | 1 + src/CMakeLists.txt | 6 ++- src/flat_hash_map.cpp | 18 +++++++ src/flat_hash_map.h | 38 +++++++++++++ test/riptide_python_test/CMakeLists.txt | 3 +- .../riptide_python_test/hash_linear_tests.cpp | 2 +- test/riptide_test/CMakeLists.txt | 15 +++++- test/riptide_test/flat_hash_map_tests.cpp | 54 +++++++++++++++++++ 10 files changed, 140 insertions(+), 5 deletions(-) create mode 160000 external/abseil-cpp create mode 100644 src/flat_hash_map.cpp create mode 100644 src/flat_hash_map.h create mode 100644 test/riptide_test/flat_hash_map_tests.cpp diff --git a/.gitmodules b/.gitmodules index c2c74ebc..ccb83851 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "external/zstd"] path = external/zstd url = https://github.com/facebook/zstd.git +[submodule "external/abseil-cpp"] + path = external/abseil-cpp + url = https://github.com/abseil/abseil-cpp.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 203aa699..4609a223 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -10,3 +10,8 @@ set(ZSTD_MULTITHREAD_SUPPORT OFF CACHE BOOL "" FORCE) add_subdirectory(zstd/build/cmake) set(EXTERNAL_ZSTD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zstd/lib PARENT_SCOPE) + +# abseil-cpp +set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE) +set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE) +add_subdirectory(abseil-cpp) diff --git a/external/abseil-cpp b/external/abseil-cpp new file mode 160000 index 00000000..0c6302fe --- /dev/null +++ b/external/abseil-cpp @@ -0,0 +1 @@ +Subproject commit 0c6302fe427963ec5c471d3ee660120682ab15f7 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4844d18c..74742bc9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ set(SOURCES DateTime.cpp Ema.cpp #FileReadWrite.cpp + flat_hash_map.cpp GroupBy.cpp HashFunctions.cpp HashLinear.cpp @@ -118,11 +119,14 @@ get_target_property(RT_SOURCE_DIR riptide_cpp SOURCE_DIR) target_include_directories(${TARGET_NAME} PRIVATE ${EXTERNAL_ZSTD_INCLUDE_DIR} ${Python3_INCLUDE_DIRS} - ${Python3_NumPy_INCLUDE_DIRS} ) + ${Python3_NumPy_INCLUDE_DIRS} + ${ABSL_COMMON_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} Python3::Python libzstd_static + absl::base + absl::flat_hash_map $<$:rt>) # Configure the library name to identify it as a Python extension module. diff --git a/src/flat_hash_map.cpp b/src/flat_hash_map.cpp new file mode 100644 index 00000000..8c8ccfae --- /dev/null +++ b/src/flat_hash_map.cpp @@ -0,0 +1,18 @@ +#include "CommonInc.h" +#include "RipTide.h" +#include "MathWorker.h" +#include "Recycler.h" +#include "flat_hash_map.h" + +#include "absl/container/flat_hash_map.h" + +int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, + int8_t * bool_out_p, int32_t size_type) +{ + return 0; +} + +template +fhm_hasher::fhm_hasher(HASH_MODE, bool) +{ +} diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h new file mode 100644 index 00000000..35fe31ed --- /dev/null +++ b/src/flat_hash_map.h @@ -0,0 +1,38 @@ +#ifndef RIPTIDE_CPP_FLAT_HASH_MAP_H + #define RIPTIDE_CPP_FLAG_HASH_MAP_H + + #include "CommonInc.h" + #include "absl/container/flat_hash_map.h" + + #include + + #if defined(_WIN32) && ! defined(__GNUC__) + #define dll_export __declspec(dllexport) + #else + #define dll_export + #endif + +namespace +{ + enum class HASH_MODE + { + HASH_MODE_PRIME = 1, + HASH_MODE_MASK = 2 + }; +} + +dll_export int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, + int64_t * output_p, int8_t * bool_out_p, int32_t size_type); + +template +struct fhm_hasher +{ + absl::flat_hash_map hasher{}; + + dll_export fhm_hasher() = default; + dll_export fhm_hasher(HASH_MODE mode = HASH_MODE_PRIME, bool deallocate = true); + + dll_export void make_hash_location(size_t array_size, uint64_t * hash_list, size_t hint_size); +}; + +#endif diff --git a/test/riptide_python_test/CMakeLists.txt b/test/riptide_python_test/CMakeLists.txt index 4b57b4a5..3519a2d7 100644 --- a/test/riptide_python_test/CMakeLists.txt +++ b/test/riptide_python_test/CMakeLists.txt @@ -73,4 +73,5 @@ endif() target_link_libraries(${TARGET_NAME} Python3::Python - riptide_cpp) + riptide_cpp + ut) diff --git a/test/riptide_python_test/hash_linear_tests.cpp b/test/riptide_python_test/hash_linear_tests.cpp index c1f0e236..f945b62f 100644 --- a/test/riptide_python_test/hash_linear_tests.cpp +++ b/test/riptide_python_test/hash_linear_tests.cpp @@ -3,7 +3,7 @@ #include "HashLinear.h" #define BOOST_UT_DISABLE_MODULE -#include "../ut/include/boost/ut.hpp" +#include "boost/ut.hpp" #include #include diff --git a/test/riptide_test/CMakeLists.txt b/test/riptide_test/CMakeLists.txt index 2fa433b3..21699f06 100644 --- a/test/riptide_test/CMakeLists.txt +++ b/test/riptide_test/CMakeLists.txt @@ -36,6 +36,7 @@ set(TARGET_NAME riptide_test) set(CMAKE_VERBOSE_MAKEFILE on) set(SOURCES + flat_hash_map_tests.cpp main.cpp test_one_input.cpp) @@ -48,7 +49,17 @@ get_target_property(RT_SOURCE_DIR riptide_cpp SOURCE_DIR) target_include_directories(${TARGET_NAME} PRIVATE ${RT_SOURCE_DIR} ${Python3_INCLUDE_DIRS} - ${Python3_NumPy_INCLUDE_DIRS}) + ${Python3_NumPy_INCLUDE_DIRS} + ${ABSL_COMMON_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} - riptide_cpp) + riptide_cpp + ut) + +if(WIN32) + set(_TARGET_DIR $) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMENT "Copying runtime dependencies from riptide_cpp to ${_TARGET_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${_TARGET_DIR}) +endif() diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp new file mode 100644 index 00000000..1335720e --- /dev/null +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -0,0 +1,54 @@ +#include "flat_hash_map.h" + +#define BOOST_UT_DISABLE_MODULE +#include "boost/ut.hpp" + +#include +#include +#include +#include +#include + +using namespace boost::ut; +using boost::ut::suite; + +namespace +{ + std::array output; + std::array bools; + std::random_device dev{}; + std::mt19937 engine{ dev() }; + + suite flat_hash_map_ops = [] + { + "is_member_uint64"_test = [&] + { + std::vector haystack(10); + std::uniform_int_distribution dist(0, ULLONG_MAX); + + std::generate(std::begin(haystack), std::end(haystack), [&] { return dist(engine); }); + std::vector needles{ haystack[3], haystack[9], haystack[0], haystack[5] }; + + expect(is_member(needles.size(), reinterpret_cast(needles.data()), haystack.size(), + reinterpret_cast(haystack.data()), output.data(), bools.data(), 8) == 0_i); + + expect( output[ 0 ] == 3_i ); + expect( output[ 1 ] == 9_i ); + expect( output[ 2 ] == 0_i ); + expect( output[ 3 ] == 5_i ); + }; + + "is_member_many_uniques"_test = [&] + { + std::vector haystack(2 * 128 * 1024ULL * 1024ULL); + std::vector needles(1024ULL * 1024ULL); + std::uniform_int_distribution dist(3002950000, haystack.size() + 3002950000); + + std::iota(std::begin(haystack), std::end(haystack), 3002954000); + std::generate(std::begin(needles), std::end(needles), [&] { return dist(engine); }); + + expect(is_member(needles.size(), reinterpret_cast(needles.data()), haystack.size(), + reinterpret_cast(haystack.data()), output.data(), bools.data(), 8) == 0_i); + }; + }; +} From 7fd0faadc92c89a647c767a7c2921696c8ca3f13 Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Thu, 31 Mar 2022 12:37:32 -0400 Subject: [PATCH 02/10] A minimal implementation of something is_member - like --- .../riptide_bench/hash_linear_bench.cpp | 20 ++- src/flat_hash_map.cpp | 20 +-- src/flat_hash_map.h | 115 ++++++++++++++---- src/overload.h | 19 +++ test/riptide_test/flat_hash_map_tests.cpp | 51 +++++++- 5 files changed, 188 insertions(+), 37 deletions(-) create mode 100644 src/overload.h diff --git a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp index bb834066..b29e4393 100644 --- a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp +++ b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp @@ -1,5 +1,6 @@ #include "RipTide.h" #include "HashLinear.h" +#include "flat_hash_map.h" #include "benchmark/benchmark.h" @@ -10,10 +11,24 @@ namespace { -#if 0 - std::vector test_data(1024ULL * 1024ULL * 1024ULL); + std::vector test_data(128ULL * 1024ULL * 1024ULL); std::random_device dev{}; CHashLinear hasher{}; + fhm_hasher new_hasher{}; + + void bench_make_hash(benchmark::State & state) + { + std::iota(std::begin(test_data), std::end(test_data), 3002954500); + std::shuffle(std::begin(test_data), std::end(test_data), std::mt19937{ dev() }); + for (auto _ : state) + { + new_hasher.make_hash(test_data.size(), reinterpret_cast(test_data.data()), 0); + benchmark::DoNotOptimize(test_data.data()); + benchmark::ClobberMemory(); + } + } + + BENCHMARK(bench_make_hash); void bench_MakeHashLocation(benchmark::State & state) { @@ -28,5 +43,4 @@ namespace } BENCHMARK(bench_MakeHashLocation); -#endif } diff --git a/src/flat_hash_map.cpp b/src/flat_hash_map.cpp index 8c8ccfae..fac123e0 100644 --- a/src/flat_hash_map.cpp +++ b/src/flat_hash_map.cpp @@ -6,13 +6,13 @@ #include "absl/container/flat_hash_map.h" -int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, - int8_t * bool_out_p, int32_t size_type) -{ - return 0; -} - -template -fhm_hasher::fhm_hasher(HASH_MODE, bool) -{ -} +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index 35fe31ed..08d56747 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -1,38 +1,111 @@ #ifndef RIPTIDE_CPP_FLAT_HASH_MAP_H - #define RIPTIDE_CPP_FLAG_HASH_MAP_H +#define RIPTIDE_CPP_FLAG_HASH_MAP_H - #include "CommonInc.h" - #include "absl/container/flat_hash_map.h" +#include "CommonInc.h" +#include "overload.h" +#include "absl/container/flat_hash_map.h" - #include +#include +#include +#include - #if defined(_WIN32) && ! defined(__GNUC__) - #define dll_export __declspec(dllexport) - #else - #define dll_export - #endif +#if defined(_WIN32) && ! defined(__GNUC__) +#define dll_export __declspec(dllexport) +#else +#define dll_export +#endif namespace { - enum class HASH_MODE - { - HASH_MODE_PRIME = 1, - HASH_MODE_MASK = 2 - }; + using is_member_allowed_types_t = std::variant< uint8_t, uint16_t, uint32_t, uint64_t, float, double, int8_t, int16_t, int32_t, int64_t>; } -dll_export int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, - int64_t * output_p, int8_t * bool_out_p, int32_t size_type); +template< typename T > +struct type_deducer; -template +template struct fhm_hasher { - absl::flat_hash_map hasher{}; + absl::flat_hash_map hasher{}; + KeyT const * data_series_p{}; - dll_export fhm_hasher() = default; - dll_export fhm_hasher(HASH_MODE mode = HASH_MODE_PRIME, bool deallocate = true); + dll_export fhm_hasher() {} - dll_export void make_hash_location(size_t array_size, uint64_t * hash_list, size_t hint_size); + dll_export void make_hash(size_t array_size, char const * hash_list_p, size_t hint_size) + { + do_make_hash(array_size, reinterpret_cast< KeyT const * >(hash_list_p), hint_size); + } + + void do_make_hash(size_t array_size, KeyT const * hash_list_p, size_t hint_size) + { + if (not hint_size) + { + hint_size = array_size; + } + + hasher.reserve(hint_size); + + data_series_p = hash_list_p; + + for( size_t i{0}; i != hint_size; ++i ) + { + hasher.try_emplace( hash_list_p[ i ], i ); + } + } + + int64_t find( KeyT const * key ) const noexcept + { + auto ret_iter = hasher.find(*key); + + return ret_iter != hasher.end() ? ret_iter->second : -1ll; + } }; +template< typename KeyT > +struct is_member_check +{ + fhm_hasher hash{}; + + int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, int8_t * bool_out_p) + { + memset( output_p, -1, sizeof( KeyT ) * needles_size ); + memset( bool_out_p, 0, needles_size ); + + KeyT const * typed_needles_p{ reinterpret_cast< KeyT const *>(needles_p) }; + + hash.make_hash(haystack_size, haystack_p, 0); + + for( ptrdiff_t elem{0}; elem != needles_size; ++elem ) + { + int64_t found_at = hash.find(typed_needles_p + elem); + if ( found_at > 0 ) + { + *(output_p + elem) = found_at; + *(bool_out_p + elem) = 1; + } + } + return 0; + } +}; + +dll_export int +is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, + int8_t * bool_out_p, is_member_allowed_types_t sample_value) +{ + auto hasher_calls = overload{ + [=](uint8_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint16_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint32_t)->int { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint64_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int8_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int16_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int32_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int64_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](float)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](double)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + }; + + return std::visit(hasher_calls, sample_value); +} + #endif diff --git a/src/overload.h b/src/overload.h new file mode 100644 index 00000000..67a4fb9a --- /dev/null +++ b/src/overload.h @@ -0,0 +1,19 @@ +#ifndef RIPTIDECPP_OVERLOAD_H +#define RIPTIDECPP_OVERLOAD_H + +template +struct overload : Fs... +{ + template + overload(Ts &&... ts) + : Fs{ std::forward(ts) }... + { + } + + using Fs::operator()...; +}; + +template +overload(Ts &&...) -> overload...>; + +#endif diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp index 1335720e..10b383b8 100644 --- a/test/riptide_test/flat_hash_map_tests.cpp +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include using namespace boost::ut; using boost::ut::suite; @@ -21,6 +22,45 @@ namespace suite flat_hash_map_ops = [] { + "make_hash"_test = [&](DataT arg) + { + boost::ut::log << "Data Type: " << reflection::type_name() << " "; + std::array data{}; + if constexpr( std::is_floating_point_v ) + { + std::uniform_real_distribution dist(0, std::numeric_limits::max()); + std::generate(std::begin(data), std::end(data), [&] { return dist(engine); }); + } + else + { + std::uniform_int_distribution dist(0, std::numeric_limits::max()); + std::generate(std::begin(data), std::end(data), [&] { return dist(engine); }); + } + + fhm_hasher hash{}; + + hash.make_hash(data.size(), reinterpret_cast< char const * >(data.data()), 0); + + expect( hash.hasher.find(data[0])->second == 0u); + expect(hash.hasher.find(data[1])->second == 1u); + expect(hash.hasher.find(data[2])->second == 2u); + expect(hash.hasher.find(data[3])->second == 3u); + expect(hash.hasher.find(data[4])->second == 4u); + expect(hash.hasher.find(data[5])->second == 5u); + expect(hash.hasher.find(data[6])->second == 6u); + expect(hash.hasher.find(data[7])->second == 7u); + expect(hash.hasher.find(data[8])->second == 8u); + expect(hash.hasher.find(data[9])->second == 9u); + expect(hash.hasher.find(data[10])->second == 10u); + expect(hash.hasher.find(data[11])->second == 11u); + expect(hash.hasher.find(data[12])->second == 12u); + expect(hash.hasher.find(data[13])->second == 13u); + expect(hash.hasher.find(data[14])->second == 14u); + expect(hash.hasher.find(data[15])->second == 15u); + expect(hash.hasher.find(data[16])->second == 16u); + expect(hash.hasher.find(data[17])->second == 17u); + } | std::tuple{}; + "is_member_uint64"_test = [&] { std::vector haystack(10); @@ -30,12 +70,17 @@ namespace std::vector needles{ haystack[3], haystack[9], haystack[0], haystack[5] }; expect(is_member(needles.size(), reinterpret_cast(needles.data()), haystack.size(), - reinterpret_cast(haystack.data()), output.data(), bools.data(), 8) == 0_i); + reinterpret_cast(haystack.data()), output.data(), bools.data(), needles[0]) == 0_i); expect( output[ 0 ] == 3_i ); expect( output[ 1 ] == 9_i ); - expect( output[ 2 ] == 0_i ); + expect( output[ 2 ] == -1_i ); expect( output[ 3 ] == 5_i ); + + expect( bools[ 0 ] == 1_i ); + expect( bools[ 1 ] == 1_i ); + expect( bools[ 2 ] == 0_i ); + expect( bools[ 3 ] == 1_i ); }; "is_member_many_uniques"_test = [&] @@ -48,7 +93,7 @@ namespace std::generate(std::begin(needles), std::end(needles), [&] { return dist(engine); }); expect(is_member(needles.size(), reinterpret_cast(needles.data()), haystack.size(), - reinterpret_cast(haystack.data()), output.data(), bools.data(), 8) == 0_i); + reinterpret_cast(haystack.data()), output.data(), bools.data(), needles[0]) == 0_i); }; }; } From f4663902660da2add4f454734d98f6fe3a41c70b Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Thu, 31 Mar 2022 15:29:38 -0400 Subject: [PATCH 03/10] Trying to understand the difference between bench_MakeHashLocation and bench_IsMemberHash64 --- .../riptide_bench/hash_linear_bench.cpp | 55 +++++++++++++++++-- src/flat_hash_map.h | 9 ++- .../riptide_python_test/hash_linear_tests.cpp | 2 +- test/riptide_test/flat_hash_map_tests.cpp | 2 +- 4 files changed, 59 insertions(+), 9 deletions(-) diff --git a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp index b29e4393..c3ac80af 100644 --- a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp +++ b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp @@ -4,6 +4,7 @@ #include "benchmark/benchmark.h" +#include #include #include #include @@ -15,20 +16,30 @@ namespace std::random_device dev{}; CHashLinear hasher{}; fhm_hasher new_hasher{}; + std::vector needles(1024ULL * 1024ULL); + std::array output; + std::array bools; - void bench_make_hash(benchmark::State & state) + void bench_IsMemberHash64(benchmark::State & state) { + std::mt19937 engine(dev()); + std::uniform_int_distribution dist(3002950000, test_data.size() + 3002950000); std::iota(std::begin(test_data), std::end(test_data), 3002954500); - std::shuffle(std::begin(test_data), std::end(test_data), std::mt19937{ dev() }); + std::generate(std::begin(needles), std::end(needles), [&] { return dist(engine); }); + for (auto _ : state) { - new_hasher.make_hash(test_data.size(), reinterpret_cast(test_data.data()), 0); + IsMemberHash64(needles.size(), needles.data(), test_data.size(), test_data.data(), output.data(), bools.data(), 8, HASH_MODE(1), 0); benchmark::DoNotOptimize(test_data.data()); + benchmark::DoNotOptimize(output.data()); + benchmark::DoNotOptimize(bools.data()); + benchmark::DoNotOptimize(needles.data()); benchmark::ClobberMemory(); } + } - BENCHMARK(bench_make_hash); + BENCHMARK(bench_IsMemberHash64)->Unit(benchmark::kMillisecond); void bench_MakeHashLocation(benchmark::State & state) { @@ -42,5 +53,39 @@ namespace } } - BENCHMARK(bench_MakeHashLocation); + BENCHMARK(bench_MakeHashLocation)->Unit(benchmark::kMillisecond); + + void bench_is_member(benchmark::State & state) + { + std::mt19937 engine(dev()); + std::uniform_int_distribution dist(3002950000, test_data.size() + 3002950000); + std::iota(std::begin(test_data), std::end(test_data), 3002954500); + std::generate(std::begin(needles), std::end(needles), [&] { return dist(engine); }); + + for (auto _ : state) + { + is_member(needles.size(), reinterpret_cast(needles.data()), test_data.size(), reinterpret_cast(test_data.data()), output.data(), bools.data(), uint64_t{}); + benchmark::DoNotOptimize(test_data.data()); + benchmark::DoNotOptimize(output.data()); + benchmark::DoNotOptimize(bools.data()); + benchmark::DoNotOptimize(needles.data()); + benchmark::ClobberMemory(); + } + } + + BENCHMARK(bench_is_member)->Unit(benchmark::kMillisecond); + + void bench_make_hash(benchmark::State & state) + { + std::iota(std::begin(test_data), std::end(test_data), 3002954500); + std::shuffle(std::begin(test_data), std::end(test_data), std::mt19937{ dev() }); + for (auto _ : state) + { + new_hasher.make_hash(test_data.size(), reinterpret_cast(test_data.data()), 0); + benchmark::DoNotOptimize(test_data.data()); + benchmark::ClobberMemory(); + } + } + + BENCHMARK(bench_make_hash)->Unit(benchmark::kMillisecond); } diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index 08d56747..c82e9067 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -68,8 +68,8 @@ struct is_member_check int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, int8_t * bool_out_p) { - memset( output_p, -1, sizeof( KeyT ) * needles_size ); - memset( bool_out_p, 0, needles_size ); +// memset( output_p, -1, sizeof( KeyT ) * needles_size ); +// memset( bool_out_p, 0, needles_size ); KeyT const * typed_needles_p{ reinterpret_cast< KeyT const *>(needles_p) }; @@ -83,6 +83,11 @@ struct is_member_check *(output_p + elem) = found_at; *(bool_out_p + elem) = 1; } + else + { + *(output_p + elem) = -1; + *(bool_out_p + elem) = 0; + } } return 0; } diff --git a/test/riptide_python_test/hash_linear_tests.cpp b/test/riptide_python_test/hash_linear_tests.cpp index f945b62f..4699f28b 100644 --- a/test/riptide_python_test/hash_linear_tests.cpp +++ b/test/riptide_python_test/hash_linear_tests.cpp @@ -25,7 +25,7 @@ namespace { "ismember64_uint64_too_many_uniques"_test = [&] { - std::vector haystack(2 * 128 * 1024ULL * 1024ULL); + std::vector haystack(128ULL * 1024ULL * 1024ULL); std::vector needles(1024ULL * 1024ULL); std::random_device dev{}; std::mt19937 engine(dev()); diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp index 10b383b8..8cfd9511 100644 --- a/test/riptide_test/flat_hash_map_tests.cpp +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -85,7 +85,7 @@ namespace "is_member_many_uniques"_test = [&] { - std::vector haystack(2 * 128 * 1024ULL * 1024ULL); + std::vector haystack(128 * 1024ULL * 1024ULL); std::vector needles(1024ULL * 1024ULL); std::uniform_int_distribution dist(3002950000, haystack.size() + 3002950000); From f515279a2490e788e18088f151a6682322e6e3c8 Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Fri, 1 Apr 2022 12:54:56 -0400 Subject: [PATCH 04/10] Find 0th key --- .../riptide_bench/riptide_bench/hash_linear_bench.cpp | 2 +- src/flat_hash_map.h | 9 +++++---- test/riptide_test/flat_hash_map_tests.cpp | 10 ++++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp index c3ac80af..81f1415f 100644 --- a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp +++ b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp @@ -12,7 +12,7 @@ namespace { - std::vector test_data(128ULL * 1024ULL * 1024ULL); + std::vector test_data(2ULL * 1024ULL * 1024ULL); std::random_device dev{}; CHashLinear hasher{}; fhm_hasher new_hasher{}; diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index c82e9067..054673b0 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -8,6 +8,7 @@ #include #include #include +#include #if defined(_WIN32) && ! defined(__GNUC__) #define dll_export __declspec(dllexport) @@ -49,15 +50,15 @@ struct fhm_hasher for( size_t i{0}; i != hint_size; ++i ) { - hasher.try_emplace( hash_list_p[ i ], i ); + hasher.emplace(hash_list_p[i], i); } } int64_t find( KeyT const * key ) const noexcept { - auto ret_iter = hasher.find(*key); + bool found = hasher.contains(*key); - return ret_iter != hasher.end() ? ret_iter->second : -1ll; + return found ? hasher.at( *key ): -1ll; } }; @@ -78,7 +79,7 @@ struct is_member_check for( ptrdiff_t elem{0}; elem != needles_size; ++elem ) { int64_t found_at = hash.find(typed_needles_p + elem); - if ( found_at > 0 ) + if ( found_at >= 0 ) { *(output_p + elem) = found_at; *(bool_out_p + elem) = 1; diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp index 8cfd9511..413f0fa8 100644 --- a/test/riptide_test/flat_hash_map_tests.cpp +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -64,23 +64,25 @@ namespace "is_member_uint64"_test = [&] { std::vector haystack(10); - std::uniform_int_distribution dist(0, ULLONG_MAX); + std::uniform_int_distribution dist(0, ULLONG_MAX - 2); std::generate(std::begin(haystack), std::end(haystack), [&] { return dist(engine); }); - std::vector needles{ haystack[3], haystack[9], haystack[0], haystack[5] }; + std::vector needles{ haystack[3], haystack[9], haystack[0], haystack[5], ULLONG_MAX - 1 }; expect(is_member(needles.size(), reinterpret_cast(needles.data()), haystack.size(), reinterpret_cast(haystack.data()), output.data(), bools.data(), needles[0]) == 0_i); expect( output[ 0 ] == 3_i ); expect( output[ 1 ] == 9_i ); - expect( output[ 2 ] == -1_i ); + expect( output[ 2 ] == 0_i ); expect( output[ 3 ] == 5_i ); + expect( output[ 4 ] == -1_i ); expect( bools[ 0 ] == 1_i ); expect( bools[ 1 ] == 1_i ); - expect( bools[ 2 ] == 0_i ); + expect( bools[ 2 ] == 1_i ); expect( bools[ 3 ] == 1_i ); + expect( bools[ 4 ] == 0_i ); }; "is_member_many_uniques"_test = [&] From ece5cb15bb171336cddfc5964c453693ea51c8de Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Mon, 4 Apr 2022 15:21:32 -0400 Subject: [PATCH 05/10] Add in the tbb code as a submodule --- .gitmodules | 3 +++ external/oneTBB | 1 + 2 files changed, 4 insertions(+) create mode 160000 external/oneTBB diff --git a/.gitmodules b/.gitmodules index ccb83851..12e87b1d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "external/abseil-cpp"] path = external/abseil-cpp url = https://github.com/abseil/abseil-cpp.git +[submodule "external/oneTBB"] + path = external/oneTBB + url = https://github.com/oneapi-src/oneTBB.git diff --git a/external/oneTBB b/external/oneTBB new file mode 160000 index 00000000..9d2a3477 --- /dev/null +++ b/external/oneTBB @@ -0,0 +1 @@ +Subproject commit 9d2a3477ce276d437bf34b1582781e5b11f9b37a From 9dedaa3ed7c0f80e2e6df6d96d67579905eaee62 Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Mon, 4 Apr 2022 15:21:59 -0400 Subject: [PATCH 06/10] Compile in tbb, as well as some commented through variations in the benchmarks --- CMakeLists.txt | 1 + .../riptide_bench/CMakeLists.txt | 4 +- .../riptide_bench/hash_linear_bench.cpp | 12 +- external/CMakeLists.txt | 3 + src/CMakeLists.txt | 2 + src/flat_hash_map.h | 156 ++++++++++++------ 6 files changed, 122 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2825c9f8..94121133 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ else() endif() add_subdirectory(external ${EXLUSION_SPECIFIER}) + add_subdirectory(src) set(BENCHMARK_ENABLE_GTEST_TESTS OFF) add_subdirectory(bench ${EXLUSION_SPECIFIER}) diff --git a/bench/riptide_bench/riptide_bench/CMakeLists.txt b/bench/riptide_bench/riptide_bench/CMakeLists.txt index 41c64327..a09cce29 100644 --- a/bench/riptide_bench/riptide_bench/CMakeLists.txt +++ b/bench/riptide_bench/riptide_bench/CMakeLists.txt @@ -41,11 +41,11 @@ add_executable(${TARGET_NAME} ${HEADERS} ${SOURCES}) get_target_property(RT_SOURCE_DIR riptide_cpp SOURCE_DIR) -target_include_directories(${TARGET_NAME} PRIVATE ${RT_SOURCE_DIR} ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS}) +target_include_directories(${TARGET_NAME} PRIVATE ${RT_SOURCE_DIR} external/oneTBB/include ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS}) target_link_directories(${TARGET_NAME} PRIVATE ${Python3_LIBRARY_DIRS}) -target_link_libraries(${TARGET_NAME} PRIVATE riptide_cpp ${Python3_Libraries} benchmark::benchmark $<$:pthread> $<$:rt>) +target_link_libraries(${TARGET_NAME} PRIVATE riptide_cpp TBB::tbb ${Python3_Libraries} benchmark::benchmark $<$:pthread> $<$:rt>) if(WIN32) set(_TARGET_DIR $) diff --git a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp index 81f1415f..bb13e735 100644 --- a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp +++ b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp @@ -17,8 +17,8 @@ namespace CHashLinear hasher{}; fhm_hasher new_hasher{}; std::vector needles(1024ULL * 1024ULL); - std::array output; - std::array bools; + std::array output{}; + std::array bools{}; void bench_IsMemberHash64(benchmark::State & state) { @@ -39,7 +39,7 @@ namespace } - BENCHMARK(bench_IsMemberHash64)->Unit(benchmark::kMillisecond); + BENCHMARK(bench_IsMemberHash64)->Unit(benchmark::kMillisecond)->UseRealTime(); void bench_MakeHashLocation(benchmark::State & state) { @@ -53,7 +53,7 @@ namespace } } - BENCHMARK(bench_MakeHashLocation)->Unit(benchmark::kMillisecond); + BENCHMARK(bench_MakeHashLocation)->Unit(benchmark::kMillisecond)->UseRealTime(); void bench_is_member(benchmark::State & state) { @@ -73,7 +73,7 @@ namespace } } - BENCHMARK(bench_is_member)->Unit(benchmark::kMillisecond); +// BENCHMARK(bench_is_member)->Unit(benchmark::kMillisecond)->UseRealTime(); void bench_make_hash(benchmark::State & state) { @@ -87,5 +87,5 @@ namespace } } - BENCHMARK(bench_make_hash)->Unit(benchmark::kMillisecond); + BENCHMARK(bench_make_hash)->Unit(benchmark::kMillisecond)->UseRealTime(); } diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 4609a223..4b5c54ea 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -15,3 +15,6 @@ set(EXTERNAL_ZSTD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zstd/lib PARENT_SCOPE) set(ABSL_BUILD_TESTING OFF CACHE BOOL "" FORCE) set(ABSL_PROPAGATE_CXX_STD ON CACHE BOOL "" FORCE) add_subdirectory(abseil-cpp) + +set(TBB_DISABLE_HWLOC_AUTOMATIC_SEARCH ON CACHE BOOL "" FORCE) +add_subdirectory(oneTBB) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 74742bc9..38cb9e88 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ get_target_property(RT_SOURCE_DIR riptide_cpp SOURCE_DIR) target_include_directories(${TARGET_NAME} PRIVATE ${EXTERNAL_ZSTD_INCLUDE_DIR} + ../external/oneTBB/include ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS} ${ABSL_COMMON_INCLUDE_DIRS}) @@ -127,6 +128,7 @@ target_link_libraries(${TARGET_NAME} libzstd_static absl::base absl::flat_hash_map + TBB::tbb $<$:rt>) # Configure the library name to identify it as a Python extension module. diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index 054673b0..9822b68b 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -1,40 +1,86 @@ #ifndef RIPTIDE_CPP_FLAT_HASH_MAP_H -#define RIPTIDE_CPP_FLAG_HASH_MAP_H - -#include "CommonInc.h" -#include "overload.h" -#include "absl/container/flat_hash_map.h" - -#include -#include -#include -#include - -#if defined(_WIN32) && ! defined(__GNUC__) -#define dll_export __declspec(dllexport) -#else -#define dll_export -#endif + #define RIPTIDE_CPP_FLAG_HASH_MAP_H + + #include "CommonInc.h" + #include "overload.h" + #include "absl/container/flat_hash_map.h" +#include "oneapi/tbb/concurrent_hash_map.h" +#include "oneapi/tbb/blocked_range.h" +#include "oneapi/tbb/parallel_for.h" + + #include + #include + #include + #include +#include +#include + + #if defined(_WIN32) && ! defined(__GNUC__) + #define dll_export __declspec(dllexport) + #else + #define dll_export + #endif namespace { - using is_member_allowed_types_t = std::variant< uint8_t, uint16_t, uint32_t, uint64_t, float, double, int8_t, int16_t, int32_t, int64_t>; + using is_member_allowed_types_t = + std::variant; + + template< typename KeyT > + struct hash_function + { + size_t const operator()(KeyT const & key) const + { + if constexpr( std::is_integral_v) + { + return static_cast(key) % 3145739; + } + else + { + size_t retval{}; + if constexpr( sizeof( KeyT ) > sizeof(size_t)) + { + memcpy( &retval, &key, sizeof(size_t) ); + return retval % 3145739; + } + else + { + memcpy(&retval, &key, sizeof(KeyT)); + return retval % 3245739; + } + } + } + }; + + std::vector< char > backing(128ULL * 1024ULL * 1024ULL); } -template< typename T > +template struct type_deducer; + template struct fhm_hasher { - absl::flat_hash_map hasher{}; + std::pmr::monotonic_buffer_resource mono{ backing.data(), backing.capacity() }; + std::pmr::unsynchronized_pool_resource pool{ &mono }; + std::pmr::memory_resource * resource{&pool}; + +// oneapi::tbb::concurrent_hash_map hasher; +// absl::flat_hash_map hasher{}; +// absl::flat_hash_map, +// absl::container_internal::hash_default_eq, +// std::pmr::polymorphic_allocator>> hasher{resource}; + std::pmr::unordered_map> hasher{resource}; +// std::unordered_map hasher{}; KeyT const * data_series_p{}; dll_export fhm_hasher() {} dll_export void make_hash(size_t array_size, char const * hash_list_p, size_t hint_size) { - do_make_hash(array_size, reinterpret_cast< KeyT const * >(hash_list_p), hint_size); + do_make_hash(array_size, reinterpret_cast(hash_list_p), hint_size); } void do_make_hash(size_t array_size, KeyT const * hash_list_p, size_t hint_size) @@ -47,39 +93,44 @@ struct fhm_hasher hasher.reserve(hint_size); data_series_p = hash_list_p; - - for( size_t i{0}; i != hint_size; ++i ) + + for (size_t i{ 0 }; i != hint_size; ++i) { hasher.emplace(hash_list_p[i], i); +// typename oneapi::tbb::concurrent_hash_map::accessor a; +// hasher.insert(a, hash_list_p[i]); +// a->second = i; } } - int64_t find( KeyT const * key ) const noexcept + int64_t find(KeyT const * key) const noexcept { - bool found = hasher.contains(*key); - - return found ? hasher.at( *key ): -1ll; +// typename oneapi::tbb::concurrent_hash_map::accessor a; +// a->first = *key; +// bool found = hasher.find(a); +// bool found = hasher.contains(*key); + bool found = hasher.find(*key) != hasher.end(); + + return found ? hasher.at(*key) : -1ll; } }; -template< typename KeyT > +template struct is_member_check { fhm_hasher hash{}; - int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, int8_t * bool_out_p) + int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, + int8_t * bool_out_p) { -// memset( output_p, -1, sizeof( KeyT ) * needles_size ); -// memset( bool_out_p, 0, needles_size ); - - KeyT const * typed_needles_p{ reinterpret_cast< KeyT const *>(needles_p) }; - + KeyT const * typed_needles_p{ reinterpret_cast(needles_p) }; + hash.make_hash(haystack_size, haystack_p, 0); - for( ptrdiff_t elem{0}; elem != needles_size; ++elem ) + for (ptrdiff_t elem{ 0 }; elem != needles_size; ++elem) { int64_t found_at = hash.find(typed_needles_p + elem); - if ( found_at >= 0 ) + if (found_at >= 0) { *(output_p + elem) = found_at; *(bool_out_p + elem) = 1; @@ -94,21 +145,30 @@ struct is_member_check } }; -dll_export int -is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, - int8_t * bool_out_p, is_member_allowed_types_t sample_value) +dll_export int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, + int64_t * output_p, int8_t * bool_out_p, is_member_allowed_types_t sample_value) { auto hasher_calls = overload{ - [=](uint8_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](uint16_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](uint32_t)->int { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](uint64_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](int8_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](int16_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](int32_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](int64_t)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](float)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, - [=](double)->int{ return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint8_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint16_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint32_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](uint64_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int8_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int16_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int32_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](int64_t) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](float) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + [=](double) -> int + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, }; return std::visit(hasher_calls, sample_value); From c6f0d230add2429cb1ebdac77a102645520cecc3 Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Tue, 12 Apr 2022 10:08:34 -0400 Subject: [PATCH 07/10] This needs the correct tests for tbb --- .../riptide_bench/hash_linear_bench.cpp | 2 +- src/CMakeLists.txt | 1 + src/flat_hash_map.h | 34 +++++++++---------- test/riptide_test/flat_hash_map_tests.cpp | 3 ++ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp index bb13e735..90dd318d 100644 --- a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp +++ b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp @@ -73,7 +73,7 @@ namespace } } -// BENCHMARK(bench_is_member)->Unit(benchmark::kMillisecond)->UseRealTime(); + BENCHMARK(bench_is_member)->Unit(benchmark::kMillisecond)->UseRealTime(); void bench_make_hash(benchmark::State & state) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38cb9e88..bd4a3a89 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,7 @@ set(HEADERS DateTime.h Ema.h #FileReadWrite.h + flat_hash_map.h GroupBy.h HashFunctions.h HashLinear.h diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index 9822b68b..a91dd381 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -62,17 +62,17 @@ struct type_deducer; template struct fhm_hasher { - std::pmr::monotonic_buffer_resource mono{ backing.data(), backing.capacity() }; - std::pmr::unsynchronized_pool_resource pool{ &mono }; - std::pmr::memory_resource * resource{&pool}; +// std::pmr::monotonic_buffer_resource mono{ backing.data(), backing.capacity() }; +// std::pmr::unsynchronized_pool_resource pool{ &mono }; +// std::pmr::memory_resource * resource{&mono}; -// oneapi::tbb::concurrent_hash_map hasher; + oneapi::tbb::concurrent_hash_map hasher; // absl::flat_hash_map hasher{}; // absl::flat_hash_map, // absl::container_internal::hash_default_eq, // std::pmr::polymorphic_allocator>> hasher{resource}; - std::pmr::unordered_map> hasher{resource}; +// std::pmr::unordered_map> hasher{resource}; // std::unordered_map hasher{}; KeyT const * data_series_p{}; @@ -90,35 +90,35 @@ struct fhm_hasher hint_size = array_size; } - hasher.reserve(hint_size); +// hasher.reserve(hint_size); data_series_p = hash_list_p; for (size_t i{ 0 }; i != hint_size; ++i) { - hasher.emplace(hash_list_p[i], i); -// typename oneapi::tbb::concurrent_hash_map::accessor a; -// hasher.insert(a, hash_list_p[i]); -// a->second = i; +// hasher.emplace(hash_list_p[i], i); + typename oneapi::tbb::concurrent_hash_map::accessor a; + hasher.insert(a, hash_list_p[i]); + a->second = i; } } int64_t find(KeyT const * key) const noexcept { -// typename oneapi::tbb::concurrent_hash_map::accessor a; -// a->first = *key; -// bool found = hasher.find(a); -// bool found = hasher.contains(*key); - bool found = hasher.find(*key) != hasher.end(); + typename oneapi::tbb::concurrent_hash_map::const_accessor finder; + bool found = hasher.find(finder, *key); + // bool found = hasher.contains(*key); + // bool found = hasher.find(*key) != hasher.end(); - return found ? hasher.at(*key) : -1ll; + //return found ? hasher.at(*key) : -1ll; + return found ? finder->second : -1ll; } }; template struct is_member_check { - fhm_hasher hash{}; + fhm_hasher mutable hash{}; int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, int8_t * bool_out_p) diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp index 413f0fa8..0cb6351e 100644 --- a/test/riptide_test/flat_hash_map_tests.cpp +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -41,6 +41,8 @@ namespace hash.make_hash(data.size(), reinterpret_cast< char const * >(data.data()), 0); + if constexpr( not std::is_same_v> ) + { expect( hash.hasher.find(data[0])->second == 0u); expect(hash.hasher.find(data[1])->second == 1u); expect(hash.hasher.find(data[2])->second == 2u); @@ -59,6 +61,7 @@ namespace expect(hash.hasher.find(data[15])->second == 15u); expect(hash.hasher.find(data[16])->second == 16u); expect(hash.hasher.find(data[17])->second == 17u); + } } | std::tuple{}; "is_member_uint64"_test = [&] From 385c9b8bc09aa85524fed6a3ea8946cc1b0148b8 Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Wed, 27 Apr 2022 14:13:05 -0400 Subject: [PATCH 08/10] Attempt to deal with the plethora of previous hashing implementations. HashLinear -> HashFunctions -> flat_hash_map for now --- .../riptide_bench/hash_linear_bench.cpp | 2 +- src/HashFunctions.cpp | 29 +++- src/HashLinear.cpp | 153 +++++++++++++----- src/flat_hash_map.cpp | 50 ++++-- src/flat_hash_map.h | 75 ++++++--- src/one_input.cpp | 3 + src/one_input_impl.h | 8 + src/operation_traits.h | 7 +- test/riptide_test/flat_hash_map_tests.cpp | 88 +++++++--- 9 files changed, 308 insertions(+), 107 deletions(-) diff --git a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp index 90dd318d..732f2de4 100644 --- a/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp +++ b/bench/riptide_bench/riptide_bench/hash_linear_bench.cpp @@ -15,7 +15,7 @@ namespace std::vector test_data(2ULL * 1024ULL * 1024ULL); std::random_device dev{}; CHashLinear hasher{}; - fhm_hasher new_hasher{}; + fhm_hasher new_hasher{}; std::vector needles(1024ULL * 1024ULL); std::array output{}; std::array bools{}; diff --git a/src/HashFunctions.cpp b/src/HashFunctions.cpp index 21e4ad47..ea348519 100644 --- a/src/HashFunctions.cpp +++ b/src/HashFunctions.cpp @@ -1,7 +1,11 @@ #include "RipTide.h" +#include "flat_hash_map.h" #include "HashFunctions.h" #include "HashLinear.h" #include "ndarray.h" +#include "one_input.h" + +#include // struct ndbuf; // typedef struct ndbuf { @@ -107,15 +111,27 @@ PyObject * IsMember64(PyObject * self, PyObject * args) } else { - if (arrayType1 == NPY_FLOAT32 || arrayType1 == NPY_FLOAT64) + if (std::getenv("RT_NEW_HASH")) { - IsMemberHash64(arraySize1, pDataIn1, arraySize2, pDataIn2, pDataOut2, pDataOut1, sizeType1 + 100, - HASH_MODE(hashMode), hintSize); + throw std::runtime_error("We're executing the new code path"); + auto [opt_op_trait, opt_type_trait] = riptable_cpp::set_traits(0, arrayType1); + riptable_cpp::data_type_t variant = *opt_type_trait; + [[maybe_unused]] int retval = is_member_for_type( + arraySize1, reinterpret_cast(pDataIn1), arraySize2, reinterpret_cast(pDataIn2), + pDataOut2, pDataOut1, variant, std::make_index_sequence>{}); } else { - IsMemberHash64(arraySize1, pDataIn1, arraySize2, pDataIn2, pDataOut2, pDataOut1, sizeType1, - HASH_MODE(hashMode), hintSize); + if (arrayType1 == NPY_FLOAT32 || arrayType1 == NPY_FLOAT64) + { + IsMemberHash64(arraySize1, pDataIn1, arraySize2, pDataIn2, pDataOut2, pDataOut1, sizeType1 + 100, + HASH_MODE(hashMode), hintSize); + } + else + { + IsMemberHash64(arraySize1, pDataIn1, arraySize2, pDataIn2, pDataOut2, pDataOut1, sizeType1, + HASH_MODE(hashMode), hintSize); + } } PyObject * retObject = Py_BuildValue("(OO)", boolArray, indexArray); @@ -168,7 +184,8 @@ PyObject * IsMemberCategorical(PyObject * self, PyObject * args) int sizeType1 = (int)NpyItemSize((PyObject *)inArr1); int sizeType2 = (int)NpyItemSize((PyObject *)inArr2); - LOGGING("IsMember32 %s vs %s size: %d %d\n", NpyToString(arrayType1), NpyToString(arrayType2), sizeType1, sizeType2); + LOGGING("IsMemberCategorical %s vs %s size: %d %d\n", NpyToString(arrayType1), NpyToString(arrayType2), sizeType1, + sizeType2); switch (arrayType1) { diff --git a/src/HashLinear.cpp b/src/HashLinear.cpp index 74da5458..18723b5e 100644 --- a/src/HashLinear.cpp +++ b/src/HashLinear.cpp @@ -2,11 +2,14 @@ #include #include "CommonInc.h" #include "RipTide.h" +#include "flat_hash_map.h" #include "HashLinear.h" #include "MathWorker.h" +#include "one_input.h" #include "Recycler.h" #include +#include #ifndef LogError #define LogError(...) @@ -5769,55 +5772,125 @@ PyObject * IsMember32(PyObject * self, PyObject * args) } else { - if (arrayType1 == NPY_FLOAT32 || arrayType1 == NPY_FLOAT64) + if (std::getenv("RT_NEW_HASH")) { - LOGGING("Calling float!\n"); - sizeType1 += 100; - } + if (arrayType1 == NPY_FLOAT32 || arrayType1 == NPY_FLOAT64) + { + LOGGING("Calling float!\n"); + sizeType1 += 100; + } - int dtype = NPY_INT8; + int dtype = NPY_INT8; - if (arraySize2 < 100) - { - dtype = NPY_INT8; - } - else if (arraySize2 < 30000) - { - dtype = NPY_INT16; - } - else if (arraySize2 < 2000000000) - { - dtype = NPY_INT32; + if (arraySize2 < 100) + { + dtype = NPY_INT8; + } + else if (arraySize2 < 30000) + { + dtype = NPY_INT16; + } + else if (arraySize2 < 2000000000) + { + dtype = NPY_INT32; + } + else + { + dtype = NPY_INT64; + } + + indexArray = AllocateLikeNumpyArray(inArr1, dtype); + + // make sure allocation succeeded + if (indexArray) + { + auto [opt_op_trait, opt_type_trait] = riptable_cpp::set_traits(0, arrayType1); + riptable_cpp::data_type_t variant = *opt_type_trait; + switch( dtype ) + { + case NPY_INT8: + { + [[maybe_unused]] int retval8 = is_member_for_type( + arraySize1, reinterpret_cast(pDataIn1), arraySize2, reinterpret_cast(pDataIn2), + reinterpret_cast(PyArray_BYTES(indexArray)), pDataOut1, variant, std::make_index_sequence>{}); + } + break; + case NPY_INT16: + { + [[maybe_unused]] int retval16 = is_member_for_type( + arraySize1, reinterpret_cast(pDataIn1), arraySize2, reinterpret_cast(pDataIn2), + reinterpret_cast(PyArray_BYTES(indexArray)), pDataOut1, variant, std::make_index_sequence>{}); + } + break; + case NPY_INT32: + { + [[maybe_unused]] int retval32 = is_member_for_type( + arraySize1, reinterpret_cast(pDataIn1), arraySize2, reinterpret_cast(pDataIn2), + reinterpret_cast(PyArray_BYTES(indexArray)), pDataOut1, variant, std::make_index_sequence>{}); + } + break; + default: + { + [[maybe_unused]] int retval64 = is_member_for_type( + arraySize1, reinterpret_cast(pDataIn1), arraySize2, reinterpret_cast(pDataIn2), + reinterpret_cast(PyArray_BYTES(indexArray)), pDataOut1, variant, std::make_index_sequence>{}); + } + break; + } + } } else { - dtype = NPY_INT64; - } + if (arrayType1 == NPY_FLOAT32 || arrayType1 == NPY_FLOAT64) + { + LOGGING("Calling float!\n"); + sizeType1 += 100; + } - indexArray = AllocateLikeNumpyArray(inArr1, dtype); + int dtype = NPY_INT8; - // make sure allocation succeeded - if (indexArray) - { - void * pDataOut2 = PyArray_BYTES(indexArray); - switch (dtype) + if (arraySize2 < 100) { - case NPY_INT8: - IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int8_t *)pDataOut2, pDataOut1, - sizeType1, HASH_MODE(hashMode), hintSize); - break; - case NPY_INT16: - IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int16_t *)pDataOut2, pDataOut1, - sizeType1, HASH_MODE(hashMode), hintSize); - break; - CASE_NPY_INT32: - IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int32_t *)pDataOut2, pDataOut1, - sizeType1, HASH_MODE(hashMode), hintSize); - break; - CASE_NPY_INT64: - IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int64_t *)pDataOut2, pDataOut1, - sizeType1, HASH_MODE(hashMode), hintSize); - break; + dtype = NPY_INT8; + } + else if (arraySize2 < 30000) + { + dtype = NPY_INT16; + } + else if (arraySize2 < 2000000000) + { + dtype = NPY_INT32; + } + else + { + dtype = NPY_INT64; + } + + indexArray = AllocateLikeNumpyArray(inArr1, dtype); + + // make sure allocation succeeded + if (indexArray) + { + void * pDataOut2 = PyArray_BYTES(indexArray); + switch (dtype) + { + case NPY_INT8: + IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int8_t *)pDataOut2, pDataOut1, + sizeType1, HASH_MODE(hashMode), hintSize); + break; + case NPY_INT16: + IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int16_t *)pDataOut2, pDataOut1, + sizeType1, HASH_MODE(hashMode), hintSize); + break; + CASE_NPY_INT32: + IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int32_t *)pDataOut2, pDataOut1, + sizeType1, HASH_MODE(hashMode), hintSize); + break; + CASE_NPY_INT64: + IsMemberHash32(arraySize1, pDataIn1, arraySize2, pDataIn2, (int64_t *)pDataOut2, pDataOut1, + sizeType1, HASH_MODE(hashMode), hintSize); + break; + } } } } diff --git a/src/flat_hash_map.cpp b/src/flat_hash_map.cpp index fac123e0..7daa967c 100644 --- a/src/flat_hash_map.cpp +++ b/src/flat_hash_map.cpp @@ -6,13 +6,43 @@ #include "absl/container/flat_hash_map.h" -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; -template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; +template struct fhm_hasher; diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index a91dd381..b745d3a2 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -59,21 +59,21 @@ template struct type_deducer; -template +template struct fhm_hasher { // std::pmr::monotonic_buffer_resource mono{ backing.data(), backing.capacity() }; // std::pmr::unsynchronized_pool_resource pool{ &mono }; // std::pmr::memory_resource * resource{&mono}; - oneapi::tbb::concurrent_hash_map hasher; -// absl::flat_hash_map hasher{}; -// absl::flat_hash_map hasher; +// absl::flat_hash_map hasher{}; +// absl::flat_hash_map, // absl::container_internal::hash_default_eq, -// std::pmr::polymorphic_allocator>> hasher{resource}; -// std::pmr::unordered_map> hasher{resource}; -// std::unordered_map hasher{}; +// std::pmr::polymorphic_allocator>> hasher{resource}; +// std::pmr::unordered_map> hasher{resource}; +// std::unordered_map hasher{}; KeyT const * data_series_p{}; dll_export fhm_hasher() {} @@ -94,10 +94,10 @@ struct fhm_hasher data_series_p = hash_list_p; - for (size_t i{ 0 }; i != hint_size; ++i) + for (IndexT i{ 0 }; i != hint_size; ++i) { // hasher.emplace(hash_list_p[i], i); - typename oneapi::tbb::concurrent_hash_map::accessor a; + typename oneapi::tbb::concurrent_hash_map::accessor a; hasher.insert(a, hash_list_p[i]); a->second = i; } @@ -105,7 +105,7 @@ struct fhm_hasher int64_t find(KeyT const * key) const noexcept { - typename oneapi::tbb::concurrent_hash_map::const_accessor finder; + typename oneapi::tbb::concurrent_hash_map::const_accessor finder; bool found = hasher.find(finder, *key); // bool found = hasher.contains(*key); // bool found = hasher.find(*key) != hasher.end(); @@ -115,12 +115,12 @@ struct fhm_hasher } }; -template +template struct is_member_check { - fhm_hasher mutable hash{}; + fhm_hasher mutable hash{}; - int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, int64_t * output_p, + int operator()(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, IndexT * output_p, int8_t * bool_out_p) { KeyT const * typed_needles_p{ reinterpret_cast(needles_p) }; @@ -129,7 +129,7 @@ struct is_member_check for (ptrdiff_t elem{ 0 }; elem != needles_size; ++elem) { - int64_t found_at = hash.find(typed_needles_p + elem); + IndexT found_at = static_cast(hash.find(typed_needles_p + elem)); if (found_at >= 0) { *(output_p + elem) = found_at; @@ -145,33 +145,56 @@ struct is_member_check } }; -dll_export int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, - int64_t * output_p, int8_t * bool_out_p, is_member_allowed_types_t sample_value) +template +dll_export inline int is_member(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, + out_t * output_p, int8_t * bool_out_p, is_member_allowed_types_t sample_value) { auto hasher_calls = overload{ [=](uint8_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](uint16_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](uint32_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](uint64_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](int8_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](int16_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](int32_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](int64_t) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](float) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, [=](double) -> int - { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, + { return is_member_check{}(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p); }, }; return std::visit(hasher_calls, sample_value); } +template +inline int is_member_shim(size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, + out_t * output_p, int8_t * bool_out_p, data_trait_t const * data_p) +{ + if ( data_p ) + { + using T = typename data_trait_t::data_type; + T const sample_value{}; + return is_member( needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p, sample_value ); + } + else + { + return 0; + } +} + +template< typename variant_t, typename out_t, size_t... Is> +dll_export inline int is_member_for_type( size_t needles_size, char const * needles_p, size_t haystack_size, char const * haystack_p, + out_t * output_p, int8_t * bool_out_p, variant_t data_type_traits, std::index_sequence) +{ + return (is_member_shim(needles_size, needles_p, haystack_size, haystack_p, output_p, bool_out_p, std::get_if(&data_type_traits)) + ...); +} #endif diff --git a/src/one_input.cpp b/src/one_input.cpp index a7528527..b7303d6b 100644 --- a/src/one_input.cpp +++ b/src/one_input.cpp @@ -197,6 +197,9 @@ namespace riptable_cpp case MATH_OPERATION::SQRT: retval.first = sqrt_op{}; break; + default: + retval.first = none_op{}; + break; } return retval; diff --git a/src/one_input_impl.h b/src/one_input_impl.h index ee6bc1a8..e3b9f1d0 100644 --- a/src/one_input_impl.h +++ b/src/one_input_impl.h @@ -564,6 +564,14 @@ namespace riptable_cpp } } + template + decltype(auto) calculate(char const * in_p, none_op const * requested_op, calculation_t const * in_type, + wide_ops_t wide_ops) + { + using T = typename calculation_t::data_type const; + return T{}; + } + // numpy standard is to treat stride as bytes template void perform_operation(char const * in_p, char * out_p, ptrdiff_t & starting_element, int64_t const in_array_stride, diff --git a/src/operation_traits.h b/src/operation_traits.h index 2bd0421a..aa2844e8 100644 --- a/src/operation_traits.h +++ b/src/operation_traits.h @@ -191,11 +191,16 @@ namespace riptable_cpp static constexpr bool value_return = true; using simd_implementation = std::false_type; }; + struct none_op + { + static constexpr bool value_return = true; + using simd_implementation = std::false_type; + }; using operation_t = std::variant; + exp_op, exp2_op, cbrt_op, tan_op, cos_op, sin_op, signbit_op, none_op>; } // namespace riptable_cpp diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp index 0cb6351e..2d88a9c0 100644 --- a/test/riptide_test/flat_hash_map_tests.cpp +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -26,7 +26,7 @@ namespace { boost::ut::log << "Data Type: " << reflection::type_name() << " "; std::array data{}; - if constexpr( std::is_floating_point_v ) + if constexpr (std::is_floating_point_v) { std::uniform_real_distribution dist(0, std::numeric_limits::max()); std::generate(std::begin(data), std::end(data), [&] { return dist(engine); }); @@ -37,33 +37,75 @@ namespace std::generate(std::begin(data), std::end(data), [&] { return dist(engine); }); } - fhm_hasher hash{}; + fhm_hasher hash{}; - hash.make_hash(data.size(), reinterpret_cast< char const * >(data.data()), 0); + hash.make_hash(data.size(), reinterpret_cast(data.data()), 0); - if constexpr( not std::is_same_v> ) + if constexpr (not std::is_same_v>) { - expect( hash.hasher.find(data[0])->second == 0u); - expect(hash.hasher.find(data[1])->second == 1u); - expect(hash.hasher.find(data[2])->second == 2u); - expect(hash.hasher.find(data[3])->second == 3u); - expect(hash.hasher.find(data[4])->second == 4u); - expect(hash.hasher.find(data[5])->second == 5u); - expect(hash.hasher.find(data[6])->second == 6u); - expect(hash.hasher.find(data[7])->second == 7u); - expect(hash.hasher.find(data[8])->second == 8u); - expect(hash.hasher.find(data[9])->second == 9u); - expect(hash.hasher.find(data[10])->second == 10u); - expect(hash.hasher.find(data[11])->second == 11u); - expect(hash.hasher.find(data[12])->second == 12u); - expect(hash.hasher.find(data[13])->second == 13u); - expect(hash.hasher.find(data[14])->second == 14u); - expect(hash.hasher.find(data[15])->second == 15u); - expect(hash.hasher.find(data[16])->second == 16u); - expect(hash.hasher.find(data[17])->second == 17u); + expect(hash.hasher.find(data[0])->second == 0u); + expect(hash.hasher.find(data[1])->second == 1u); + expect(hash.hasher.find(data[2])->second == 2u); + expect(hash.hasher.find(data[3])->second == 3u); + expect(hash.hasher.find(data[4])->second == 4u); + expect(hash.hasher.find(data[5])->second == 5u); + expect(hash.hasher.find(data[6])->second == 6u); + expect(hash.hasher.find(data[7])->second == 7u); + expect(hash.hasher.find(data[8])->second == 8u); + expect(hash.hasher.find(data[9])->second == 9u); + expect(hash.hasher.find(data[10])->second == 10u); + expect(hash.hasher.find(data[11])->second == 11u); + expect(hash.hasher.find(data[12])->second == 12u); + expect(hash.hasher.find(data[13])->second == 13u); + expect(hash.hasher.find(data[14])->second == 14u); + expect(hash.hasher.find(data[15])->second == 15u); + expect(hash.hasher.find(data[16])->second == 16u); + expect(hash.hasher.find(data[17])->second == 17u); + } + else + { + using const_acc = typename oneapi::tbb::concurrent_hash_map::const_accessor; + const oneapi::tbb::concurrent_hash_map & const_map = hash.hasher; + const_acc ca{}; + expect(hash.hasher.find(ca, data[0])); + expect(ca->second == 0_i); + expect(hash.hasher.find(ca, data[1])); + expect(ca->second == 1_i); + expect(hash.hasher.find(ca, data[2])); + expect(ca->second == 2_i); + expect(hash.hasher.find(ca, data[3])); + expect(ca->second == 3_i); + expect(hash.hasher.find(ca, data[4])); + expect(ca->second == 4_i); + expect(hash.hasher.find(ca, data[5])); + expect(ca->second == 5_i); + expect(hash.hasher.find(ca, data[6])); + expect(ca->second == 6_i); + expect(hash.hasher.find(ca, data[7])); + expect(ca->second == 7_i); + expect(hash.hasher.find(ca, data[8])); + expect(ca->second == 8_i); + expect(hash.hasher.find(ca, data[9])); + expect(ca->second == 9_i); + expect(hash.hasher.find(ca, data[10])); + expect(ca->second == 10_i); + expect(hash.hasher.find(ca, data[11])); + expect(ca->second == 11_i); + expect(hash.hasher.find(ca, data[12])); + expect(ca->second == 12_i); + expect(hash.hasher.find(ca, data[13])); + expect(ca->second == 13_i); + expect(hash.hasher.find(ca, data[14])); + expect(ca->second == 14_i); + expect(hash.hasher.find(ca, data[15])); + expect(ca->second == 15_i); + expect(hash.hasher.find(ca, data[16])); + expect(ca->second == 16_i); + expect(hash.hasher.find(ca, data[17])); + expect(ca->second == 17_i); } } | std::tuple{}; - + "is_member_uint64"_test = [&] { std::vector haystack(10); From a3d45385032dad52e243d97e847d30be928ec67f Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Wed, 27 Apr 2022 14:54:04 -0400 Subject: [PATCH 09/10] Pytest now runs clean (or as clean as it ever does), still single-threaded though --- src/flat_hash_map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flat_hash_map.h b/src/flat_hash_map.h index b745d3a2..8d8cb787 100644 --- a/src/flat_hash_map.h +++ b/src/flat_hash_map.h @@ -137,7 +137,7 @@ struct is_member_check } else { - *(output_p + elem) = -1; + *(output_p + elem) = std::numeric_limits::min(); *(bool_out_p + elem) = 0; } } From 238e7b73861932275058a17eeff20d2fc0b60126 Mon Sep 17 00:00:00 2001 From: Staffan Tjernstrom <8728287+staffantj@users.noreply.github.com> Date: Wed, 27 Apr 2022 15:36:15 -0400 Subject: [PATCH 10/10] Let the unit tests catch up with the updated invalid index value change --- test/riptide_test/flat_hash_map_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/riptide_test/flat_hash_map_tests.cpp b/test/riptide_test/flat_hash_map_tests.cpp index 2d88a9c0..6b55625a 100644 --- a/test/riptide_test/flat_hash_map_tests.cpp +++ b/test/riptide_test/flat_hash_map_tests.cpp @@ -121,7 +121,7 @@ namespace expect( output[ 1 ] == 9_i ); expect( output[ 2 ] == 0_i ); expect( output[ 3 ] == 5_i ); - expect( output[ 4 ] == -1_i ); + expect( output[ 4 ] < -9'223'372'036'854'775'807_ll ); expect( bools[ 0 ] == 1_i ); expect( bools[ 1 ] == 1_i );