Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1ee6db9
navy: offload checksums to Intel DSA via the DTO library
byrnedj Sep 23, 2026
2a51bd5
navy: DSA copy offload APIs, wait policies and device-truthful self-c…
byrnedj Sep 23, 2026
3277a9e
navy: offload the BlockCache region-flush copy to DSA; read gate and …
byrnedj Sep 23, 2026
bf4fde6
nvmcache: optional DSA copy-out for flash hits
byrnedj Sep 23, 2026
68c36fd
cachebench: DSA offload knobs and counters; harness updates
byrnedj Sep 23, 2026
454f95b
navy: default checksum offload gate 16 KiB
byrnedj Sep 25, 2026
f33641e
run_dsa_cachebench: software arms do not need a DSA work queue
byrnedj Sep 28, 2026
bb055e9
navy: say why the checksum offload self-check failed
byrnedj Sep 28, 2026
e5ef7c4
run_dsa_cachebench: --offload none turns checksums and offloads off e…
byrnedj Sep 28, 2026
929d318
run_dsa_cachebench: PREFILL_NAVY=1 populates the Navy file before a run
byrnedj Sep 29, 2026
d43b985
navy: probe the flush-copy self-check at the region size
byrnedj Sep 29, 2026
4705fdb
cmake: pin DTO at cachelib-navy 991f4f9 (WQ max_transfer_size fallback)
byrnedj Sep 29, 2026
d2cb61a
cmake: pin DTO at cachelib-navy af84a04 (general batch descriptor API)
byrnedj Oct 1, 2026
29581fa
cmake: pin DTO at cachelib-navy 5b98ab5 (mem* recursion fix at -O2)
byrnedj Oct 1, 2026
3fd48bd
run_dsa_cachebench: DTO statistics off by default
byrnedj Oct 1, 2026
c765193
navy: BatchChecksumOp - many checksum / copy+checksum operations in o…
byrnedj Oct 1, 2026
c57c50b
navy: batch value verification in region reclaim/cleanup; optional in…
byrnedj Oct 1, 2026
2ac1e06
navy/cachebench: plumb checksumOffloadBatchReclaim, checksumOffloadBa…
byrnedj Oct 1, 2026
3175b6b
navy: checksumOffloadBatchReclaim off by default
byrnedj Oct 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions cachelib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)

option(BUILD_TESTS "If enabled, compile the tests." ON)

option(BUILD_WITH_DTO
"If enabled, build with the DTO library for Intel DSA offload support." OFF)


set(BIN_INSTALL_DIR bin CACHE STRING
"The subdirectory where binaries should be installed")
Expand Down Expand Up @@ -312,6 +315,57 @@ endfunction()
add_thrift_file(OBJECT_CACHE_PERSISTENCE
object_cache/persistence/persistent_data.thrift json)

if (BUILD_WITH_DTO)
# DSA offload runtime (DTO). Resolved here so every cachelib library can
# use it: DTO::dto is linked PUBLIC into cachelib_common (see
# common/CMakeLists.txt), the base library of every other cachelib target,
# which propagates the headers, the link dependency, and the
# CACHELIB_BUILD_WITH_DTO compile definition to the whole tree and to
# consumers of the installed package.
#
# The DSA checksum offload code requires a DTO with the caller-allocated
# async submit/poll API, the mixed-member batch API (dto_submit_batch) and
# the raw-CRC32C convention, which upstream intel/DTO does not have — so
# when no installed DTO cmake package is found, fetch and build the pinned
# revision in-tree rather than linking whatever libdto happens to be on
# the system.
set(CACHELIB_DTO_GIT_REPOSITORY "https://github.com/byrnedj/DTO.git"
CACHE STRING "Git repository for DTO when no installed copy is found")
set(CACHELIB_DTO_GIT_TAG "5b98ab5c33b33af814b839b7b4efd360a2e41889"
CACHE STRING "DTO revision to fetch when no installed copy is found")

find_package(DTO CONFIG QUIET)
if (DTO_FOUND)
message(STATUS "Using installed DTO package: ${DTO_DIR}")
else()
if (CMAKE_VERSION VERSION_LESS 3.14)
message(FATAL_ERROR
"BUILD_WITH_DTO needs an installed DTO cmake package or "
"CMake >= 3.14 to fetch one. Install DTO from "
"${CACHELIB_DTO_GIT_REPOSITORY} at ${CACHELIB_DTO_GIT_TAG}, "
"or upgrade CMake.")
endif()
# DTO links against libaccel-config and libnuma; check for them here so
# a missing system package fails at configure time with a clear message
# instead of at link time inside the fetched project.
find_library(ACCEL_CONFIG_LIBRARY accel-config)
find_library(NUMA_LIBRARY numa)
if (NOT ACCEL_CONFIG_LIBRARY OR NOT NUMA_LIBRARY)
message(FATAL_ERROR
"BUILD_WITH_DTO requires the libaccel-config and libnuma "
"development packages (e.g. apt install libaccel-config-dev "
"libnuma-dev)")
endif()
message(STATUS "DTO not installed; fetching "
"${CACHELIB_DTO_GIT_REPOSITORY} @ ${CACHELIB_DTO_GIT_TAG}")
include(FetchContent)
FetchContent_Declare(dto
GIT_REPOSITORY "${CACHELIB_DTO_GIT_REPOSITORY}"
GIT_TAG "${CACHELIB_DTO_GIT_TAG}")
FetchContent_MakeAvailable(dto)
endif()
endif()

add_subdirectory (common)
add_subdirectory (shm)
add_subdirectory (navy)
Expand Down
6 changes: 6 additions & 0 deletions cachelib/allocator/Cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,12 @@ void CacheBase::updateGlobalCacheStats(const std::string& statPrefix) const {
if (stats.nvmCacheEnabled) {
counters_.updateDelta(statPrefix + "nvm.alloc_attempts",
stats.numNvmAllocAttempts);
counters_.updateDelta(statPrefix + "nvm.copy_out_offloaded",
stats.numNvmCopyOutOffloaded);
counters_.updateDelta(statPrefix + "nvm.copy_out_offloaded_bytes",
stats.numNvmCopyOutOffloadedBytes);
counters_.updateDelta(statPrefix + "nvm.copy_out_fallbacks",
stats.numNvmCopyOutFallbacks);
counters_.updateDelta(statPrefix + "nvm.destructor_alloc",
stats.numNvmAllocForItemDestructor);
counters_.updateDelta(statPrefix + "nvm.destructor_alloc_errors",
Expand Down
7 changes: 5 additions & 2 deletions cachelib/allocator/CacheStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ struct SizeVerify {};
void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const {
#ifndef SKIP_SIZE_VERIFY
#ifdef __GLIBCXX__
#define EXPECTED_SIZE 16944
#define EXPECTED_SIZE 17200
#endif
#ifdef _LIBCPP_VERSION
#define EXPECTED_SIZE 16944
#define EXPECTED_SIZE 17200
#endif
SizeVerify<sizeof(Stats)> a = SizeVerify<EXPECTED_SIZE>{};
std::ignore = a;
Expand Down Expand Up @@ -106,6 +106,9 @@ void Stats::populateGlobalCacheStats(GlobalCacheStats& ret) const {
ret.numInsertOrReplaceInserted = numInsertOrReplaceInserted.get();
ret.numInsertOrReplaceReplaced = numInsertOrReplaceReplaced.get();
ret.numNvmAllocAttempts = numNvmAllocAttempts.get();
ret.numNvmCopyOutOffloaded = numNvmCopyOutOffloaded.get();
ret.numNvmCopyOutOffloadedBytes = numNvmCopyOutOffloadedBytes.get();
ret.numNvmCopyOutFallbacks = numNvmCopyOutFallbacks.get();
ret.numNvmAllocForItemDestructor = numNvmAllocForItemDestructor.get();
ret.numNvmItemDestructorAllocErrors = numNvmItemDestructorAllocErrors.get();

Expand Down
5 changes: 5 additions & 0 deletions cachelib/allocator/CacheStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,11 @@ struct GlobalCacheStats {
// attempts made from nvm cache to allocate an item for promotion
uint64_t numNvmAllocAttempts{0};

// flash-hit copy-outs performed by Intel DSA, bytes moved, CPU fallbacks
uint64_t numNvmCopyOutOffloaded{0};
uint64_t numNvmCopyOutOffloadedBytes{0};
uint64_t numNvmCopyOutFallbacks{0};

// attempts made from nvm cache to allocate an item for its destructor
uint64_t numNvmAllocForItemDestructor{0};
// heap allocate errors for item destructor
Expand Down
6 changes: 6 additions & 0 deletions cachelib/allocator/CacheStatsInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ struct Stats {
// attempts made from nvm cache to allocate an item for promotion
TLCounter numNvmAllocAttempts{0};

// flash-hit copy-outs (Navy buffer -> DRAM item) performed by Intel DSA,
// the bytes they moved, and offload attempts that fell back to the CPU
TLCounter numNvmCopyOutOffloaded{0};
TLCounter numNvmCopyOutOffloadedBytes{0};
TLCounter numNvmCopyOutFallbacks{0};

// attempts made from nvm cache to allocate an item for its destructor
TLCounter numNvmAllocForItemDestructor{0};
// heap allocate errors for item destructor
Expand Down
6 changes: 6 additions & 0 deletions cachelib/allocator/nvmcache/NavyConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ std::map<std::string, std::string> EnginesConfig::serialize() const {
folly::to<std::string>(blockCache().getNumInMemBuffers());
configMap["navyConfig::blockCacheDataChecksum"] =
blockCache().getDataChecksum() ? "true" : "false";
configMap["navyConfig::blockCacheChecksumOffload"] =
blockCache().getChecksumOffload() ? "true" : "false";
configMap["navyConfig::blockCacheChecksumOffloadMinSize"] =
folly::to<std::string>(blockCache().getChecksumOffloadMinSize());
configMap["navyConfig::blockCacheSegmentedFifoSegmentRatio"] =
folly::join(",", blockCache().getSFifoSegmentRatio());

Expand All @@ -335,6 +339,8 @@ std::map<std::string, std::string> EnginesConfig::serialize() const {
folly::to<std::string>(bigHash().getBucketBfSize());
configMap["navyConfig::bigHashSmallItemMaxSize"] =
folly::to<std::string>(bigHash().getSmallItemMaxSize());
configMap["navyConfig::bigHashChecksumOffload"] =
bigHash().getChecksumOffload() ? "true" : "false";
return configMap;
}

Expand Down
118 changes: 118 additions & 0 deletions cachelib/allocator/nvmcache/NavyConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,68 @@ class BlockCacheConfig {
return *this;
}

// Offload data checksumming (fused with the value copy on the write path)
// to Intel DSA via the DTO library. Requires data checksum to be enabled
// and CacheLib built with BUILD_WITH_DTO. @minSize is the minimum value
// size to use the offloaded path; smaller values are checksummed in
// software, since submitting and polling a descriptor costs about what the
// CPU needs to CRC 16-32 KiB (16 KiB measured as the break-even).
BlockCacheConfig& setChecksumOffload(bool checksumOffload,
uint32_t minSize = 16384) noexcept {
checksumOffload_ = checksumOffload;
checksumOffloadMinSize_ = minSize;
return *this;
}

// Separate minimum size for offloading checksum VERIFICATION on the read,
// reclaim and reinsertion paths; 0 = same as the write-path gate. Verifying
// has no CPU work to overlap with the accelerator, so its break-even is
// higher than the fused write.
BlockCacheConfig& setChecksumOffloadReadMinSize(uint32_t minSize) noexcept {
checksumOffloadReadMinSize_ = minSize;
return *this;
}

// Cache-control hint on the fused write-path copy (default true): steer the
// value bytes toward the CPU cache. Turn off when the next reader of the
// region buffer is the device (directFlush / flush copy offload).
BlockCacheConfig& setChecksumOffloadCacheControl(bool enable) noexcept {
checksumOffloadCacheControl_ = enable;
return *this;
}

// Region reclaim/cleanup verify values as DSA Batch descriptors (<= 64 per
// submission) instead of one descriptor each. Default false; needs the
// checksum offload. See BlockCache::Config::checksumOffloadBatchReclaim.
BlockCacheConfig& setChecksumOffloadBatchReclaim(bool enable) noexcept {
checksumOffloadBatchReclaim_ = enable;
return *this;
}

// Coalesce write-path fused copy+CRC descriptors across the fibers of a
// NavyThread (yielding only when siblings are runnable). Default false;
// measured neutral to slightly negative. See BlockCache::Config.
BlockCacheConfig& setChecksumOffloadBatch(bool enable) noexcept {
checksumOffloadBatch_ = enable;
return *this;
}

// Defer the checksum of values below the offload gate to region flush,
// where they are batched on DSA (implies skipInMemValueVerify). Default
// false. See BlockCache::Config::checksumDeferSmall.
BlockCacheConfig& setChecksumDeferSmall(bool enable) noexcept {
checksumDeferSmall_ = enable;
return *this;
}

// Lookups served from the in-memory region buffer skip the value checksum
// (the bytes never left DRAM; the header checksum still covers the
// descriptor). Default false. See BlockCache::Config::skipInMemValueVerify.
BlockCacheConfig& setSkipInMemValueVerify(bool enable) noexcept {
skipInMemValueVerify_ = enable;
return *this;
}

BlockCacheConfig& setPreciseRemove(bool preciseRemove) noexcept {
preciseRemove_ = preciseRemove;
return *this;
Expand All @@ -561,6 +623,17 @@ class BlockCacheConfig {
return *this;
}

// When directFlush is off: copy the region buffer into the flush write
// buffer with one Intel DSA Memory Move instead of memcpy. Requires
// CacheLib built with BUILD_WITH_DTO; falls back to memcpy if DSA is
// unusable. The write buffers are pooled, so after the first flush per
// buffer their pages are resident; a work queue with block-on-fault covers
// the first touch.
BlockCacheConfig& setFlushCopyOffload(bool enable) noexcept {
flushCopyOffload_ = enable;
return *this;
}

BlockCacheConfig& setAllocatorCount(uint32_t numAllocators) noexcept {
allocatorsPerPriority_ = {numAllocators};
return *this;
Expand Down Expand Up @@ -617,13 +690,33 @@ class BlockCacheConfig {

bool getDataChecksum() const { return dataChecksum_; }

bool getChecksumOffload() const { return checksumOffload_; }

uint32_t getChecksumOffloadMinSize() const { return checksumOffloadMinSize_; }

uint32_t getChecksumOffloadReadMinSize() const {
return checksumOffloadReadMinSize_;
}

bool getChecksumOffloadCacheControl() const {
return checksumOffloadCacheControl_;
}

bool getChecksumOffloadBatchReclaim() const {
return checksumOffloadBatchReclaim_;
}
bool getChecksumOffloadBatch() const { return checksumOffloadBatch_; }
bool getChecksumDeferSmall() const { return checksumDeferSmall_; }
bool getSkipInMemValueVerify() const { return skipInMemValueVerify_; }

uint64_t getSize() const { return size_; }

bool isRegionManagerFlushAsync() const { return regionManagerFlushAsync_; }

bool isRecoverEvictionPolicy() const { return recoverEvictionPolicy_; }

bool isDirectFlush() const { return directFlush_; }
bool isFlushCopyOffload() const { return flushCopyOffload_; }

bool isCombinedEntryBlockEnabled() const { return useCombinedEntryBlock_; }

Expand Down Expand Up @@ -660,6 +753,16 @@ class BlockCacheConfig {
uint32_t regionSize_{16 * 1024 * 1024};
// Whether enabling data checksum for Navy BlockCache.
bool dataChecksum_{true};
// Whether to offload data checksumming to Intel DSA (fused with the value
// copy on the write path), and the minimum value size to do so.
bool checksumOffload_{false};
uint32_t checksumOffloadMinSize_{16384};
uint32_t checksumOffloadReadMinSize_{0};
bool checksumOffloadCacheControl_{true};
bool checksumOffloadBatchReclaim_{false};
bool checksumOffloadBatch_{false};
bool checksumDeferSmall_{false};
bool skipInMemValueVerify_{false};
// Whether to remove an item by checking the key (true) or only the hash value
// (false).
bool preciseRemove_{false};
Expand All @@ -677,6 +780,9 @@ class BlockCacheConfig {
// Whether to write region buffer directly without intermediate copy.
bool directFlush_{false};

// Whether to do the flush copy (when not directFlush) on Intel DSA.
bool flushCopyOffload_{false};

// Whether to use Combined entry block (For index entries and small sized
// items).
// Only FixedSizeIndex will support this and it doesn't work with
Expand Down Expand Up @@ -739,6 +845,14 @@ class BigHashConfig {
return *this;
}

// Offload bucket checksumming to Intel DSA via the DTO library. Most
// beneficial with large buckets (16KB+). Requires CacheLib built with
// BUILD_WITH_DTO.
BigHashConfig& setChecksumOffload(bool checksumOffload) noexcept {
checksumOffload_ = checksumOffload;
return *this;
}

bool isBloomFilterEnabled() const { return bucketBfSize_ > 0; }

unsigned int getSizePct() const { return sizePct_; }
Expand All @@ -751,6 +865,8 @@ class BigHashConfig {

uint8_t getNumMutexesPower() const { return numMutexesPower_; }

bool getChecksumOffload() const { return checksumOffload_; }

private:
// Percentage of how much of the device out of all is given to BigHash
// engine in Navy, e.g. 50.
Expand All @@ -766,6 +882,8 @@ class BigHashConfig {
uint64_t smallItemMaxSize_{};
// numMutexes = 1 << numMutexesPower_.
uint8_t numMutexesPower_{14};
// Whether to offload bucket checksumming to Intel DSA.
bool checksumOffload_{false};
};

// Config for a pair of small,large engines.
Expand Down
15 changes: 15 additions & 0 deletions cachelib/allocator/nvmcache/NavySetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ uint64_t setupBigHash(const navy::BigHashConfig& bigHashConfig,
// Set number of mutexes from config
bigHash->setNumMutexesPower(bigHashConfig.getNumMutexesPower());

bigHash->setChecksumOffload(bigHashConfig.getChecksumOffload());

proto.setBigHash(std::move(bigHash), bigHashConfig.getSmallItemMaxSize());

if (bigHashCacheOffset <= bigHashStartOffsetLimit) {
Expand Down Expand Up @@ -196,6 +198,18 @@ uint64_t setupBlockCache(const navy::BlockCacheConfig& blockCacheConfig,
auto blockCache = cachelib::navy::createBlockCacheProto();
blockCache->setLayout(blockCacheOffset, blockCacheSize, regionSize);
blockCache->setChecksum(blockCacheConfig.getDataChecksum());
blockCache->setChecksumOffload(blockCacheConfig.getChecksumOffload(),
blockCacheConfig.getChecksumOffloadMinSize());
blockCache->setChecksumOffloadReadMinSize(
blockCacheConfig.getChecksumOffloadReadMinSize());
blockCache->setChecksumOffloadCacheControl(
blockCacheConfig.getChecksumOffloadCacheControl());
blockCache->setChecksumOffloadBatchReclaim(
blockCacheConfig.getChecksumOffloadBatchReclaim());
blockCache->setChecksumOffloadBatch(blockCacheConfig.getChecksumOffloadBatch());
blockCache->setChecksumDeferSmall(blockCacheConfig.getChecksumDeferSmall());
blockCache->setSkipInMemValueVerify(
blockCacheConfig.getSkipInMemValueVerify());

// set eviction policy
auto segmentRatio = blockCacheConfig.getSFifoSegmentRatio();
Expand All @@ -214,6 +228,7 @@ uint64_t setupBlockCache(const navy::BlockCacheConfig& blockCacheConfig,
blockCache->setRecoverEvictionPolicy(
blockCacheConfig.isRecoverEvictionPolicy());
blockCache->setDirectFlush(blockCacheConfig.isDirectFlush());
blockCache->setFlushCopyOffload(blockCacheConfig.isFlushCopyOffload());
blockCache->setUseCombinedEntryBlock(
blockCacheConfig.isCombinedEntryBlockEnabled());
blockCache->setNumAllocatorsPerPriority(
Expand Down
Loading
Loading