This commit is contained in:
Terry Kim 2017-04-25 22:32:00 -07:00 коммит произвёл GitHub
Родитель a2337ff44e
Коммит 3b1648fc1a
26 изменённых файлов: 170 добавлений и 110 удалений

59
CMakeLists.txt Normal file
Просмотреть файл

@ -0,0 +1,59 @@
cmake_minimum_required(VERSION 2.8.12)
project(L4 CXX)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
message(FATAL_ERROR "Use provided solution file.")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(-std=c++14)
add_definitions(-DBOOST_TEST_DYN_LINK)
endif()
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.45.0 COMPONENTS unit_test_framework REQUIRED)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/inc
${CMAKE_CURRENT_SOURCE_DIR}/inc/L4)
# Need to expand the followings for installation.
set(L4_HEADERS
inc/L4/Utils/MurmurHash3.h)
set(L4_SOURCES
src/EpochActionManager.cpp
src/MurmurHash3.cpp
src/PerfLogger.cpp)
add_library(L4
${L4_SOURCES}
${L4_HEADERS})
enable_testing()
add_executable(L4.UnitTests
Unittests/CacheHashTableTest.cpp
Unittests/EpochManagerTest.cpp
Unittests/HashTableManagerTest.cpp
Unittests/HashTableRecordTest.cpp
Unittests/HashTableServiceTest.cpp
Unittests/PerfInfoTest.cpp
Unittests/ReadWriteHashTableSerializerTest.cpp
Unittests/ReadWriteHashTableTest.cpp
Unittests/SettingAdapterTest.cpp
Unittests/Utils.cpp
Unittests/UtilsTest.cpp
Unittests/Main.cpp)
target_link_libraries(L4.UnitTests
L4
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
-lpthread)
#${CMAKE_THREAD_LIBS_INIT})
add_test(NAME L4UnitTests COMMAND L4.UnitTests)

Просмотреть файл

@ -1,4 +1,6 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <sstream>
#include "Utils.h"
#include "Mocks.h"
#include "CheckedAllocator.h"
@ -6,9 +8,6 @@
#include "L4/HashTable/Cache/Metadata.h"
#include "L4/HashTable/Cache/HashTable.h"
#include <algorithm>
#include <sstream>
namespace L4
{
namespace UnitTests

Просмотреть файл

@ -1,9 +1,9 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include <mutex>
#include <condition_variable>
#include "Utils.h"
#include "L4/Interprocess/Connection/ConnectionMonitor.h"
#include "L4/Interprocess/Connection/EndPointInfoUtils.h"
#include <mutex>
#include <condition_variable>
namespace L4
{

Просмотреть файл

@ -1,11 +1,11 @@
#include "stdafx.h"
#include <atomic>
#include <boost/test/unit_test.hpp>
#include "Utils.h"
#include "L4/Epoch/EpochQueue.h"
#include "L4/Epoch/EpochActionManager.h"
#include "L4/LocalMemory/EpochManager.h"
#include "L4/Log/PerfCounter.h"
#include "L4/Utils/Lock.h"
#include <atomic>
namespace L4
{
@ -150,7 +150,7 @@ BOOST_AUTO_TEST_CASE(EpochManagerTest)
EpochManagerConfig(100000U, std::chrono::milliseconds(5U), 1U),
perfData);
std::atomic<bool> isActionCalled = false;
std::atomic<bool> isActionCalled{ false };
auto action = [&]() { isActionCalled = true; };
auto epochCounterReferenced = epochManager.GetEpochRefManager().AddRef();

Просмотреть файл

@ -1,4 +1,4 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include "Utils.h"
#include "Mocks.h"
#include "L4/HashTable/Config.h"

Просмотреть файл

@ -1,9 +1,9 @@
#include "stdafx.h"
#include "L4/HashTable/Common/Record.h"
#include "Utils.h"
#include <boost/test/unit_test.hpp>
#include <boost/optional.hpp>
#include <string>
#include <vector>
#include "L4/HashTable/Common/Record.h"
#include "Utils.h"
namespace L4
{
@ -30,7 +30,7 @@ protected:
Validate(
RecordSerializer{
isFixedKey ? static_cast<RecordSerializer::KeySize>(key.size()) : 0U,
isFixedKey ? static_cast<RecordSerializer::KeySize>(key.size()) : std::uint16_t(0),
isFixedValue ? static_cast<RecordSerializer::ValueSize>(value.size()) : 0U,
useMetaValue ? static_cast<RecordSerializer::ValueSize>(metaValue.size()) : 0U },
key,

Просмотреть файл

@ -1,9 +1,9 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include <utility>
#include <vector>
#include "Mocks.h"
#include "Utils.h"
#include "L4/LocalMemory/HashTableService.h"
#include <vector>
#include <utility>
namespace L4
{

Просмотреть файл

@ -1,5 +1,2 @@
#pragma once
#define BOOST_TEST_MODULE L4Unittests
#include <boost/test/unit_test.hpp>

Просмотреть файл

@ -1,6 +1,5 @@
#pragma once
#include "stdafx.h"
#include "L4/Epoch/IEpochActionManager.h"
#include "L4/Log/PerfLogger.h"

Просмотреть файл

@ -1,6 +1,6 @@
#include "stdafx.h"
#include "L4/Log/PerfLogger.h"
#include <boost/test/unit_test.hpp>
#include <limits>
#include "L4/Log/PerfLogger.h"
namespace L4
{

Просмотреть файл

@ -1,13 +1,13 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include <string>
#include <sstream>
#include <vector>
#include "Utils.h"
#include "Mocks.h"
#include "L4/HashTable/ReadWrite/HashTable.h"
#include "L4/HashTable/ReadWrite/Serializer.h"
#include "L4/Log/PerfCounter.h"
#include "L4/LocalMemory/Memory.h"
#include <string>
#include <sstream>
#include <vector>
namespace L4
{

Просмотреть файл

@ -1,4 +1,4 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include "Utils.h"
#include "Mocks.h"
#include "CheckedAllocator.h"

Просмотреть файл

@ -1,4 +1,4 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include "L4/HashTable/Common/SettingAdapter.h"
#include "L4/HashTable/Common/Record.h"
#include "CheckedAllocator.h"

Просмотреть файл

@ -40,7 +40,6 @@
</Lib>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)Unittests;$(SolutionDir)inc;$(SolutionDir)inc/L4;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalOptions>/Zm136 %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
@ -60,16 +59,13 @@
<ClCompile Include="PerfInfoTest.cpp" />
<ClCompile Include="ReadWriteHashTableTest.cpp" />
<ClCompile Include="SettingAdapterTest.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Main.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="UtilsTest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CheckedAllocator.h" />
<ClInclude Include="Mocks.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="Utils.h" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -11,9 +11,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HashTableServiceTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -50,6 +47,9 @@
<ClCompile Include="ConnectionMonitorTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Utils.h">
@ -61,9 +61,6 @@
<ClInclude Include="CheckedAllocator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

Просмотреть файл

@ -1,4 +1,4 @@
#include "stdafx.h"
#include <boost/test/unit_test.hpp>
#include "Utils.h"
namespace L4

Просмотреть файл

@ -3,6 +3,7 @@
#include <array>
#include <cstdint>
#include <string>
#include <string.h>
#include <vector>
#include "L4/Log/PerfCounter.h"
#include "L4/Utils/Exception.h"

Просмотреть файл

@ -1,6 +1,6 @@
#include "stdafx.h"
#include "L4/Utils/Math.h"
#include <boost/test/unit_test.hpp>
#include <array>
#include "L4/Utils/Math.h"
namespace L4
{

Просмотреть файл

@ -1 +0,0 @@
#include "stdafx.h"

Просмотреть файл

@ -32,7 +32,7 @@ public:
: m_frontIndex{ epochCounter }
, m_backIndex{ epochCounter }
, m_mutexForBackIndex{}
, m_refCounts{ queueSize, Allocator::rebind<RefCount>::other(allocator) }
, m_refCounts{ queueSize, typename Allocator::template rebind<RefCount>::other(allocator) }
{
if (queueSize == 0U)
{
@ -81,7 +81,7 @@ public:
std::uint64_t AddRef()
{
// The synchronization is needed for EpochCounterManager::AddNewEpoch().
EpochQueue::SharableLock lock(m_epochQueue.m_mutexForBackIndex);
typename EpochQueue::SharableLock lock(m_epochQueue.m_mutexForBackIndex);
++m_epochQueue.m_refCounts[m_epochQueue.m_backIndex % m_epochQueue.m_refCounts.size()];
@ -126,7 +126,7 @@ public:
void AddNewEpoch()
{
// The synchronization is needed for EpochRefManager::AddRef().
EpochQueue::ExclusiveLock lock(m_epochQueue.m_mutexForBackIndex);
typename EpochQueue::ExclusiveLock lock(m_epochQueue.m_mutexForBackIndex);
++m_epochQueue.m_backIndex;

Просмотреть файл

@ -3,6 +3,7 @@
#include <chrono>
#include <cstdint>
#include <mutex>
#include <stdexcept>
#include "detail/ToRawPointer.h"
#include "Epoch/IEpochActionManager.h"
#include "HashTable/IHashTable.h"
@ -28,17 +29,21 @@ public:
using Base = ReadWrite::ReadOnlyHashTable<Allocator>;
using HashTable = typename Base::HashTable;
using Key = typename Base::Key;
using Value = typename Base::Value;
using IIteratorPtr = typename Base::IIteratorPtr;
class Iterator;
ReadOnlyHashTable(
HashTable& hashTable,
std::chrono::seconds recordTimeToLive)
: Base{
: Base(
hashTable,
RecordSerializer{
hashTable.m_setting.m_fixedKeySize,
hashTable.m_setting.m_fixedValueSize,
Metadata::c_metaDataSize } }
Metadata::c_metaDataSize })
, m_recordTimeToLive{ recordTimeToLive }
{}
@ -47,7 +52,7 @@ public:
const auto status = GetInternal(key, value);
// Note that the following const_cast is safe and necessary to update cache hit information.
const_cast<HashTablePerfData&>(GetPerfData()).Increment(
const_cast<HashTablePerfData&>(this->GetPerfData()).Increment(
status
? HashTablePerfCounter::CacheHitCount
: HashTablePerfCounter::CacheMissCount);
@ -58,10 +63,10 @@ public:
virtual IIteratorPtr GetIterator() const override
{
return std::make_unique<Iterator>(
m_hashTable,
m_recordSerializer,
this->m_hashTable,
this->m_recordSerializer,
m_recordTimeToLive,
GetCurrentEpochTime());
this->GetCurrentEpochTime());
}
ReadOnlyHashTable(const ReadOnlyHashTable&) = delete;
@ -80,7 +85,7 @@ protected:
// If the record with the given key is found, check if the record is expired or not.
// Note that the following const_cast is safe and necessary to update the access status.
Metadata metaData{ const_cast<std::uint32_t*>(reinterpret_cast<const std::uint32_t*>(value.m_data)) };
if (metaData.IsExpired(GetCurrentEpochTime(), m_recordTimeToLive))
if (metaData.IsExpired(this->GetCurrentEpochTime(), m_recordTimeToLive))
{
return false;
}
@ -101,27 +106,27 @@ template <typename Allocator, typename Clock>
class ReadOnlyHashTable<Allocator, Clock>::Iterator : public Base::Iterator
{
public:
using Base = typename Base::Iterator;
using BaseIterator = typename Base::Iterator;
Iterator(
const HashTable& hashTable,
const RecordSerializer& recordDeserializer,
std::chrono::seconds recordTimeToLive,
std::chrono::seconds currentEpochTime)
: Base(hashTable, recordDeserializer)
: BaseIterator(hashTable, recordDeserializer)
, m_recordTimeToLive{ recordTimeToLive }
, m_currentEpochTime{ currentEpochTime }
{}
Iterator(Iterator&& other)
: Base(std::move(other))
: BaseIterator(std::move(other))
, m_recordTimeToLive{ std::move(other.m_recordTimeToLive) }
, m_currentEpochTime{ std::move(other.m_currentEpochTime) }
{}
bool MoveNext() override
{
if (!Base::MoveNext())
if (!BaseIterator::MoveNext())
{
return false;
}
@ -131,20 +136,20 @@ public:
const Metadata metaData{
const_cast<std::uint32_t*>(
reinterpret_cast<const std::uint32_t*>(
Base::GetValue().m_data)) };
BaseIterator::GetValue().m_data)) };
if (!metaData.IsExpired(m_currentEpochTime, m_recordTimeToLive))
{
return true;
}
} while (Base::MoveNext());
} while (BaseIterator::MoveNext());
return false;
}
Value GetValue() const override
{
auto value = Base::GetValue();
auto value = BaseIterator::GetValue();
value.m_data += Metadata::c_metaDataSize;
value.m_size -= Metadata::c_metaDataSize;
@ -174,6 +179,10 @@ public:
using WritableBase = typename ReadWrite::WritableHashTable<Allocator>;
using HashTable = typename ReadOnlyBase::HashTable;
using Key = typename ReadOnlyBase::Key;
using Value = typename ReadOnlyBase::Value;
using ISerializerPtr = typename WritableBase::ISerializerPtr;
WritableHashTable(
HashTable& hashTable,
IEpochActionManager& epochManager,
@ -210,7 +219,7 @@ public:
virtual ISerializerPtr GetSerializer() const override
{
throw std::exception("Not implemented yet.");
throw std::runtime_error("Not implemented yet.");
}
private:
@ -219,13 +228,13 @@ private:
void EvictBasedOnTime(const Key& key)
{
const auto bucketIndex = GetBucketInfo(key).first;
const auto bucketIndex = this->GetBucketInfo(key).first;
auto* entry = &m_hashTable.m_buckets[bucketIndex];
auto* entry = &(this->m_hashTable.m_buckets[bucketIndex]);
const auto curEpochTime = GetCurrentEpochTime();
const auto curEpochTime = this->GetCurrentEpochTime();
HashTable::Lock lock{ m_hashTable.GetMutex(bucketIndex) };
typename HashTable::Lock lock{ this->m_hashTable.GetMutex(bucketIndex) };
while (entry != nullptr)
{
@ -238,12 +247,12 @@ private:
const Metadata metadata{
const_cast<std::uint32_t*>(
reinterpret_cast<const std::uint32_t*>(
m_recordSerializer.Deserialize(*data).m_value.m_data)) };
this->m_recordSerializer.Deserialize(*data).m_value.m_data)) };
if (metadata.IsExpired(curEpochTime, m_recordTimeToLive))
if (metadata.IsExpired(curEpochTime, this->m_recordTimeToLive))
{
WritableBase::Remove(*entry, i);
m_hashTable.m_perfData.Increment(HashTablePerfCounter::EvictedRecordsCount);
this->m_hashTable.m_perfData.Increment(HashTablePerfCounter::EvictedRecordsCount);
}
}
}
@ -272,12 +281,12 @@ private:
return;
}
const auto curEpochTime = GetCurrentEpochTime();
const auto curEpochTime = this->GetCurrentEpochTime();
// The max number of iterations we are going through per eviction is twice the number
// of buckets so that it can clear the access status. Note that this is the worst
// case scenario and the eviction process should exit much quicker in a normal case.
auto& buckets = m_hashTable.m_buckets;
auto& buckets = this->m_hashTable.m_buckets;
std::uint64_t numIterationsRemaining = buckets.size() * 2U;
while (numBytesToFree > 0U && numIterationsRemaining-- > 0U)
@ -287,8 +296,8 @@ private:
// Lock the bucket since another thread can bypass Evict() since TotalDataSize can
// be updated before the lock on m_evictMutex is released.
HashTable::UniqueLock lock{ m_hashTable.GetMutex(currentBucketIndex) };
HashTable::Entry* entry = &bucket;
typename HashTable::UniqueLock lock{ this->m_hashTable.GetMutex(currentBucketIndex) };
typename HashTable::Entry* entry = &bucket;
while (entry != nullptr)
{
@ -298,7 +307,7 @@ private:
if (data != nullptr)
{
const auto record = m_recordSerializer.Deserialize(*data);
const auto record = this->m_recordSerializer.Deserialize(*data);
const auto& value = record.m_value;
Metadata metadata{
@ -309,7 +318,7 @@ private:
// Evict this record if
// 1: the record is expired, or
// 2: the entry is not recently accessed (and unset the access bit if set).
if (metadata.IsExpired(curEpochTime, m_recordTimeToLive)
if (metadata.IsExpired(curEpochTime, this->m_recordTimeToLive)
|| !metadata.UpdateAccessStatus(false))
{
const auto numBytesFreed = record.m_key.m_size + value.m_size;
@ -317,7 +326,7 @@ private:
WritableBase::Remove(*entry, i);
m_hashTable.m_perfData.Increment(HashTablePerfCounter::EvictedRecordsCount);
this->m_hashTable.m_perfData.Increment(HashTablePerfCounter::EvictedRecordsCount);
}
}
}
@ -357,15 +366,15 @@ private:
RecordBuffer* CreateRecordBuffer(const Key& key, const Value& value)
{
const auto bufferSize = m_recordSerializer.CalculateBufferSize(key, value);
const auto bufferSize = this->m_recordSerializer.CalculateBufferSize(key, value);
auto buffer = Detail::to_raw_pointer(
m_hashTable.GetAllocator<std::uint8_t>().allocate(bufferSize));
this->m_hashTable.template GetAllocator<std::uint8_t>().allocate(bufferSize));
std::uint32_t metaDataBuffer;
Metadata{ &metaDataBuffer, GetCurrentEpochTime() };
Metadata{ &metaDataBuffer, this->GetCurrentEpochTime() };
// 4-byte Metadata is inserted between key and value buffer.
return m_recordSerializer.Serialize(
return this->m_recordSerializer.Serialize(
key,
value,
Value{ reinterpret_cast<std::uint8_t*>(&metaDataBuffer), sizeof(metaDataBuffer) },

Просмотреть файл

@ -385,7 +385,7 @@ protected:
curEntry->m_next.Store(
new (Detail::to_raw_pointer(
this->m_hashTable.template GetAllocator<typename HashTable::Entry>().allocate(1U)))
HashTable::Entry(),
typename HashTable::Entry(),
std::memory_order_release);
stat.m_isNewEntryAdded = true;

Просмотреть файл

@ -103,10 +103,10 @@ public:
{
DeserializerHelper helper(stream);
HashTable::Setting setting;
typename HashTable::Setting setting;
helper.Deserialize(setting);
auto hashTable{ memory.MakeUnique<HashTable>(
auto hashTable{ memory.template MakeUnique<HashTable>(
setting,
memory.GetAllocator()) };

Просмотреть файл

@ -9,18 +9,18 @@ namespace L4
namespace LocalMemory
{
class Context : private EpochRefPolicy<EpochManager::EpochRefManager>
class Context : private EpochRefPolicy<EpochManager::TheEpochRefManager>
{
public:
Context(
HashTableManager& hashTableManager,
EpochManager::EpochRefManager& epochRefManager)
: EpochRefPolicy<EpochManager::EpochRefManager>(epochRefManager)
EpochManager::TheEpochRefManager& epochRefManager)
: EpochRefPolicy<EpochManager::TheEpochRefManager>(epochRefManager)
, m_hashTableManager{ hashTableManager }
{}
Context(Context&& context)
: EpochRefPolicy<EpochManager::EpochRefManager>(std::move(context))
: EpochRefPolicy<EpochManager::TheEpochRefManager>(std::move(context))
, m_hashTableManager{ context.m_hashTableManager }
{}

Просмотреть файл

@ -20,11 +20,11 @@ namespace LocalMemory
class EpochManager : public IEpochActionManager
{
public:
using EpochQueue = EpochQueue<
using TheEpochQueue = EpochQueue<
boost::shared_lock_guard<Utils::ReaderWriterLockSlim>,
std::lock_guard<Utils::ReaderWriterLockSlim>>;
using EpochRefManager = EpochRefManager<EpochQueue>;
using TheEpochRefManager = EpochRefManager<TheEpochQueue>;
EpochManager(
const EpochManagerConfig& config,
@ -47,7 +47,7 @@ public:
}}
{}
EpochRefManager& GetEpochRefManager()
TheEpochRefManager& GetEpochRefManager()
{
return m_epochRefManager;
}
@ -62,7 +62,7 @@ public:
EpochManager& operator=(const EpochManager&) = delete;
private:
using EpochCounterManager = EpochCounterManager<EpochQueue>;
using TheEpochCounterManager = EpochCounterManager<TheEpochQueue>;
using ProcessingThread = Utils::RunningThread<std::function<void()>>;
@ -100,16 +100,20 @@ private:
EpochManagerConfig m_config;
// The global current epoch counter.
#if defined(_MSC_VER)
std::atomic_uint64_t m_currentEpochCounter;
#else
std::atomic<std::uint64_t> m_currentEpochCounter;
#endif
// Epoch queue.
EpochQueue m_epochQueue;
TheEpochQueue m_epochQueue;
// Handles adding/decrementing ref counts.
EpochRefManager m_epochRefManager;
TheEpochRefManager m_epochRefManager;
// Handles adding new epoch and finding the epoch counts that have zero ref counts.
EpochCounterManager m_epochCounterManager;
TheEpochCounterManager m_epochCounterManager;
// Handles registering/performing actions.
EpochActionManager m_epochActionManager;

Просмотреть файл

@ -42,8 +42,8 @@ public:
using namespace HashTable;
using InternalHashTable = ReadWrite::WritableHashTable<Allocator>::HashTable;
using Memory = LocalMemory::Memory<Allocator>;
using InternalHashTable = typename ReadWrite::WritableHashTable<Allocator>::HashTable;
using Memory = typename LocalMemory::Memory<Allocator>;
Memory memory{ allocator };
@ -53,8 +53,8 @@ public:
Deserialize(
memory,
*(serializerConfig->m_stream))
: memory.MakeUnique<InternalHashTable>(
InternalHashTable::Setting{
: memory.template MakeUnique<InternalHashTable>(
typename InternalHashTable::Setting{
config.m_setting.m_numBuckets,
(std::max)(config.m_setting.m_numBucketsPerMutex.get_value_or(1U), 1U),
config.m_setting.m_fixedKeySize.get_value_or(0U),