Skip to content

Commit 7874531

Browse files
committed
forget additional files
1 parent 695f774 commit 7874531

9 files changed

Lines changed: 825 additions & 8 deletions

File tree

cachelib/CMakeLists.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,56 @@ endfunction()
315315
add_thrift_file(OBJECT_CACHE_PERSISTENCE
316316
object_cache/persistence/persistent_data.thrift json)
317317

318+
if (BUILD_WITH_DTO)
319+
# DSA offload runtime (DTO). Resolved here so every cachelib library can
320+
# use it: DTO::dto is linked PUBLIC into cachelib_common (see
321+
# common/CMakeLists.txt), the base library of every other cachelib target,
322+
# which propagates the headers, the link dependency, and the
323+
# CACHELIB_BUILD_WITH_DTO compile definition to the whole tree and to
324+
# consumers of the installed package.
325+
#
326+
# The DSA checksum offload code requires a DTO with the caller-allocated
327+
# async submit/poll API and the raw-CRC32C convention, which upstream
328+
# intel/DTO does not have — so when no installed DTO cmake package is
329+
# found, fetch and build the pinned revision in-tree rather than linking
330+
# whatever libdto happens to be on the system.
331+
set(CACHELIB_DTO_GIT_REPOSITORY "https://github.com/byrnedj/DTO.git"
332+
CACHE STRING "Git repository for DTO when no installed copy is found")
333+
set(CACHELIB_DTO_GIT_TAG "64b01f8d06c939d8a70066e0b643cb97ffced060"
334+
CACHE STRING "DTO revision to fetch when no installed copy is found")
335+
336+
find_package(DTO CONFIG QUIET)
337+
if (DTO_FOUND)
338+
message(STATUS "Using installed DTO package: ${DTO_DIR}")
339+
else()
340+
if (CMAKE_VERSION VERSION_LESS 3.14)
341+
message(FATAL_ERROR
342+
"BUILD_WITH_DTO needs an installed DTO cmake package or "
343+
"CMake >= 3.14 to fetch one. Install DTO from "
344+
"${CACHELIB_DTO_GIT_REPOSITORY} at ${CACHELIB_DTO_GIT_TAG}, "
345+
"or upgrade CMake.")
346+
endif()
347+
# DTO links against libaccel-config and libnuma; check for them here so
348+
# a missing system package fails at configure time with a clear message
349+
# instead of at link time inside the fetched project.
350+
find_library(ACCEL_CONFIG_LIBRARY accel-config)
351+
find_library(NUMA_LIBRARY numa)
352+
if (NOT ACCEL_CONFIG_LIBRARY OR NOT NUMA_LIBRARY)
353+
message(FATAL_ERROR
354+
"BUILD_WITH_DTO requires the libaccel-config and libnuma "
355+
"development packages (e.g. apt install libaccel-config-dev "
356+
"libnuma-dev)")
357+
endif()
358+
message(STATUS "DTO not installed; fetching "
359+
"${CACHELIB_DTO_GIT_REPOSITORY} @ ${CACHELIB_DTO_GIT_TAG}")
360+
include(FetchContent)
361+
FetchContent_Declare(dto
362+
GIT_REPOSITORY "${CACHELIB_DTO_GIT_REPOSITORY}"
363+
GIT_TAG "${CACHELIB_DTO_GIT_TAG}")
364+
FetchContent_MakeAvailable(dto)
365+
endif()
366+
endif()
367+
318368
add_subdirectory (common)
319369
add_subdirectory (shm)
320370
add_subdirectory (navy)

cachelib/cachebench/cache/Cache.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
#include <sys/stat.h>
2727
#include <sys/types.h>
2828

29+
#include <algorithm>
2930
#include <atomic>
31+
#include <cstring>
3032
#include <iostream>
3133

3234
#include "cachelib/allocator/CacheAllocator.h"
@@ -1411,11 +1413,18 @@ void Cache<Allocator>::setStringItem(WriteHandle& handle,
14111413
}
14121414

14131415
auto ptr = reinterpret_cast<char*>(getMemory(handle));
1414-
std::strncpy(ptr, str.c_str(), dataSize);
1416+
// memcpy/memset instead of strncpy: strncpy scans for the terminator byte
1417+
// by byte and is not interposed by DTO, while memcpy/memset of large
1418+
// values can be offloaded to DSA. Like strncpy, write exactly dataSize
1419+
// bytes: the string (truncated if needed), then a zero-filled tail.
1420+
const size_t copyLen = std::min<size_t>(str.size() + 1, dataSize);
1421+
std::memcpy(ptr, str.c_str(), copyLen);
14151422

14161423
// Make sure the copied string ends with null char
14171424
if (str.size() + 1 > dataSize) {
14181425
ptr[dataSize - 1] = '\0';
1426+
} else if (copyLen < dataSize) {
1427+
std::memset(ptr + copyLen, 0, dataSize - copyLen);
14191428
}
14201429
}
14211430

cachelib/cmake/cachelib-config.cmake.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ find_dependency(fizz )
3535
find_dependency(wangle)
3636
find_dependency(FBThrift)
3737

38+
# When cachelib was built with DSA checksum offload, cachelib_navy links the
39+
# DTO runtime; resolve its imported target for consumers. A DTO fetched and
40+
# built in-tree installs its package into this same prefix.
41+
if (@BUILD_WITH_DTO@)
42+
find_dependency(DTO CONFIG)
43+
endif()
44+
3845
if (NOT TARGET cachelib)
3946
include("${CACHELIB_CMAKE_DIR}/cachelib-targets.cmake")
4047
endif()

cachelib/common/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ target_link_libraries(cachelib_common PUBLIC
5151
${XXHASH_LIBRARY}
5252
)
5353

54+
if (BUILD_WITH_DTO)
55+
# PUBLIC so the DTO headers/link dependency and the compile definition
56+
# propagate to every cachelib library and to installed-package consumers.
57+
# DTO itself is resolved (installed package or pinned fetch) in the
58+
# top-level CMakeLists.txt.
59+
target_link_libraries(cachelib_common PUBLIC DTO::dto)
60+
target_compile_definitions(cachelib_common PUBLIC CACHELIB_BUILD_WITH_DTO)
61+
endif()
62+
5463
install(TARGETS cachelib_common
5564
EXPORT cachelib-exports
5665
DESTINATION ${LIB_INSTALL_DIR} )

cachelib/navy/CMakeLists.txt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,9 @@ target_link_libraries(cachelib_navy PUBLIC
6666
GTest::gmock
6767
)
6868

69-
if (BUILD_WITH_DTO)
70-
find_path(DTO_INCLUDE_DIR NAMES dto.h REQUIRED)
71-
find_library(DTO_LIBRARY NAMES dto REQUIRED)
72-
target_include_directories(cachelib_navy PRIVATE ${DTO_INCLUDE_DIR})
73-
target_compile_definitions(cachelib_navy PRIVATE CACHELIB_BUILD_WITH_DTO)
74-
target_link_libraries(cachelib_navy PUBLIC ${DTO_LIBRARY} accel-config)
75-
endif()
69+
# DTO (DSA offload runtime) is resolved in the top-level CMakeLists.txt and
70+
# linked PUBLIC into cachelib_common, so cachelib_navy inherits DTO::dto and
71+
# the CACHELIB_BUILD_WITH_DTO definition transitively.
7672

7773
install(TARGETS cachelib_navy
7874
EXPORT cachelib-exports
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cachelib/navy/common/ChecksumOffload.h"
18+
19+
#include <folly/fibers/FiberManager.h>
20+
#include <folly/logging/xlog.h>
21+
#include <folly/portability/Asm.h>
22+
23+
#include <cstring>
24+
#include <random>
25+
#include <vector>
26+
27+
#include "cachelib/navy/common/Hash.h"
28+
29+
#ifdef CACHELIB_BUILD_WITH_DTO
30+
#include <dto.h>
31+
#endif
32+
33+
namespace facebook {
34+
namespace cachelib {
35+
namespace navy {
36+
37+
#ifdef CACHELIB_BUILD_WITH_DTO
38+
39+
static_assert(sizeof(dto_async_op) <= 192,
40+
"AsyncChecksumOp::opStorage_ too small for dto_async_op");
41+
static_assert(alignof(dto_async_op) <= 64,
42+
"AsyncChecksumOp::opStorage_ under-aligned for dto_async_op");
43+
44+
bool checksumOffloadSupported() { return true; }
45+
46+
AsyncChecksumOp::~AsyncChecksumOp() {
47+
// A submitted DSA operation writes to opStorage_ (completion record) and,
48+
// for copies, to dest_. It must be drained before this object dies.
49+
if (state_ == State::kDsaPending) {
50+
wait();
51+
}
52+
}
53+
54+
void AsyncChecksumOp::submitImpl(uint8_t* dest,
55+
BufferView src,
56+
bool cacheControl) {
57+
XDCHECK(state_ == State::kIdle);
58+
dest_ = dest;
59+
src_ = src;
60+
copy_ = dest != nullptr;
61+
62+
auto* op = reinterpret_cast<dto_async_op*>(opStorage_);
63+
const int rc = copy_ ? dto_submit_memcpy_crc(op, dest, src.data(),
64+
src.size(), cacheControl)
65+
: dto_submit_crc(op, src.data(), src.size());
66+
if (rc == DTO_ASYNC_SUBMITTED) {
67+
state_ = State::kDsaPending;
68+
return;
69+
}
70+
// DTO_ASYNC_FALLBACK: nothing submitted or copied; run on CPU now.
71+
if (copy_) {
72+
std::memcpy(dest, src.data(), src.size());
73+
}
74+
crc_ = checksum(src);
75+
state_ = State::kCpuDone;
76+
}
77+
78+
uint32_t AsyncChecksumOp::wait() {
79+
XDCHECK(state_ != State::kIdle);
80+
if (state_ == State::kCpuDone) {
81+
state_ = State::kIdle;
82+
return crc_;
83+
}
84+
85+
auto* op = reinterpret_cast<dto_async_op*>(opStorage_);
86+
// Yielding suspends this fiber and lets other request fibers run on the
87+
// same NavyThread while the accelerator works; that is the actual
88+
// "free the writer thread" mechanism. Yield ONLY when another fiber is
89+
// ready to run: with a lone fiber, yield() returns immediately and the
90+
// loop would busy-spin through the FiberManager at full rate for the
91+
// whole accelerator operation, which is far more expensive than a pause
92+
// poll. Outside fiber context (plain thread-pool schedulers, tests) this
93+
// reduces to a pause-poll.
94+
auto* fm = folly::fibers::onFiber()
95+
? folly::fibers::FiberManager::getFiberManagerUnsafe()
96+
: nullptr;
97+
int rc;
98+
while ((rc = dto_async_poll(op)) == DTO_ASYNC_PENDING) {
99+
if (fm && fm->hasReadyTasks()) {
100+
folly::fibers::yield();
101+
} else {
102+
folly::asm_volatile_pause();
103+
}
104+
}
105+
state_ = State::kIdle;
106+
if (rc == DTO_ASYNC_DONE) {
107+
return static_cast<uint32_t>(dto_async_crc_val(op));
108+
}
109+
// Accelerator failure: destination contents are unspecified, so redo the
110+
// whole operation on the CPU. dest_ is not yet visible to readers per the
111+
// submit contract, so overwriting is safe.
112+
if (copy_) {
113+
std::memcpy(dest_, src_.data(), src_.size());
114+
}
115+
return checksum(src_);
116+
}
117+
118+
#else // !CACHELIB_BUILD_WITH_DTO
119+
120+
bool checksumOffloadSupported() { return false; }
121+
122+
AsyncChecksumOp::~AsyncChecksumOp() = default;
123+
124+
void AsyncChecksumOp::submitImpl(uint8_t* dest,
125+
BufferView src,
126+
bool /* cacheControl */) {
127+
XDCHECK(state_ == State::kIdle);
128+
if (dest != nullptr) {
129+
std::memcpy(dest, src.data(), src.size());
130+
}
131+
crc_ = checksum(src);
132+
state_ = State::kCpuDone;
133+
}
134+
135+
uint32_t AsyncChecksumOp::wait() {
136+
XDCHECK(state_ == State::kCpuDone);
137+
state_ = State::kIdle;
138+
return crc_;
139+
}
140+
141+
#endif // CACHELIB_BUILD_WITH_DTO
142+
143+
void AsyncChecksumOp::submitCopyAndChecksum(uint8_t* dest,
144+
BufferView src,
145+
bool cacheControl) {
146+
XDCHECK(dest);
147+
submitImpl(dest, src, cacheControl);
148+
}
149+
150+
void AsyncChecksumOp::submitChecksum(BufferView src) {
151+
submitImpl(nullptr, src, false);
152+
}
153+
154+
uint32_t copyAndChecksum(uint8_t* dest,
155+
BufferView src,
156+
folly::FunctionRef<void()> overlap) {
157+
AsyncChecksumOp op;
158+
// Cache control: destinations of fused copies (write buffers) are read
159+
// again shortly, by lookups served from in-memory buffers and by the
160+
// device flush path.
161+
op.submitCopyAndChecksum(dest, src, true /* cacheControl */);
162+
overlap();
163+
return op.wait();
164+
}
165+
166+
uint32_t checksumWithOverlap(BufferView src,
167+
folly::FunctionRef<void()> overlap) {
168+
AsyncChecksumOp op;
169+
op.submitChecksum(src);
170+
overlap();
171+
return op.wait();
172+
}
173+
174+
bool checksumOffloadSelfCheck() {
175+
if (!checksumOffloadSupported()) {
176+
return false;
177+
}
178+
// Exercise both operations on a buffer large enough to exceed DTO's
179+
// minimum-size gates (DTO_CRC_MIN_BYTES / DTO_MIN_BYTES) so the DSA path
180+
// actually runs, and verify parity with navy::checksum() plus copy
181+
// fidelity. If DSA is unavailable, the CPU fallback must also match.
182+
constexpr size_t kSize = 1024 * 1024;
183+
std::vector<uint8_t> src(kSize);
184+
std::vector<uint8_t> dst(kSize, 0);
185+
std::mt19937 gen{12345};
186+
for (auto& b : src) {
187+
b = static_cast<uint8_t>(gen());
188+
}
189+
190+
const BufferView view{src.size(), src.data()};
191+
const uint32_t sw = checksum(view);
192+
const uint32_t viaCrc = checksumWithOverlap(view, [] {});
193+
const uint32_t viaCopy = copyAndChecksum(dst.data(), view, [] {});
194+
return viaCrc == sw && viaCopy == sw &&
195+
std::memcmp(dst.data(), src.data(), kSize) == 0;
196+
}
197+
198+
} // namespace navy
199+
} // namespace cachelib
200+
} // namespace facebook

0 commit comments

Comments
 (0)