From b5ebca1be64431bcad6788a38cf15917264b4c07 Mon Sep 17 00:00:00 2001 From: "Mag1c.H" Date: Sat, 13 Dec 2025 14:16:13 +0800 Subject: [PATCH 01/10] [Feat] Next Store Interface (#510) Define the StoreV1 interface, see issue #490 for details. --- .clang-format | 2 +- .github/workflows/cpp-linter.yml | 2 +- .github/workflows/ucmstore.yml | 14 +- ucm/shared/infra/status/status.h | 26 +- ucm/shared/infra/template/spsc_ring_queue.h | 121 +++++++ ucm/shared/infra/thread/latch.h | 36 ++- .../infra/time/now_time.h} | 17 +- ucm/shared/test/CMakeLists.txt | 2 +- .../test/case/infra/spsc_ring_queue_test.cc | 87 ++++++ ucm/shared/vendor/CMakeLists.txt | 4 + ucm/sparse/gsa/prefetch/CMakeLists.txt | 2 +- ucm/store/CMakeLists.txt | 10 +- ucm/store/detail/CMakeLists.txt | 3 + ucm/store/detail/task/task_manager.h | 121 +++++++ ucm/store/{ => detail}/task/task_queue.h | 0 ucm/store/{ => detail}/task/task_set.h | 0 ucm/store/{ => detail}/task/task_shard.h | 0 .../task/task_waiter.h} | 25 +- ucm/store/detail/type/types.h | 69 ++++ ucm/store/device/simu/CMakeLists.txt | 2 - ucm/store/dramstore/CMakeLists.txt | 0 ucm/store/dramstore/__init__.py | 0 ucm/store/dramstore/dramstore_connector.py | 251 --------------- ucm/store/factory.py | 3 - ucm/store/factory_v1.py | 62 ++++ ucm/store/infra/CMakeLists.txt | 11 - ucm/store/localstore/CMakeLists.txt | 17 - ucm/store/localstore/__init__.py | 0 ucm/store/localstore/cc/api/localstore.cc | 96 ------ ucm/store/localstore/cc/api/localstore.h | 82 ----- .../localstore/cc/domain/cache/cache_data.cc | 47 --- .../localstore/cc/domain/cache/cache_hash.cc | 295 ------------------ .../localstore/cc/domain/cache/cache_index.cc | 105 ------- .../localstore/cc/domain/cache/cache_index.h | 49 --- .../cc/domain/cache/cache_instance.cc | 52 --- .../cc/domain/cache/cache_instance.h | 45 --- .../cc/domain/cache/cache_layout.cc | 38 --- .../localstore/cc/domain/cache/cache_meta.cc | 147 --------- .../cc/domain/cache/cache_segment.cc | 60 ---- .../cc/domain/cache/cache_segment.h | 47 --- ucm/store/localstore/cpy/localstore.py.cc | 120 ------- ucm/store/nfsstore/CMakeLists.txt | 4 +- .../cc/domain}/file/file.cc | 0 .../{infra => nfsstore/cc/domain}/file/file.h | 0 .../cc/domain}/file/ifile.h | 0 .../cc/domain}/file/posix_file.cc | 0 .../cc/domain}/file/posix_file.h | 0 .../nfsstore/cc/domain/trans/posix_queue.h | 9 +- .../nfsstore/cc/domain/trans/trans_manager.h | 2 +- .../{ => nfsstore}/device/CMakeLists.txt | 0 .../device/ascend/CMakeLists.txt | 2 +- .../device/ascend/ascend_device.cc | 0 .../{ => nfsstore}/device/cuda/CMakeLists.txt | 2 +- .../{ => nfsstore}/device/cuda/cuda_device.cu | 0 .../{ => nfsstore}/device/ibuffered_device.h | 0 ucm/store/{ => nfsstore}/device/idevice.h | 0 .../{ => nfsstore}/device/maca/CMakeLists.txt | 2 +- .../{ => nfsstore}/device/maca/maca_device.cu | 0 .../{ => nfsstore}/device/musa/CMakeLists.txt | 2 +- .../{ => nfsstore}/device/musa/musa_device.cc | 0 .../{ => nfsstore}/device/musa/musa_device.mu | 0 ucm/store/nfsstore/device/simu/CMakeLists.txt | 2 + .../{ => nfsstore}/device/simu/simu_device.cc | 3 +- ucm/store/pcstore/CMakeLists.txt | 2 +- ucm/store/pcstore/cc/domain/file/file.cc | 97 ++++++ .../cc/domain/file/file.h} | 40 ++- ucm/store/pcstore/cc/domain/file/ifile.h | 81 +++++ .../pcstore/cc/domain/file/posix_file.cc | 245 +++++++++++++++ .../cc/domain/file/posix_file.h} | 48 +-- ucm/store/pcstore/pcstore_connector_v1.py | 221 +++++++++++++ ucm/store/task/CMakeLists.txt | 3 - ucm/store/task/task_manager.cc | 98 ------ ucm/store/task/task_manager.h | 57 ---- ucm/store/task/task_waiter.h | 68 ---- ucm/store/test/CMakeLists.txt | 8 - ucm/store/test/case/cmn/path_base.h | 58 ---- ucm/store/test/case/cmn/random.h | 50 --- ucm/store/test/case/infra/file_test.cc | 42 --- ucm/store/test/case/infra/posix_file_test.cc | 149 --------- .../test/case/localstore/cache_hash_test.cc | 88 ------ .../test/case/localstore/cache_index_test.cc | 63 ---- .../case/localstore/cache_instance_test.cc | 82 ----- .../test/case/localstore/cache_layout_test.cc | 35 --- .../case/localstore/cache_segment_test.cc | 66 ---- ucm/store/test/case/nfsstore/hotness_test.cc | 54 ---- .../test/case/nfsstore/space_manager_test.cc | 125 -------- .../test/case/nfsstore/space_property_test.cc | 61 ---- .../test/case/nfsstore/space_recycle_test.cc | 124 -------- ...embed_and_fetch.py => pcstore_embed_v1.py} | 100 +++--- ucm/store/ucmstore_v1.h | 122 ++++++++ ucm/store/ucmstore_v1.py | 187 +++++++++++ 91 files changed, 1611 insertions(+), 2861 deletions(-) create mode 100644 ucm/shared/infra/template/spsc_ring_queue.h rename ucm/{store/localstore/cc/domain/cache/cache_layout.h => shared/infra/time/now_time.h} (79%) create mode 100644 ucm/shared/test/case/infra/spsc_ring_queue_test.cc create mode 100644 ucm/store/detail/CMakeLists.txt create mode 100644 ucm/store/detail/task/task_manager.h rename ucm/store/{ => detail}/task/task_queue.h (100%) rename ucm/store/{ => detail}/task/task_set.h (100%) rename ucm/store/{ => detail}/task/task_shard.h (100%) rename ucm/store/{localstore/cc/domain/cache/cache_data.h => detail/task/task_waiter.h} (72%) create mode 100644 ucm/store/detail/type/types.h delete mode 100644 ucm/store/device/simu/CMakeLists.txt delete mode 100644 ucm/store/dramstore/CMakeLists.txt delete mode 100644 ucm/store/dramstore/__init__.py delete mode 100644 ucm/store/dramstore/dramstore_connector.py create mode 100644 ucm/store/factory_v1.py delete mode 100644 ucm/store/infra/CMakeLists.txt delete mode 100644 ucm/store/localstore/CMakeLists.txt delete mode 100644 ucm/store/localstore/__init__.py delete mode 100644 ucm/store/localstore/cc/api/localstore.cc delete mode 100644 ucm/store/localstore/cc/api/localstore.h delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_data.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_hash.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_index.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_index.h delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_instance.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_instance.h delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_layout.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_meta.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_segment.cc delete mode 100644 ucm/store/localstore/cc/domain/cache/cache_segment.h delete mode 100644 ucm/store/localstore/cpy/localstore.py.cc rename ucm/store/{infra => nfsstore/cc/domain}/file/file.cc (100%) rename ucm/store/{infra => nfsstore/cc/domain}/file/file.h (100%) rename ucm/store/{infra => nfsstore/cc/domain}/file/ifile.h (100%) rename ucm/store/{infra => nfsstore/cc/domain}/file/posix_file.cc (100%) rename ucm/store/{infra => nfsstore/cc/domain}/file/posix_file.h (100%) rename ucm/store/{ => nfsstore}/device/CMakeLists.txt (100%) rename ucm/store/{ => nfsstore}/device/ascend/CMakeLists.txt (83%) rename ucm/store/{ => nfsstore}/device/ascend/ascend_device.cc (100%) rename ucm/store/{ => nfsstore}/device/cuda/CMakeLists.txt (86%) rename ucm/store/{ => nfsstore}/device/cuda/cuda_device.cu (100%) rename ucm/store/{ => nfsstore}/device/ibuffered_device.h (100%) rename ucm/store/{ => nfsstore}/device/idevice.h (100%) rename ucm/store/{ => nfsstore}/device/maca/CMakeLists.txt (90%) rename ucm/store/{ => nfsstore}/device/maca/maca_device.cu (100%) rename ucm/store/{ => nfsstore}/device/musa/CMakeLists.txt (82%) rename ucm/store/{ => nfsstore}/device/musa/musa_device.cc (100%) rename ucm/store/{ => nfsstore}/device/musa/musa_device.mu (100%) create mode 100644 ucm/store/nfsstore/device/simu/CMakeLists.txt rename ucm/store/{ => nfsstore}/device/simu/simu_device.cc (99%) create mode 100644 ucm/store/pcstore/cc/domain/file/file.cc rename ucm/store/{localstore/cc/domain/cache/cache_hash.h => pcstore/cc/domain/file/file.h} (54%) create mode 100644 ucm/store/pcstore/cc/domain/file/ifile.h create mode 100644 ucm/store/pcstore/cc/domain/file/posix_file.cc rename ucm/store/{localstore/cc/domain/cache/cache_meta.h => pcstore/cc/domain/file/posix_file.h} (54%) create mode 100644 ucm/store/pcstore/pcstore_connector_v1.py delete mode 100644 ucm/store/task/CMakeLists.txt delete mode 100644 ucm/store/task/task_manager.cc delete mode 100644 ucm/store/task/task_manager.h delete mode 100644 ucm/store/task/task_waiter.h delete mode 100644 ucm/store/test/case/cmn/path_base.h delete mode 100644 ucm/store/test/case/cmn/random.h delete mode 100644 ucm/store/test/case/infra/file_test.cc delete mode 100644 ucm/store/test/case/infra/posix_file_test.cc delete mode 100644 ucm/store/test/case/localstore/cache_hash_test.cc delete mode 100644 ucm/store/test/case/localstore/cache_index_test.cc delete mode 100644 ucm/store/test/case/localstore/cache_instance_test.cc delete mode 100644 ucm/store/test/case/localstore/cache_layout_test.cc delete mode 100644 ucm/store/test/case/localstore/cache_segment_test.cc delete mode 100644 ucm/store/test/case/nfsstore/hotness_test.cc delete mode 100644 ucm/store/test/case/nfsstore/space_manager_test.cc delete mode 100644 ucm/store/test/case/nfsstore/space_property_test.cc delete mode 100644 ucm/store/test/case/nfsstore/space_recycle_test.cc rename ucm/store/test/e2e/{dramstore_embed_and_fetch.py => pcstore_embed_v1.py} (54%) create mode 100644 ucm/store/ucmstore_v1.h create mode 100644 ucm/store/ucmstore_v1.py diff --git a/.clang-format b/.clang-format index 0344f940d..e9b5c5711 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,4 @@ -BasedOnStyle: LLVM +BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 100 AccessModifierOffset: -4 diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml index e013a62c7..189683d5d 100644 --- a/.github/workflows/cpp-linter.yml +++ b/.github/workflows/cpp-linter.yml @@ -25,7 +25,7 @@ jobs: files-changed-only: true lines-changed-only: diff format-review: true - thread-comments: ${{ github.event_name == 'pull_request' && 'update' }} + version: 20 - name: Fail fast?! if: steps.linter.outputs.checks-failed != 0 diff --git a/.github/workflows/ucmstore.yml b/.github/workflows/ucmstore.yml index da1e0460b..08c52aad2 100644 --- a/.github/workflows/ucmstore.yml +++ b/.github/workflows/ucmstore.yml @@ -13,7 +13,7 @@ env: BUILD_TYPE: Debug jobs: - ci: + cc_gtest: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix @@ -24,20 +24,12 @@ jobs: - name: Install googletest run: | - git clone https://github.com/google/googletest.git --depth=1 --branch=v1.12.0 + git clone https://github.com/google/googletest.git --depth=1 --branch=v1.17.0 cd googletest mkdir build && cd build cmake -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=True .. sudo make install -j - - name: Install mockcpp - run: | - git clone https://github.com/sinojelly/mockcpp.git --depth=1 - cd mockcpp - mkdir build && cd build - cmake -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=True -DMOCKCPP_XUNIT="gtest" -DMOCKCPP_XUNIT_HOME=/usr/local/ .. - sudo make install -j - - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type @@ -45,7 +37,7 @@ jobs: - name: Build # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j - name: Test working-directory: ${{github.workspace}}/build diff --git a/ucm/shared/infra/status/status.h b/ucm/shared/infra/status/status.h index 3711de842..bbe6f2157 100644 --- a/ucm/shared/infra/status/status.h +++ b/ucm/shared/infra/status/status.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace UC { @@ -49,6 +50,7 @@ class Status { static constexpr int32_t EDESERIALIZE_ = __MakeStatusCode<7>(); static constexpr int32_t EUNSUPPORTED_ = __MakeStatusCode<8>(); static constexpr int32_t ENOSPACE_ = __MakeStatusCode<9>(); + static constexpr int32_t ETIMEOUT_ = __MakeStatusCode<10>(); int32_t code_; std::string message_; explicit Status(int32_t code) : code_(code) {} @@ -72,8 +74,15 @@ class Status { static Status Error(std::string message) { return {ERROR_, std::move(message)}; } static Status Error() { return Status{ERROR_}; } static Status InvalidParam() { return Status{EPARAM_}; } + static Status InvalidParam(std::string message) { return {EPARAM_, std::move(message)}; } + template + static Status InvalidParam(fmt::format_string fmt, Args&&... args) + { + return InvalidParam(fmt::format(fmt, std::forward(args)...)); + } static Status OutOfMemory() { return Status{EOOM_}; } static Status OsApiError() { return Status{EOSERROR_}; } + static Status OsApiError(std::string message) { return Status{EOSERROR_, std::move(message)}; } static Status DuplicateKey() { return Status{EDUPLICATE_}; } static Status Retry() { return Status{ERETRY_}; } static Status NotFound() { return Status{ENOOBJ_}; } @@ -81,10 +90,25 @@ class Status { static Status DeserializeFailed() { return Status{EDESERIALIZE_}; } static Status Unsupported() { return Status{EUNSUPPORTED_}; } static Status NoSpace() { return Status{ENOSPACE_}; } + static Status Timeout() { return Status{ETIMEOUT_}; } +}; + +template +class Expected { + std::variant v_; + +public: + Expected(T&& val) : v_(std::move(val)) {} + Expected(Status err) : v_(err) {} + bool HasValue() const noexcept { return v_.index() == 1; } + explicit operator bool() const noexcept { return HasValue(); } + T& Value() & { return std::get(v_); } + T&& Value() && { return std::get(std::move(v_)); } + Status Error() const { return std::get(v_); } }; inline std::string format_as(const Status& status) { return status.ToString(); } -} // namespace UC +} // namespace UC #endif diff --git a/ucm/shared/infra/template/spsc_ring_queue.h b/ucm/shared/infra/template/spsc_ring_queue.h new file mode 100644 index 000000000..7bb7efbde --- /dev/null +++ b/ucm/shared/infra/template/spsc_ring_queue.h @@ -0,0 +1,121 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#ifndef UNIFIEDCACHE_INFRA_SPSC_RING_QUEUE_H +#define UNIFIEDCACHE_INFRA_SPSC_RING_QUEUE_H + +#include +#include +#include +#include +#include +#include + +namespace UC { + +template +class SpscRingQueue { + alignas(64) std::atomic head_ = 0; + alignas(64) std::atomic tail_ = 0; + bool pow2_{false}; + size_t mask_{0}; + size_t capacity_{0}; + std::unique_ptr buffer_; + + size_t Mod(size_t n) { return pow2_ ? (n & mask_) : (n % capacity_); } + +public: + void Setup(size_t capacity) + { + capacity_ = capacity; + mask_ = capacity_ - 1; + pow2_ = (capacity_ & mask_) == 0; + buffer_ = std::make_unique(capacity_); + } + + void Push(T&& value) + { + while (true) { + const size_t currentHead = head_.load(std::memory_order_relaxed); + const size_t nextHead = Mod(currentHead + 1); + if (nextHead != tail_.load(std::memory_order_acquire)) { + buffer_[currentHead] = std::move(value); + head_.store(nextHead, std::memory_order_release); + return; + } + std::this_thread::yield(); + } + } + + bool TryPush(T&& value) + { + const size_t currentHead = head_.load(std::memory_order_relaxed); + const size_t nextHead = Mod(currentHead + 1); + const size_t currentTail = tail_.load(std::memory_order_acquire); + if (nextHead == currentTail) { return false; } + buffer_[currentHead] = std::move(value); + head_.store(nextHead, std::memory_order_release); + return true; + } + + bool TryPop(T& value) + { + const size_t currentHead = head_.load(std::memory_order_acquire); + const size_t currentTail = tail_.load(std::memory_order_relaxed); + if (currentTail == currentHead) { return false; } + value = std::move(buffer_[currentTail]); + tail_.store(Mod(currentTail + 1), std::memory_order_release); + return true; + } + + template + void ConsumerLoop(const std::atomic_bool& stop, ConsumerHandler&& handler, Args&&... args) + { + constexpr size_t kSpinLimit = 16; + constexpr size_t kTaskBatch = 64; + size_t spinCount = 0; + size_t taskCount = 0; + T task; + while (!stop.load(std::memory_order_relaxed)) { + if (TryPop(task)) { + spinCount = 0; + std::invoke(handler, std::forward(args)..., std::move(task)); + if (++taskCount % kTaskBatch == 0) { + if (stop.load(std::memory_order_acquire)) { break; } + } + continue; + } + if (++spinCount < kSpinLimit) { + std::this_thread::yield(); + } else { + if (stop.load(std::memory_order_acquire)) { break; } + std::this_thread::sleep_for(std::chrono::microseconds(100)); + spinCount = 0; + } + } + } +}; + +} // namespace UC + +#endif diff --git a/ucm/shared/infra/thread/latch.h b/ucm/shared/infra/thread/latch.h index fb1dcf583..037c916b7 100644 --- a/ucm/shared/infra/thread/latch.h +++ b/ucm/shared/infra/thread/latch.h @@ -28,21 +28,25 @@ #include #include #include +#include "time/now_time.h" namespace UC { class Latch { public: - explicit Latch(const size_t expected = 0) : counter_{expected} {} + Latch() : startTp{NowTime::Now()} {} + void Set(size_t expected) noexcept { this->counter_.store(expected); } + void SetEpilog(std::function finish) noexcept { finish_ = std::move(finish); } void Up() { ++this->counter_; } - void Done(std::function finish) noexcept + void Done(std::function&& finish = nullptr) noexcept { auto counter = this->counter_.load(std::memory_order_acquire); while (counter > 0) { auto desired = counter - 1; if (this->counter_.compare_exchange_weak(counter, desired, std::memory_order_acq_rel)) { if (desired == 0) { - if (finish) { finish(); } + auto& fn = finish ? finish : finish_; + if (fn) { fn(); } std::lock_guard lg(this->mutex_); this->cv_.notify_all(); } @@ -57,13 +61,33 @@ class Latch { if (this->counter_ == 0) { return; } this->cv_.wait(lk, [this] { return this->counter_ == 0; }); } + bool WaitFor(size_t timeoutMs) noexcept + { + if (timeoutMs == 0) { + this->Wait(); + return true; + } + std::unique_lock lk(this->mutex_); + if (this->counter_ == 0) { return true; } + auto elapsed = std::chrono::duration(NowTime::Now() - startTp); + auto elapsedMs = std::chrono::duration_cast(elapsed); + auto timeMs = std::chrono::milliseconds(timeoutMs); + if (timeMs <= elapsedMs) { return false; } + auto remainMs = timeMs - elapsedMs; + return this->cv_.wait_for(lk, remainMs, [this] { return this->counter_ == 0; }); + } + bool Check() noexcept { return this->counter_ == 0; } + +public: + double startTp{0}; protected: std::mutex mutex_; std::condition_variable cv_; - std::atomic counter_; + std::atomic counter_{0}; + std::function finish_{nullptr}; }; -} // namespace UC +} // namespace UC -#endif // UNIFIEDCACHE_INFRA_LATCH_H +#endif // UNIFIEDCACHE_INFRA_LATCH_H diff --git a/ucm/store/localstore/cc/domain/cache/cache_layout.h b/ucm/shared/infra/time/now_time.h similarity index 79% rename from ucm/store/localstore/cc/domain/cache/cache_layout.h rename to ucm/shared/infra/time/now_time.h index c4480b8e5..21fd55f27 100644 --- a/ucm/store/localstore/cc/domain/cache/cache_layout.h +++ b/ucm/shared/infra/time/now_time.h @@ -21,19 +21,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ -#ifndef UNIFIEDCACHE_CACHE_LAYOUT_H -#define UNIFIEDCACHE_CACHE_LAYOUT_H +#ifndef UNIFIEDCACHE_SHARED_INFRA_TIME_NOW_TIME_H +#define UNIFIEDCACHE_SHARED_INFRA_TIME_NOW_TIME_H -#include +#include namespace UC { -class CacheLayout { +class NowTime { public: - static std::string MetaShmFile(); - static std::string DataShmFile(const size_t id); + static auto Now() + { + auto now = std::chrono::steady_clock::now().time_since_epoch(); + return std::chrono::duration(now).count(); + } }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/shared/test/CMakeLists.txt b/ucm/shared/test/CMakeLists.txt index 07241d814..b99d46a8f 100644 --- a/ucm/shared/test/CMakeLists.txt +++ b/ucm/shared/test/CMakeLists.txt @@ -5,7 +5,7 @@ if(BUILD_UNIT_TESTS) target_include_directories(ucmshared.test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/case) target_link_libraries(ucmshared.test PRIVATE trans - gtest_main gtest mockcpp + gtest_main gtest ) gtest_discover_tests(ucmshared.test) endif() diff --git a/ucm/shared/test/case/infra/spsc_ring_queue_test.cc b/ucm/shared/test/case/infra/spsc_ring_queue_test.cc new file mode 100644 index 000000000..def570b30 --- /dev/null +++ b/ucm/shared/test/case/infra/spsc_ring_queue_test.cc @@ -0,0 +1,87 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#include "template/spsc_ring_queue.h" +#include + +class UCSpscRingQueueTest : public testing::Test {}; + +TEST_F(UCSpscRingQueueTest, Basic) +{ + UC::SpscRingQueue queue; + queue.Setup(16); + size_t data; + ASSERT_FALSE(queue.TryPop(data)); + ASSERT_TRUE(queue.TryPush(1023)); + ASSERT_TRUE(queue.TryPop(data)); + ASSERT_EQ(data, 1023); + ASSERT_FALSE(queue.TryPop(data)); +} + +TEST_F(UCSpscRingQueueTest, FIFO) +{ + UC::SpscRingQueue queue; + queue.Setup(16); + constexpr size_t nElem = 10; + for (size_t i = 0; i < nElem; i++) { ASSERT_TRUE(queue.TryPush(std::move(i))); } + for (size_t i = 0; i < nElem; i++) { + size_t value = -1; + ASSERT_TRUE(queue.TryPop(value)); + ASSERT_EQ(value, i); + } + size_t value = -1; + ASSERT_FALSE(queue.TryPop(value)); +} + +TEST_F(UCSpscRingQueueTest, Full) +{ + constexpr size_t N = 10; + UC::SpscRingQueue queue; + queue.Setup(N); + constexpr size_t nElem = N - 1; + for (size_t i = 0; i < nElem; i++) { ASSERT_TRUE(queue.TryPush(std::move(i))); } + ASSERT_FALSE(queue.TryPush(999)); + size_t value = -1; + ASSERT_TRUE(queue.TryPop(value)); + ASSERT_EQ(value, 0); + ASSERT_TRUE(queue.TryPush(999)); +} + +TEST_F(UCSpscRingQueueTest, MoveOnly) +{ + struct MoveOnly { + int value; + MoveOnly() = default; + explicit MoveOnly(int v) : value(v) {} + MoveOnly(const MoveOnly&) = delete; + MoveOnly& operator=(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + }; + UC::SpscRingQueue queue; + queue.Setup(9); + EXPECT_TRUE(queue.TryPush(MoveOnly(42))); + MoveOnly obj; + EXPECT_TRUE(queue.TryPop(obj)); + EXPECT_EQ(obj.value, 42); +} diff --git a/ucm/shared/vendor/CMakeLists.txt b/ucm/shared/vendor/CMakeLists.txt index 411139a79..d70e957a6 100644 --- a/ucm/shared/vendor/CMakeLists.txt +++ b/ucm/shared/vendor/CMakeLists.txt @@ -22,6 +22,10 @@ function(EnableDept) endif() message(STATUS "Fetching ${DEPT_NAME}(${DEPT_TAG}) from ${VALID_GIT_URL}") FetchContent_Declare(${DEPT_NAME} GIT_REPOSITORY ${VALID_GIT_URL} GIT_TAG ${DEPT_TAG} GIT_SHALLOW TRUE) + string(TOUPPER ${DEPT_NAME} NAME_UPPER) + set(${NAME_UPPER}_INSTALL OFF CACHE INTERNAL "" FORCE) + set(${NAME_UPPER}_BUILD_TESTS OFF CACHE INTERNAL "" FORCE) + set(${NAME_UPPER}_BUILD_EXAMPLES OFF CACHE INTERNAL "" FORCE) FetchContent_MakeAvailable(${DEPT_NAME}) endfunction() diff --git a/ucm/sparse/gsa/prefetch/CMakeLists.txt b/ucm/sparse/gsa/prefetch/CMakeLists.txt index 7cd054a6f..ace6f497f 100644 --- a/ucm/sparse/gsa/prefetch/CMakeLists.txt +++ b/ucm/sparse/gsa/prefetch/CMakeLists.txt @@ -39,7 +39,7 @@ set(LIBRARIES torch_python gomp pthread - storetask + storeintf ) # NPU特殊配置 diff --git a/ucm/store/CMakeLists.txt b/ucm/store/CMakeLists.txt index c3825360d..b4bd46a44 100644 --- a/ucm/store/CMakeLists.txt +++ b/ucm/store/CMakeLists.txt @@ -1,10 +1,8 @@ -include_directories(.) -add_subdirectory(infra) -add_subdirectory(device) +add_subdirectory(detail) +add_library(storeintf INTERFACE) +target_include_directories(storeintf INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(storeintf INTERFACE storedetail infra_status) add_subdirectory(nfsstore) add_subdirectory(pcstore) -add_subdirectory(dramstore) -add_subdirectory(localstore) add_subdirectory(mooncakestore) -add_subdirectory(task) add_subdirectory(test) diff --git a/ucm/store/detail/CMakeLists.txt b/ucm/store/detail/CMakeLists.txt new file mode 100644 index 000000000..d994453d3 --- /dev/null +++ b/ucm/store/detail/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB_RECURSE UCM_STORE_DETAIL_SOURCE "*.*") +add_library(storedetail OBJECT ${UCM_STORE_DETAIL_SOURCE}) +target_include_directories(storedetail PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/ucm/store/detail/task/task_manager.h b/ucm/store/detail/task/task_manager.h new file mode 100644 index 000000000..49c2f7ef2 --- /dev/null +++ b/ucm/store/detail/task/task_manager.h @@ -0,0 +1,121 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#ifndef UNIFIEDCACHE_TASK_MANAGER_H +#define UNIFIEDCACHE_TASK_MANAGER_H + +#include +#include "status/status.h" +#include "task_queue.h" +#include "task_set.h" + +namespace UC { + +class TaskManager { + using TaskPtr = std::shared_ptr; + using WaiterPtr = std::shared_ptr; + using TaskPair = std::pair; + using QueuePtr = std::shared_ptr; + +public: + virtual ~TaskManager() = default; + virtual Status Submit(Task&& task, size_t& taskId) noexcept + { + taskId = task.Id(); + const auto taskStr = task.Str(); + TaskPtr taskPtr = nullptr; + WaiterPtr waiterPtr = nullptr; + try { + taskPtr = std::make_shared(std::move(task)); + waiterPtr = std::make_shared(0, task.StartTp()); + } catch (const std::exception& e) { + UC_ERROR("Failed({}) to submit task({}).", e.what(), taskStr); + return Status::OutOfMemory(); + } + std::lock_guard lg(mutex_); + const auto& [iter, success] = + tasks_.emplace(taskId, std::make_pair(std::move(taskPtr), std::move(waiterPtr))); + if (!success) { + UC_ERROR("Failed to submit task({}).", taskStr); + return Status::OutOfMemory(); + } + auto shards = iter->second.first->Split(queues_.size(), iter->second.second); + for (auto& shard : shards) { + auto& q = queues_[qIndex_++]; + if (qIndex_ == queues_.size()) { qIndex_ = 0; } + q->Push(shard); + } + return Status::OK(); + } + virtual Status Wait(const size_t taskId) noexcept + { + TaskPtr task = nullptr; + WaiterPtr waiter = nullptr; + { + std::lock_guard lg(mutex_); + auto iter = tasks_.find(taskId); + if (iter == tasks_.end()) { + UC_ERROR("Not found task by id({}).", taskId); + return Status::NotFound(); + } + task = iter->second.first; + waiter = iter->second.second; + tasks_.erase(iter); + } + if (!waiter->Wait(timeoutMs_)) { + UC_ERROR("Task({}) timeout({}).", task->Str(), timeoutMs_); + failureSet_.Insert(taskId); + waiter->Wait(); + } + auto failure = failureSet_.Contains(taskId); + if (failure) { + failureSet_.Remove(taskId); + UC_ERROR("Task({}) failed.", task->Str()); + return Status::Error(); + } + return Status::OK(); + } + virtual Status Check(const size_t taskId, bool& finish) noexcept + { + std::lock_guard lg(mutex_); + auto iter = tasks_.find(taskId); + if (iter == tasks_.end()) { + UC_ERROR("Not found task by id({}).", taskId); + return Status::NotFound(); + } + finish = iter->second.second->Finish(); + return Status::OK(); + } + +protected: + std::mutex mutex_; + std::unordered_map tasks_; + size_t qIndex_{0}; + std::vector queues_; + size_t timeoutMs_{0}; + TaskSet failureSet_; +}; + +} // namespace UC + +#endif diff --git a/ucm/store/task/task_queue.h b/ucm/store/detail/task/task_queue.h similarity index 100% rename from ucm/store/task/task_queue.h rename to ucm/store/detail/task/task_queue.h diff --git a/ucm/store/task/task_set.h b/ucm/store/detail/task/task_set.h similarity index 100% rename from ucm/store/task/task_set.h rename to ucm/store/detail/task/task_set.h diff --git a/ucm/store/task/task_shard.h b/ucm/store/detail/task/task_shard.h similarity index 100% rename from ucm/store/task/task_shard.h rename to ucm/store/detail/task/task_shard.h diff --git a/ucm/store/localstore/cc/domain/cache/cache_data.h b/ucm/store/detail/task/task_waiter.h similarity index 72% rename from ucm/store/localstore/cc/domain/cache/cache_data.h rename to ucm/store/detail/task/task_waiter.h index 89a9536a2..f4bfa5c79 100644 --- a/ucm/store/localstore/cc/domain/cache/cache_data.h +++ b/ucm/store/detail/task/task_waiter.h @@ -21,24 +21,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ -#ifndef UNIFIEDCACHE_CACHE_DATA_H -#define UNIFIEDCACHE_CACHE_DATA_H +#ifndef UNIFIEDCACHE_ITASK_WAITER_H +#define UNIFIEDCACHE_ITASK_WAITER_H -#include -#include "cache_segment.h" +#include "thread/latch.h" namespace UC { -class CacheData { - uint32_t ioNumberPerSegment_; - std::vector segments_; - +class TaskWaiter : public Latch { public: - CacheData() : ioNumberPerSegment_{0} {} - Status Setup(const size_t ioSize, const size_t ioNumber); - void* At(const size_t index); + TaskWaiter(const size_t expected, const double startTp) : Latch{} + { + this->startTp = startTp; + Set(expected); + } + using Latch::Wait; + virtual bool Wait(const size_t timeoutMs) noexcept { return WaitFor(timeoutMs); } + virtual bool Finish() noexcept { return Check(); } }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/detail/type/types.h b/ucm/store/detail/type/types.h new file mode 100644 index 000000000..9f96656a2 --- /dev/null +++ b/ucm/store/detail/type/types.h @@ -0,0 +1,69 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#ifndef UNIFIEDCACHE_STORE_DETAIL_TYPE_TYPES_H +#define UNIFIEDCACHE_STORE_DETAIL_TYPE_TYPES_H + +#include +#include +#include + +namespace UC::Detail { + +using BlockId = std::array; /* 16-byte block hash */ +using TaskHandle = std::size_t; /* Opaque task token (0 = invalid) */ + +/** + * @brief Hasher of BlockId + */ +struct BlockIdHasher { + size_t operator()(const BlockId& blockId) const noexcept + { + std::string_view sv(reinterpret_cast(blockId.data()), blockId.size()); + return std::hash{}(sv); + } +}; + +/** + * @brief Describes one shard (slice) of a block. + */ +struct Shard { + BlockId owner; /* Parent block identifier */ + std::size_t index; /* Shard index inside the block */ + std::vector addrs; /* Device-side buffer addresses */ +}; + +/** + * @brief Batch descriptor for load or dump operations. + * + * Inherits from std::vector to store the shard list and reserves + * room for future extensions (priority, deadline, etc.). + */ +struct TaskDesc : std::vector { + using vector::vector; /* Inherit all ctors */ + std::string brief; /* Description of Task */ +}; + +} // namespace UC::Detail + +#endif diff --git a/ucm/store/device/simu/CMakeLists.txt b/ucm/store/device/simu/CMakeLists.txt deleted file mode 100644 index 1838f18cd..000000000 --- a/ucm/store/device/simu/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_library(storedevice STATIC simu_device.cc) -target_link_libraries(storedevice PUBLIC storeinfra) diff --git a/ucm/store/dramstore/CMakeLists.txt b/ucm/store/dramstore/CMakeLists.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/ucm/store/dramstore/__init__.py b/ucm/store/dramstore/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ucm/store/dramstore/dramstore_connector.py b/ucm/store/dramstore/dramstore_connector.py deleted file mode 100644 index 24f4306bc..000000000 --- a/ucm/store/dramstore/dramstore_connector.py +++ /dev/null @@ -1,251 +0,0 @@ -# -# MIT License -# -# Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# - -from dataclasses import dataclass -from typing import Any, Dict, List, Optional - -import torch - -from ucm.logger import init_logger -from ucm.store.ucmstore import Task, UcmKVStoreBase - -logger = init_logger(__name__) - -SUCCESS = 0 -FAILURE = -1 - -if torch.cuda.is_available(): - device = torch.cuda -elif hasattr(torch, "musa") and torch.musa.is_available(): - device = torch.musa -elif hasattr(torch, "npu") and torch.npu.is_available(): - device = torch.npu -else: - raise RuntimeError( - "No supported accelerator found. " - "Please ensure either CUDA or NPU is available." - ) - - -@dataclass -class DramTask(Task): - task_id: str = "1" - event: Optional[Any] = None - - -class UcmDramStore(UcmKVStoreBase): - """ - Dram Connector - """ - - def __init__(self, config: Dict): - super().__init__(config) - self.dram_cache: Dict[str, any] = {} - self.max_cache_byte = int(config.get("max_cache_size", 5368709120)) - self.kv_block_size = int(config.get("kv_block_size", 262144)) - self.max_block_num = self.max_cache_byte // self.kv_block_size - if config["role"] == "scheduler": - self.cached_blocks = set() - - def cc_store(self) -> int: - """ - get the underlying implementation of Store - - Returns: - cc pointer to Store - """ - return 0 - - def create(self, block_ids: List[str]) -> List[int]: - """ - create kv cache space in storage - - Args: - block_ids (List[str]): vLLM block hash. - Returns: - success mask - """ - return [SUCCESS] * len(block_ids) - - def lookup(self, block_ids: List[str]) -> List[bool]: - """ - Get number of blocks that can be loaded from the - external KV cache. - - Args: - block_ids (List[str]): vLLM block hash. - - Returns: - hit block mask, True -> hit - """ - hit_list = [block_id in self.cached_blocks for block_id in block_ids] - return hit_list - - def prefetch(self, block_ids: List[str]) -> None: - """ - prefetch kv cache to high speed cache according to block_ids. - - Args: - block_ids (List[str]): vLLM block hash. - """ - pass - - def load( - self, block_ids: List[str], offset: List[int], dst_tensor: List[torch.Tensor] - ) -> Task: - """ - load kv cache to device. - - Args: - block_ids (List[str]): vLLM block hash. - offset(List[int]): tp > 1 scene - dst_tensor: List[torch.Tensor]: device tensor addr. - Returns: - task(Task). - """ - task = DramTask() - stream = device.Stream() - task.event = device.Event(enable_timing=True) - with device.stream(stream): - for i, block_id in enumerate(block_ids): - key = block_id + "_" + str(offset[i]) - dst_tensor[i].copy_(self.dram_cache[key], non_blocking=True) - task.event.record(stream=stream) - logger.debug(f"load block {block_ids} finished.") - return task - - def dump( - self, block_ids: List[str], offset: List[int], src_tensor: List[torch.Tensor] - ) -> Task: - """ - dump kv cache to device. - - Args: - block_ids (List[str]): vLLM block hash. - offset(List[int]): tp > 1 scene - src_tensor: List[torch.Tensor]: device tensor addr. - Returns: - task(Task). - """ - task = DramTask() - if len(self.dram_cache) > self.max_block_num: - logger.warning( - "Dram cache usage exceeds limit! No more kv cache offload! Try to increase your initial max_cache_size." - ) - task.task_id = "-1" - return task - else: - stream = device.Stream() - task.event = device.Event(enable_timing=True) - with device.stream(stream): - for i, block_id in enumerate(block_ids): - key = block_id + "_" + str(offset[i]) - self.dram_cache[key] = src_tensor[i].to("cpu", non_blocking=True) - task.event.record(stream=stream) - logger.debug(f"dump block {block_ids} finished.") - return task - - def fetch_data( - self, - block_ids: List[str], - offset: List[int], - dst_addr: List[int], - size: List[int], - ) -> Task: - """ - load kv cache data to device. - - Args: - block_ids (List[str]): vLLM block hash. - offset(List[int]): tp > 1 scene - dst_addr: List[int]: device tensor addr ptr. - size: List[int]: device tensor size. - Returns: - task(Task). - """ - pass - - def dump_data( - self, - block_ids: List[str], - offset: List[int], - src_addr: List[int], - size: List[int], - ) -> Task: - """ - dump kv cache data from device. - - Args: - block_ids (List[str]): vLLM block hash. - offset(List[int]): tp > 1 scene - src_addr: List[int]: device tensor addr ptr. - size: List[int]: device tensor size. - Returns: - task(Task). - """ - pass - - def wait(self, task: DramTask) -> int: - """ - wait kv cache kv transfer task finished. - - Args: - task (Task): transfer engine task. - Returns: - 0 - success - others - failed. - """ - if task.task_id == "-1": - logger.warning("Dump failure with full cache usage!") - return FAILURE - try: - event = task.event - event.synchronize() - return SUCCESS - except Exception as e: - logger.error(f"Error waiting cache for block IDs: {e}") - return FAILURE - - def commit(self, block_ids: List[str], is_success: bool = True) -> None: - """ - commit kv cache, now kv cache can be reused. - - Args: - block_ids (List[str]): vLLM block hash. - is_success(bool): if False, we need release block - """ - if is_success: - self.cached_blocks.update(block_ids) - - def check(self, task: Task) -> int: - """ - check if kv transfer task finished. - - Args: - task (Task): transfer engine task. - Returns: - 0 - finished - others - in process. - """ - pass diff --git a/ucm/store/factory.py b/ucm/store/factory.py index 8b893cda3..1db735506 100644 --- a/ucm/store/factory.py +++ b/ucm/store/factory.py @@ -57,9 +57,6 @@ def create_connector(cls, connector_name: str, config: dict) -> UcmKVStoreBase: return connector_cls(config) -UcmConnectorFactory.register_connector( - "UcmDramStore", "ucm.store.dramstore.dramstore_connector", "UcmDramStore" -) UcmConnectorFactory.register_connector( "UcmNfsStore", "ucm.store.nfsstore.nfsstore_connector", "UcmNfsStore" ) diff --git a/ucm/store/factory_v1.py b/ucm/store/factory_v1.py new file mode 100644 index 000000000..689fec7c9 --- /dev/null +++ b/ucm/store/factory_v1.py @@ -0,0 +1,62 @@ +# +# MIT License +# +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +import importlib +from typing import Callable + +from ucm.logger import init_logger +from ucm.store.ucmstore_v1 import UcmKVStoreBaseV1 + +logger = init_logger(__name__) + + +class UcmConnectorFactoryV1: + _registry: dict[str, Callable[[], type[UcmKVStoreBaseV1]]] = {} + + @classmethod + def register_connector(cls, name: str, module_path: str, class_name: str) -> None: + """Register a connector with a lazy-loading module and class name.""" + if name in cls._registry: + raise ValueError(f"Connector '{name}' is already registered.") + + def loader() -> type[UcmKVStoreBaseV1]: + module = importlib.import_module(module_path) + return getattr(module, class_name) + + cls._registry[name] = loader + + @classmethod + def create_connector(cls, connector_name: str, config: dict) -> UcmKVStoreBaseV1: + if connector_name in cls._registry: + connector_cls = cls._registry[connector_name]() + else: + raise ValueError(f"Unsupported connector type: {connector_name}") + assert issubclass(connector_cls, UcmKVStoreBaseV1) + logger.info("Creating connector with name: %s", connector_name) + return connector_cls(config) + + +UcmConnectorFactoryV1.register_connector( + "UcmPcStoreV1", "ucm.store.pcstore.pcstore_connector_v1", "UcmPcStoreV1" +) diff --git a/ucm/store/infra/CMakeLists.txt b/ucm/store/infra/CMakeLists.txt deleted file mode 100644 index 6bc8dc4a4..000000000 --- a/ucm/store/infra/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_library(storeinfra STATIC) -target_include_directories(storeinfra PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -file(GLOB_RECURSE UCMSTORE_COMMON_FILE_SOURCE_FILES "file/*.cc") -target_sources(storeinfra PRIVATE ${UCMSTORE_COMMON_FILE_SOURCE_FILES}) -target_link_libraries(storeinfra PUBLIC - infra_status - infra_logger - infra_template - infra_thread - infra_time -) diff --git a/ucm/store/localstore/CMakeLists.txt b/ucm/store/localstore/CMakeLists.txt deleted file mode 100644 index 77e924a52..000000000 --- a/ucm/store/localstore/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -file(GLOB_RECURSE UCMSTORE_LOCAL_CC_SOURCE_FILES "./cc/*.cc") -add_library(localstore STATIC ${UCMSTORE_LOCAL_CC_SOURCE_FILES}) -target_include_directories(localstore PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/cc/api - ${CMAKE_CURRENT_SOURCE_DIR}/cc/domain -) -target_link_libraries(localstore PUBLIC storeinfra storetask) - -file(GLOB_RECURSE UCMSTORE_LOCAL_CPY_SOURCE_FILES "./cpy/*.cc") -pybind11_add_module(ucmlocalstore ${UCMSTORE_LOCAL_CPY_SOURCE_FILES}) -target_link_libraries(ucmlocalstore PRIVATE localstore) - -file(RELATIVE_PATH INSTALL_REL_PATH - ${CMAKE_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} -) -install(TARGETS ucmlocalstore LIBRARY DESTINATION ${INSTALL_REL_PATH} COMPONENT ucm) diff --git a/ucm/store/localstore/__init__.py b/ucm/store/localstore/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ucm/store/localstore/cc/api/localstore.cc b/ucm/store/localstore/cc/api/localstore.cc deleted file mode 100644 index 15f5d8bc3..000000000 --- a/ucm/store/localstore/cc/api/localstore.cc +++ /dev/null @@ -1,96 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "localstore.h" -#include "logger/logger.h" -#include "status/status.h" - -namespace UC { - -class LocalStoreWithoutBackendImpl : public LocalStore { -public: - int32_t Setup(const size_t ioSize, const size_t capacity, const int32_t deviceId) { return -1; } - int32_t Alloc(const std::string& block) override { return -1; } - bool Lookup(const std::string& block) override { return false; } - void Commit(const std::string& block, const bool success) override {} - std::list Alloc(const std::list& blocks) override - { - return std::list(); - } - std::list Lookup(const std::list& blocks) override - { - return std::list(); - } - void Commit(const std::list& blocks, const bool success) override {} - size_t Submit(Task&& task) override { return 0; } - int32_t Wait(const size_t task) override { return -1; } - int32_t Check(const size_t task, bool& finish) override { return -1; } -}; - -class LocalStoreWithBackendImpl : public LocalStore { -public: - int32_t Setup(const size_t ioSize, const size_t capacity, void* backend, const int32_t deviceId) - { - return -1; - } - int32_t Alloc(const std::string& block) override { return -1; } - bool Lookup(const std::string& block) override { return false; } - void Commit(const std::string& block, const bool success) override {} - std::list Alloc(const std::list& blocks) override - { - return std::list(); - } - std::list Lookup(const std::list& blocks) override - { - return std::list(); - } - void Commit(const std::list& blocks, const bool success) override {} - size_t Submit(Task&& task) override { return 0; } - int32_t Wait(const size_t task) override { return -1; } - int32_t Check(const size_t task, bool& finish) override { return -1; } - -private: - CCStore* backend_; -}; - -int32_t LocalStore::Setup(const Config& config) -{ - if (config.backend) { - auto impl = new (std::nothrow) LocalStoreWithBackendImpl(); - if (!impl) { - UC_ERROR("Out of memory."); - return Status::OutOfMemory().Underlying(); - } - this->impl_ = impl; - return impl->Setup(config.ioSize, config.capacity, config.backend, config.deviceId); - } - auto impl = new (std::nothrow) LocalStoreWithoutBackendImpl(); - if (!impl) { - UC_ERROR("Out of memory."); - return Status::OutOfMemory().Underlying(); - } - this->impl_ = impl; - return impl->Setup(config.ioSize, config.capacity, config.deviceId); -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/api/localstore.h b/ucm/store/localstore/cc/api/localstore.h deleted file mode 100644 index edca1e0b1..000000000 --- a/ucm/store/localstore/cc/api/localstore.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_LOCALSTORE_H -#define UNIFIEDCACHE_LOCALSTORE_H - -#include "ucmstore.h" - -namespace UC { - -class LocalStore : public CCStore<> { -public: - struct Config { - size_t ioSize; - size_t capacity; - void* backend; - int32_t deviceId; - Config(const size_t ioSize, const size_t capacity) - : ioSize{ioSize}, capacity{capacity}, backend{nullptr}, deviceId{-1} - { - } - }; - -public: - LocalStore() : impl_{nullptr} {} - ~LocalStore() override - { - if (this->impl_) { delete this->impl_; } - } - int32_t Setup(const Config& config); - int32_t Alloc(const std::string& block) override { return this->impl_->Alloc(block); } - bool Lookup(const std::string& block) override { return this->impl_->Lookup(block); } - void Commit(const std::string& block, const bool success) override - { - this->impl_->Commit(block, success); - } - std::list Alloc(const std::list& blocks) override - { - return this->impl_->Alloc(blocks); - } - std::list Lookup(const std::list& blocks) override - { - return this->impl_->Lookup(blocks); - } - void Commit(const std::list& blocks, const bool success) override - { - this->impl_->Commit(blocks, success); - } - size_t Submit(Task&& task) override { return this->impl_->Submit(std::move(task)); } - int32_t Wait(const size_t task) override { return this->impl_->Wait(task); } - int32_t Check(const size_t task, bool& finish) override - { - return this->impl_->Check(task, finish); - } - -private: - LocalStore* impl_; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/localstore/cc/domain/cache/cache_data.cc b/ucm/store/localstore/cc/domain/cache/cache_data.cc deleted file mode 100644 index d17190dd8..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_data.cc +++ /dev/null @@ -1,47 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_data.h" - -namespace UC { - -Status CacheData::Setup(const size_t ioSize, const size_t ioNumber) -{ - auto segmentSize = CacheSegment::TotalSize(); - this->ioNumberPerSegment_ = segmentSize / ioSize; - auto segmentNumber = (ioNumber + this->ioNumberPerSegment_ - 1) / this->ioNumberPerSegment_; - this->segments_.reserve(segmentNumber); - for (size_t i = 0; i < segmentNumber; i++) { - auto& segment = this->segments_.emplace_back(); - auto status = segment.Setup(i, ioSize); - if (status.Failure()) { return status; } - } - return Status::OK(); -} - -void* CacheData::At(const size_t index) -{ - return this->segments_[index / this->ioNumberPerSegment_].At(index % this->ioNumberPerSegment_); -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_hash.cc b/ucm/store/localstore/cc/domain/cache/cache_hash.cc deleted file mode 100644 index 6f0325f53..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_hash.cc +++ /dev/null @@ -1,295 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_hash.h" -#include -#include - -namespace UC { - -static constexpr uint32_t BucketNumber = 17683; -static constexpr uint32_t Magic = (('C' << 16) | ('h' << 8) | 1); - -struct Key { - uint64_t k1, k2; - size_t off; - Key() { this->Init(); } - Key(const std::string& id, const size_t off) { this->Fill(id, off); } - bool operator==(const Key& k) const { return k1 == k.k1 && k2 == k.k2 && off == k.off; } - void Init() { this->k1 = this->k2 = this->off = 0; } - void Fill(const std::string& id, const size_t off) - { - auto idPair = static_cast(static_cast(id.data())); - this->k1 = idPair[0]; - this->k2 = idPair[1]; - this->off = off; - } - uint32_t Hash() - { - static std::hash hasher{}; - return (hasher(k1) | hasher(k2) | hasher(off)) % BucketNumber; - } -}; - -struct Node { - Key key; - uint32_t prev; - uint32_t next; - std::atomic ref; - uint64_t tp; - void Init() - { - this->key.Init(); - this->prev = CacheHash::npos; - this->next = CacheHash::npos; - this->ref = 0; - } -}; - -struct Bucket { - pthread_mutex_t mutex; - uint32_t head; - uint32_t tail; - void Init() - { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_mutex_init(&this->mutex, &attr); - pthread_mutexattr_destroy(&attr); - this->head = CacheHash::npos; - this->tail = CacheHash::npos; - } - void Lock() { pthread_mutex_lock(&this->mutex); } - void Unlock() { pthread_mutex_unlock(&this->mutex); } -}; - -struct Header { - uint32_t magic; - uint32_t capacity; - std::atomic tp; - uint64_t reserved[7]; - Bucket buckets[BucketNumber]; - Node nodes[0]; -}; - -inline auto HeaderPtr(void* addr) { return (Header*)addr; } - -inline void InsertNode2Bucket(Header* header, Bucket* bucket, const uint32_t index) -{ - auto slot = header->nodes + index; - slot->prev = CacheHash::npos; - slot->next = bucket->head; - if (bucket->head != CacheHash::npos) { - auto next = header->nodes + bucket->head; - next->prev = index; - } - bucket->head = index; - if (bucket->tail == CacheHash::npos) { bucket->tail = index; } -} - -inline void MoveNode2BucketHead(Header* header, Bucket* bucket, const uint32_t index) -{ - if (bucket->head == index) { return; } - auto slot = header->nodes + index; - if (bucket->tail == index) { - auto tail = header->nodes + slot->prev; - tail->next = CacheHash::npos; - bucket->tail = slot->prev; - slot->prev = CacheHash::npos; - } else { - auto prev = header->nodes + slot->prev; - auto next = header->nodes + slot->next; - prev->next = slot->next; - next->prev = slot->prev; - } - auto head = header->nodes + bucket->head; - head->prev = index; - slot->next = bucket->head; - bucket->head = index; -} - -inline void RemoveNodeFromBucket(Header* header, Bucket* bucket, const uint32_t index) -{ - auto slot = header->nodes + index; - if (bucket->head == index && bucket->tail == index) { - bucket->head = bucket->tail = CacheHash::npos; - return; - } - if (bucket->head != index && bucket->tail != index) { - auto prev = header->nodes + slot->prev; - auto next = header->nodes + slot->next; - prev->next = slot->next; - next->prev = slot->prev; - return; - } - if (bucket->head == index) { - bucket->head = slot->next; - auto head = header->nodes + slot->next; - head->prev = CacheHash::npos; - return; - } - if (bucket->tail == index) { - bucket->tail = slot->prev; - auto tail = header->nodes + slot->prev; - tail->next = CacheHash::npos; - return; - } -} - -size_t CacheHash::MemorySize() const noexcept -{ - return sizeof(Header) * sizeof(Node) * this->capacity_; -} - -void CacheHash::Setup(void* addr) noexcept -{ - this->addr_ = addr; - auto header = HeaderPtr(this->addr_); - if (header->magic == Magic) { return; } - header->capacity = this->capacity_; - header->tp.store(0, std::memory_order_relaxed); - auto reservedSize = sizeof(header->reserved) / sizeof(*header->reserved); - std::fill_n(header->reserved, reservedSize, 0); - for (uint32_t i = 0; i < BucketNumber; i++) { header->buckets[i].Init(); } - for (uint32_t i = 0; i < header->capacity; i++) { header->nodes[i].Init(); } - header->magic = Magic; - return; -} - -void CacheHash::Insert(const std::string& id, const size_t offset, const uint32_t index) noexcept -{ - auto header = HeaderPtr(this->addr_); - auto slot = header->nodes + index; - slot->key.Fill(id, offset); - auto bucket = header->buckets + slot->key.Hash(); - bucket->Lock(); - slot->ref.store(1, std::memory_order_relaxed); - slot->tp = header->tp.fetch_add(1); - InsertNode2Bucket(header, bucket, index); - bucket->Unlock(); -} - -uint32_t CacheHash::Find(const std::string& id, const size_t offset) noexcept -{ - Key key{id, offset}; - auto header = HeaderPtr(this->addr_); - auto bucket = header->buckets + key.Hash(); - bucket->Lock(); - auto pos = bucket->head; - while (pos != npos) { - auto slot = header->nodes + pos; - if (slot->key == key) { - slot->ref++; - slot->tp = header->tp.fetch_add(1); - MoveNode2BucketHead(header, bucket, pos); - break; - } - pos = slot->next; - } - bucket->Unlock(); - return pos; -} - -void CacheHash::PutRef(const uint32_t index) noexcept -{ - auto header = HeaderPtr(this->addr_); - if (index >= header->capacity) { return; } - auto slot = header->nodes + index; - auto ref = slot->ref.load(std::memory_order_acquire); - while (ref > 0) { - auto desired = ref - 1; - if (slot->ref.compare_exchange_weak(ref, desired, std::memory_order_acq_rel)) { break; } - ref = slot->ref.load(std::memory_order_acquire); - } -} - -void CacheHash::PutRef(const std::string& id, const size_t offset) noexcept -{ - Key key{id, offset}; - auto header = HeaderPtr(this->addr_); - auto bucket = header->buckets + key.Hash(); - bucket->Lock(); - auto pos = bucket->head; - while (pos != npos) { - auto slot = header->nodes + pos; - if (slot->key == key) { - this->PutRef(pos); - break; - } - pos = slot->next; - } - bucket->Unlock(); -} - -void CacheHash::Remove(const std::string& id, const size_t offset) noexcept -{ - Key key{id, offset}; - auto header = HeaderPtr(this->addr_); - auto bucket = header->buckets + key.Hash(); - bucket->Lock(); - auto pos = bucket->head; - while (pos != npos) { - auto slot = header->nodes + pos; - if (slot->key == key) { - RemoveNodeFromBucket(header, bucket, pos); - break; - } - pos = slot->next; - } - bucket->Unlock(); -} - -uint32_t CacheHash::Evict() noexcept -{ - auto header = HeaderPtr(this->addr_); - auto iBucket = npos; - auto pos = npos; - auto tp = (uint64_t)(-1); - for (uint32_t i = 0; i < BucketNumber; i++) { - auto bucket = header->buckets + i; - bucket->Lock(); - if (bucket->tail != npos) { - auto slot = header->nodes + bucket->tail; - if (slot->ref == 0 && tp > slot->tp) { - iBucket = i; - pos = bucket->tail; - tp = slot->tp; - } - } - bucket->Unlock(); - } - if (iBucket == npos) { return npos; } - auto bucket = header->buckets + iBucket; - bucket->Lock(); - auto slot = header->nodes + pos; - if (bucket->tail != pos || slot->ref != 0) { - pos = npos; - } else { - RemoveNodeFromBucket(header, bucket, pos); - } - bucket->Unlock(); - return pos; -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_index.cc b/ucm/store/localstore/cc/domain/cache/cache_index.cc deleted file mode 100644 index 4682dd096..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_index.cc +++ /dev/null @@ -1,105 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_index.h" -#include -#include - -namespace UC { - -struct Node { - uint32_t idx; - uint32_t next; -}; -struct Pointer { - uint32_t slot; - uint32_t ver; -}; -struct Header { - uint32_t magic; - uint32_t capacity; - alignas(64) std::atomic pointer; - uint64_t padding[7]; - Node nodes[0]; -}; -static_assert(sizeof(Pointer) == 8, "Pointer must be 64-bit"); -static_assert(sizeof(Header) == 128, "Header must be 128-Byte"); - -static constexpr uint32_t Magic = (('C' << 16) | ('i' << 8) | 1); -inline auto HeaderPtr(void* addr) { return (Header*)addr; } - -size_t CacheIndex::MemorySize() const noexcept -{ - return sizeof(Header) + sizeof(Node) * (this->capacity_ + 1); -} - -void CacheIndex::Setup(void* addr) noexcept -{ - this->addr_ = addr; - auto header = HeaderPtr(this->addr_); - if (header->magic == Magic) { return; } - header->capacity = this->capacity_; - header->pointer.store({1, 0}); - auto paddingSize = sizeof(header->padding) / sizeof(*header->padding); - std::fill_n(header->padding, paddingSize, 0); - for (uint32_t slot = 1; slot <= header->capacity; slot++) { - header->nodes[slot].idx = slot - 1; - header->nodes[slot].next = slot + 1; - } - header->nodes[header->capacity].next = 0; - header->magic = Magic; - return; -} - -uint32_t CacheIndex::Acquire() noexcept -{ - auto header = HeaderPtr(this->addr_); - for (;;) { - auto ptr = header->pointer.load(std::memory_order_acquire); - if (ptr.slot == 0) { return npos; } - auto next = header->nodes[ptr.slot].next; - Pointer desired{next, ptr.ver + 1}; - if (header->pointer.compare_exchange_weak(ptr, desired, std::memory_order_release, - std::memory_order_relaxed)) { - return header->nodes[ptr.slot].idx; - } - } -} - -void CacheIndex::Release(const uint32_t idx) noexcept -{ - auto header = HeaderPtr(this->addr_); - if (idx >= header->capacity) { return; } - auto slot = idx + 1; - for (;;) { - auto ptr = header->pointer.load(std::memory_order_acquire); - header->nodes[slot].next = ptr.slot; - Pointer desired{slot, ptr.ver + 1}; - if (header->pointer.compare_exchange_weak(ptr, desired, std::memory_order_release, - std::memory_order_relaxed)) { - return; - } - } -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_index.h b/ucm/store/localstore/cc/domain/cache/cache_index.h deleted file mode 100644 index 45d0e826d..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_index.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_CACHE_INDEX_H -#define UNIFIEDCACHE_CACHE_INDEX_H - -#include -#include -#include - -namespace UC { - -class CacheIndex { - uint32_t capacity_; - void* addr_; - -public: - static constexpr uint32_t npos = std::numeric_limits::max(); - CacheIndex() noexcept : capacity_{0}, addr_{nullptr} {} - void Setup(const uint32_t capacity) noexcept { this->capacity_ = capacity; } - size_t MemorySize() const noexcept; - void Setup(void* addr) noexcept; - uint32_t Acquire() noexcept; - void Release(const uint32_t idx) noexcept; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/localstore/cc/domain/cache/cache_instance.cc b/ucm/store/localstore/cc/domain/cache/cache_instance.cc deleted file mode 100644 index 3d7543a8c..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_instance.cc +++ /dev/null @@ -1,52 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_instance.h" - -namespace UC { - -Status CacheInstance::Setup(const size_t ioSize, const uint32_t capacity) noexcept -{ - auto status = this->meta_.Setup(capacity); - if (status.Failure()) { return status; } - return this->data_.Setup(ioSize, capacity); -} - -void* CacheInstance::Alloc(const std::string& id, const size_t offset) noexcept -{ - auto index = this->meta_.Alloc(id, offset); - return index != this->meta_.npos ? this->data_.At(index) : nullptr; -} - -void* CacheInstance::Find(const std::string& id, const size_t offset) noexcept -{ - auto index = this->meta_.Find(id, offset); - return index != this->meta_.npos ? this->data_.At(index) : nullptr; -} - -void CacheInstance::PutRef(const std::string& id, const size_t offset) noexcept -{ - this->meta_.PutRef(id, offset); -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_instance.h b/ucm/store/localstore/cc/domain/cache/cache_instance.h deleted file mode 100644 index 2e7839b69..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_instance.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_CACHE_INSTANCE_H -#define UNIFIEDCACHE_CACHE_INSTANCE_H - -#include "cache_data.h" -#include "cache_meta.h" - -namespace UC { - -class CacheInstance { - CacheMeta meta_; - CacheData data_; - -public: - Status Setup(const size_t ioSize, const uint32_t capacity) noexcept; - void* Alloc(const std::string& id, const size_t offset) noexcept; - void* Find(const std::string& id, const size_t offset) noexcept; - void PutRef(const std::string& id, const size_t offset) noexcept; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/localstore/cc/domain/cache/cache_layout.cc b/ucm/store/localstore/cc/domain/cache/cache_layout.cc deleted file mode 100644 index 236dc1402..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_layout.cc +++ /dev/null @@ -1,38 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_layout.h" -#include - -namespace UC { - -static const std::string ShmFilePrefix = "/ucmlocal"; - -std::string CacheLayout::MetaShmFile() { return fmt::format("{}.meta", ShmFilePrefix); } - -std::string CacheLayout::DataShmFile(const size_t id) -{ - return fmt::format("{}.dat{:06}", ShmFilePrefix, id); -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_meta.cc b/ucm/store/localstore/cc/domain/cache/cache_meta.cc deleted file mode 100644 index 057f8a31d..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_meta.cc +++ /dev/null @@ -1,147 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_meta.h" -#include -#include -#include -#include "cache_layout.h" -#include "file/file.h" -#include "logger/logger.h" - -namespace UC { - -static constexpr uint32_t Magic = (('C' << 16) | ('m' << 8) | 1); - -struct Header { - std::atomic magic; - uint32_t padding; - uint64_t reserved[7]; -}; - -CacheMeta::~CacheMeta() -{ - if (this->addr_) { - File::MUnmap(this->addr_, this->size_); - File::ShmUnlink(CacheLayout::MetaShmFile()); - } - this->addr_ = nullptr; -} - -Status CacheMeta::Setup(const Index capacity) noexcept -{ - this->index_.Setup(capacity); - this->hash_.Setup(capacity); - this->size_ = this->index_.MemorySize() + this->hash_.MemorySize(); - auto file = File::Make(CacheLayout::MetaShmFile()); - if (!file) { return Status::OutOfMemory(); } - auto openFlags = IFile::OpenFlag::CREATE | IFile::OpenFlag::EXCL | IFile::OpenFlag::READ_WRITE; - auto status = file->ShmOpen(openFlags); - if (status.Success()) { return this->InitShmMeta(file.get()); } - if (status == Status::DuplicateKey()) { return this->LoadShmMeta(file.get()); } - return status; -} - -CacheMeta::Index CacheMeta::Alloc(const std::string& id, const size_t offset) noexcept -{ - auto index = this->index_.Acquire(); - if (index != CacheIndex::npos) { - this->hash_.Insert(id, offset, index); - return index; - } - auto evict = this->hash_.Evict(); - if (evict != CacheHash::npos) { this->index_.Release(evict); } - return npos; -} - -CacheMeta::Index CacheMeta::Find(const std::string& id, const size_t offset) noexcept -{ - auto idx = this->hash_.Find(id, offset); - return idx != CacheHash::npos ? idx : npos; -} - -void CacheMeta::PutRef(const uint32_t index) noexcept { this->hash_.PutRef(index); } - -void CacheMeta::PutRef(const std::string& id, const size_t offset) noexcept -{ - this->hash_.PutRef(id, offset); -} - -Status CacheMeta::InitShmMeta(IFile* shmMetaFile) -{ - auto status = shmMetaFile->Truncate(this->size_); - if (status.Failure()) { return status; } - status = shmMetaFile->MMap(this->addr_, this->size_, true, true, true); - if (status.Failure()) { return status; } - auto header = (Header*)this->addr_; - auto indexBase = (void*)(((uint8_t*)this->addr_) + sizeof(Header)); - auto hashBase = (void*)(((uint8_t*)indexBase) + this->index_.MemorySize()); - this->index_.Setup(indexBase); - this->hash_.Setup(hashBase); - header->padding = 0; - auto reservedSize = sizeof(header->reserved) / sizeof(*header->reserved); - std::fill_n(header->reserved, reservedSize, 0); - header->magic = Magic; - return Status::OK(); -} - -Status CacheMeta::LoadShmMeta(IFile* shmMetaFile) -{ - auto openFlags = IFile::OpenFlag::READ_WRITE; - auto status = shmMetaFile->ShmOpen(openFlags); - if (status.Failure()) { return status; } - constexpr auto retryInterval = std::chrono::milliseconds(100); - constexpr auto maxTryTime = 100; - auto tryTime = 0; - IFile::FileStat stat; - do { - if (tryTime > maxTryTime) { - UC_ERROR("Shm file({}) not ready.", shmMetaFile->Path()); - return Status::Retry(); - } - std::this_thread::sleep_for(retryInterval); - status = shmMetaFile->Stat(stat); - if (status.Failure()) { return status; } - tryTime++; - } while (static_cast(stat.st_size) != this->size_); - status = shmMetaFile->MMap(this->addr_, this->size_, true, true, true); - if (status.Failure()) { return status; } - auto header = (Header*)this->addr_; - tryTime = 0; - do { - if (header->magic == Magic) { break; } - if (tryTime > maxTryTime) { - UC_ERROR("Shm file({}) not ready.", shmMetaFile->Path()); - return Status::Retry(); - } - std::this_thread::sleep_for(retryInterval); - tryTime++; - } while (true); - auto indexBase = (void*)(((uint8_t*)this->addr_) + sizeof(Header)); - auto hashBase = (void*)(((uint8_t*)indexBase) + this->index_.MemorySize()); - this->index_.Setup(indexBase); - this->hash_.Setup(hashBase); - return Status::OK(); -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_segment.cc b/ucm/store/localstore/cc/domain/cache/cache_segment.cc deleted file mode 100644 index abf7eef52..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_segment.cc +++ /dev/null @@ -1,60 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cache_segment.h" -#include "cache_layout.h" -#include "file/file.h" - -namespace UC { - -CacheSegment::~CacheSegment() -{ - if (this->base_) { - File::MUnmap(this->base_, TotalSize()); - File::ShmUnlink(CacheLayout::DataShmFile(this->id_)); - } - this->base_ = nullptr; -} - -Status CacheSegment::Setup(const size_t id, const size_t ioSize) -{ - auto path = CacheLayout::DataShmFile(this->id_); - auto file = File::Make(path); - if (!file) { return Status::OutOfMemory(); } - auto status = Status::OK(); - auto openFlags = IFile::OpenFlag::CREATE | IFile::OpenFlag::READ_WRITE; - if ((status = file->ShmOpen(openFlags)).Failure()) { return status; } - auto size = TotalSize(); - if ((status = file->Truncate(size)).Failure()) { return status; } - if ((status = file->MMap(this->base_, size, true, true, true)).Failure()) { return status; } - return status; -} - -void* CacheSegment::At(const size_t idx) const -{ - auto addr = (uint8_t*)this->base_; - auto offset = this->ioSize_ * idx; - return addr + offset; -} - -} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_segment.h b/ucm/store/localstore/cc/domain/cache/cache_segment.h deleted file mode 100644 index 1cefd5f91..000000000 --- a/ucm/store/localstore/cc/domain/cache/cache_segment.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_CACHE_SEGMENT_H -#define UNIFIEDCACHE_CACHE_SEGMENT_H - -#include -#include "status/status.h" - -namespace UC { - -class CacheSegment { - size_t id_; - size_t ioSize_; - void* base_; - -public: - static size_t TotalSize() { return 1ULL << 30; } - CacheSegment() : id_{0}, ioSize_{0}, base_{nullptr} {} - ~CacheSegment(); - Status Setup(const size_t id, const size_t ioSize); - void* At(const size_t idx) const; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/localstore/cpy/localstore.py.cc b/ucm/store/localstore/cpy/localstore.py.cc deleted file mode 100644 index c067df231..000000000 --- a/ucm/store/localstore/cpy/localstore.py.cc +++ /dev/null @@ -1,120 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "localstore.h" -#include - -namespace py = pybind11; - -namespace UC { - -class LocalStorePy : public LocalStore { -public: - void* CCStoreImpl() { return this; } - py::list AllocBatch(const py::list& blocks) - { - py::list results; - for (auto& block : blocks) { results.append(this->Alloc(block.cast())); } - return results; - } - py::list LookupBatch(const py::list& blocks) - { - py::list founds; - for (auto& block : blocks) { founds.append(this->Lookup(block.cast())); } - return founds; - } - void CommitBatch(const py::list& blocks, const bool success) - { - for (auto& block : blocks) { this->Commit(block.cast(), success); } - } - py::tuple CheckPy(const size_t task) - { - auto finish = false; - auto ret = this->Check(task, finish); - return py::make_tuple(ret, finish); - } - size_t Load(const py::list& blockIds, const py::list& offsets, const py::list& addresses, - const py::list& lengths) - { - return this->SubmitPy(blockIds, offsets, addresses, lengths, Task::Type::LOAD, - Task::Location::DEVICE, "LOCAL::S2D"); - } - size_t Dump(const py::list& blockIds, const py::list& offsets, const py::list& addresses, - const py::list& lengths) - { - return this->SubmitPy(blockIds, offsets, addresses, lengths, Task::Type::DUMP, - Task::Location::DEVICE, "LOCAL::D2S"); - } - -private: - size_t SubmitPy(const py::list& blockIds, const py::list& offsets, const py::list& addresses, - const py::list& lengths, Task::Type&& type, Task::Location&& location, - std::string&& brief) - { - Task task{std::move(type), std::move(location), std::move(brief)}; - auto blockId = blockIds.begin(); - auto offset = offsets.begin(); - auto address = addresses.begin(); - auto length = lengths.begin(); - while ((blockId != blockIds.end()) && (offset != offsets.end()) && - (address != addresses.end()) && (length != lengths.end())) { - task.Append(blockId->cast(), offset->cast(), - address->cast(), length->cast()); - blockId++; - offset++; - address++; - length++; - } - return this->Submit(std::move(task)); - } -}; - -} // namespace UC - -PYBIND11_MODULE(ucmlocalstore, module) -{ - module.attr("project") = UCM_PROJECT_NAME; - module.attr("version") = UCM_PROJECT_VERSION; - module.attr("commit_id") = UCM_COMMIT_ID; - module.attr("build_type") = UCM_BUILD_TYPE; - auto store = py::class_(module, "LocalStore"); - auto config = py::class_(store, "Config"); - config.def(py::init(), py::arg("ioSize"), py::arg("capacity")); - config.def_readwrite("ioSize", &UC::LocalStorePy::Config::ioSize); - config.def_readwrite("capacity", &UC::LocalStorePy::Config::capacity); - config.def_readwrite("backend", &UC::LocalStorePy::Config::backend); - config.def_readwrite("deviceId", &UC::LocalStorePy::Config::deviceId); - store.def("CCStoreImpl", &UC::LocalStorePy::CCStoreImpl); - store.def("Setup", &UC::LocalStorePy::Setup); - store.def("Alloc", py::overload_cast(&UC::LocalStorePy::Alloc)); - store.def("AllocBatch", &UC::LocalStorePy::AllocBatch); - store.def("Lookup", py::overload_cast(&UC::LocalStorePy::Lookup)); - store.def("LookupBatch", &UC::LocalStorePy::LookupBatch); - store.def("Load", &UC::LocalStorePy::Load); - store.def("Dump", &UC::LocalStorePy::Dump); - store.def("Wait", &UC::LocalStorePy::Wait); - store.def("Check", &UC::LocalStorePy::Check); - store.def("Commit", - py::overload_cast(&UC::LocalStorePy::Commit)); - store.def("CommitBatch", &UC::LocalStorePy::CommitBatch); -} diff --git a/ucm/store/nfsstore/CMakeLists.txt b/ucm/store/nfsstore/CMakeLists.txt index 7856c89e0..b48376278 100644 --- a/ucm/store/nfsstore/CMakeLists.txt +++ b/ucm/store/nfsstore/CMakeLists.txt @@ -1,10 +1,12 @@ +add_subdirectory(device) file(GLOB_RECURSE UCMSTORE_NFS_CC_SOURCE_FILES "./cc/*.cc") add_library(nfsstore STATIC ${UCMSTORE_NFS_CC_SOURCE_FILES}) target_include_directories(nfsstore PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cc/api ${CMAKE_CURRENT_SOURCE_DIR}/cc/domain ) -target_link_libraries(nfsstore PUBLIC storeinfra storedevice storetask) +target_link_libraries(nfsstore PUBLIC storeintf storedevice infra_logger) file(GLOB_RECURSE UCMSTORE_NFS_CPY_SOURCE_FILES "./cpy/*.cc") pybind11_add_module(ucmnfsstore ${UCMSTORE_NFS_CPY_SOURCE_FILES}) diff --git a/ucm/store/infra/file/file.cc b/ucm/store/nfsstore/cc/domain/file/file.cc similarity index 100% rename from ucm/store/infra/file/file.cc rename to ucm/store/nfsstore/cc/domain/file/file.cc diff --git a/ucm/store/infra/file/file.h b/ucm/store/nfsstore/cc/domain/file/file.h similarity index 100% rename from ucm/store/infra/file/file.h rename to ucm/store/nfsstore/cc/domain/file/file.h diff --git a/ucm/store/infra/file/ifile.h b/ucm/store/nfsstore/cc/domain/file/ifile.h similarity index 100% rename from ucm/store/infra/file/ifile.h rename to ucm/store/nfsstore/cc/domain/file/ifile.h diff --git a/ucm/store/infra/file/posix_file.cc b/ucm/store/nfsstore/cc/domain/file/posix_file.cc similarity index 100% rename from ucm/store/infra/file/posix_file.cc rename to ucm/store/nfsstore/cc/domain/file/posix_file.cc diff --git a/ucm/store/infra/file/posix_file.h b/ucm/store/nfsstore/cc/domain/file/posix_file.h similarity index 100% rename from ucm/store/infra/file/posix_file.h rename to ucm/store/nfsstore/cc/domain/file/posix_file.h diff --git a/ucm/store/nfsstore/cc/domain/trans/posix_queue.h b/ucm/store/nfsstore/cc/domain/trans/posix_queue.h index 0e2dc17f8..9083e23e5 100644 --- a/ucm/store/nfsstore/cc/domain/trans/posix_queue.h +++ b/ucm/store/nfsstore/cc/domain/trans/posix_queue.h @@ -27,8 +27,8 @@ #include "device/idevice.h" #include "space/space_layout.h" #include "status/status.h" -#include "task_queue.h" -#include "task_set.h" +#include "task/task_queue.h" +#include "task/task_set.h" #include "thread/thread_pool.h" namespace UC { @@ -45,7 +45,8 @@ class PosixQueue : public TaskQueue { public: Status Setup(const int32_t deviceId, const size_t bufferSize, const size_t bufferNumber, - TaskSet* failureSet, const SpaceLayout* layout, const size_t timeoutMs, bool useDirect = false); + TaskSet* failureSet, const SpaceLayout* layout, const size_t timeoutMs, + bool useDirect = false); void Push(std::list& shards) noexcept override; private: @@ -59,6 +60,6 @@ class PosixQueue : public TaskQueue { Status S2H(Task::Shard& shard); }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/nfsstore/cc/domain/trans/trans_manager.h b/ucm/store/nfsstore/cc/domain/trans/trans_manager.h index 06748042e..6d66ed302 100644 --- a/ucm/store/nfsstore/cc/domain/trans/trans_manager.h +++ b/ucm/store/nfsstore/cc/domain/trans/trans_manager.h @@ -25,7 +25,7 @@ #define UNIFIEDCACHE_TRANS_MANAGER_H #include "posix_queue.h" -#include "task_manager.h" +#include "task/task_manager.h" namespace UC { diff --git a/ucm/store/device/CMakeLists.txt b/ucm/store/nfsstore/device/CMakeLists.txt similarity index 100% rename from ucm/store/device/CMakeLists.txt rename to ucm/store/nfsstore/device/CMakeLists.txt diff --git a/ucm/store/device/ascend/CMakeLists.txt b/ucm/store/nfsstore/device/ascend/CMakeLists.txt similarity index 83% rename from ucm/store/device/ascend/CMakeLists.txt rename to ucm/store/nfsstore/device/ascend/CMakeLists.txt index c5e40fc6a..c8ae7de2c 100644 --- a/ucm/store/device/ascend/CMakeLists.txt +++ b/ucm/store/nfsstore/device/ascend/CMakeLists.txt @@ -6,4 +6,4 @@ set_target_properties(Ascend::ascendcl PROPERTIES ) add_library(storedevice STATIC ascend_device.cc) -target_link_libraries(storedevice PUBLIC storeinfra Ascend::ascendcl) +target_link_libraries(storedevice PUBLIC infra_status Ascend::ascendcl) diff --git a/ucm/store/device/ascend/ascend_device.cc b/ucm/store/nfsstore/device/ascend/ascend_device.cc similarity index 100% rename from ucm/store/device/ascend/ascend_device.cc rename to ucm/store/nfsstore/device/ascend/ascend_device.cc diff --git a/ucm/store/device/cuda/CMakeLists.txt b/ucm/store/nfsstore/device/cuda/CMakeLists.txt similarity index 86% rename from ucm/store/device/cuda/CMakeLists.txt rename to ucm/store/nfsstore/device/cuda/CMakeLists.txt index fa0db292d..3969b8e64 100644 --- a/ucm/store/device/cuda/CMakeLists.txt +++ b/ucm/store/nfsstore/device/cuda/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_CUDA_COMPILER ${CUDA_ROOT}/bin/nvcc) set(CMAKE_CUDA_ARCHITECTURES 75 80 86 89 90) enable_language(CUDA) add_library(storedevice STATIC cuda_device.cu) -target_link_libraries(storedevice PUBLIC storeinfra) +target_link_libraries(storedevice PUBLIC infra_status) target_compile_options(storedevice PRIVATE --diag-suppress=128 --diag-suppress=2417 --diag-suppress=2597 -Wall -fPIC diff --git a/ucm/store/device/cuda/cuda_device.cu b/ucm/store/nfsstore/device/cuda/cuda_device.cu similarity index 100% rename from ucm/store/device/cuda/cuda_device.cu rename to ucm/store/nfsstore/device/cuda/cuda_device.cu diff --git a/ucm/store/device/ibuffered_device.h b/ucm/store/nfsstore/device/ibuffered_device.h similarity index 100% rename from ucm/store/device/ibuffered_device.h rename to ucm/store/nfsstore/device/ibuffered_device.h diff --git a/ucm/store/device/idevice.h b/ucm/store/nfsstore/device/idevice.h similarity index 100% rename from ucm/store/device/idevice.h rename to ucm/store/nfsstore/device/idevice.h diff --git a/ucm/store/device/maca/CMakeLists.txt b/ucm/store/nfsstore/device/maca/CMakeLists.txt similarity index 90% rename from ucm/store/device/maca/CMakeLists.txt rename to ucm/store/nfsstore/device/maca/CMakeLists.txt index 69066670a..17589a826 100644 --- a/ucm/store/device/maca/CMakeLists.txt +++ b/ucm/store/nfsstore/device/maca/CMakeLists.txt @@ -16,5 +16,5 @@ target_include_directories(WCUDA::cudart INTERFACE /opt/maca/include/mcr ) -target_link_libraries(storedevice PUBLIC storeinfra WCUDA::cudart) +target_link_libraries(storedevice PUBLIC infra_status WCUDA::cudart) target_compile_options(storedevice PRIVATE -Wall -fPIC -std=c++17) diff --git a/ucm/store/device/maca/maca_device.cu b/ucm/store/nfsstore/device/maca/maca_device.cu similarity index 100% rename from ucm/store/device/maca/maca_device.cu rename to ucm/store/nfsstore/device/maca/maca_device.cu diff --git a/ucm/store/device/musa/CMakeLists.txt b/ucm/store/nfsstore/device/musa/CMakeLists.txt similarity index 82% rename from ucm/store/device/musa/CMakeLists.txt rename to ucm/store/nfsstore/device/musa/CMakeLists.txt index 2e1ff3a75..0d4ba5b51 100644 --- a/ucm/store/device/musa/CMakeLists.txt +++ b/ucm/store/nfsstore/device/musa/CMakeLists.txt @@ -6,4 +6,4 @@ set_target_properties(Musa::musart PROPERTIES ) add_library(storedevice STATIC musa_device.cc) -target_link_libraries(storedevice PUBLIC storeinfra Musa::musart) +target_link_libraries(storedevice PUBLIC infra_status Musa::musart) diff --git a/ucm/store/device/musa/musa_device.cc b/ucm/store/nfsstore/device/musa/musa_device.cc similarity index 100% rename from ucm/store/device/musa/musa_device.cc rename to ucm/store/nfsstore/device/musa/musa_device.cc diff --git a/ucm/store/device/musa/musa_device.mu b/ucm/store/nfsstore/device/musa/musa_device.mu similarity index 100% rename from ucm/store/device/musa/musa_device.mu rename to ucm/store/nfsstore/device/musa/musa_device.mu diff --git a/ucm/store/nfsstore/device/simu/CMakeLists.txt b/ucm/store/nfsstore/device/simu/CMakeLists.txt new file mode 100644 index 000000000..82115f3a8 --- /dev/null +++ b/ucm/store/nfsstore/device/simu/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(storedevice STATIC simu_device.cc) +target_link_libraries(storedevice PUBLIC infra_status) diff --git a/ucm/store/device/simu/simu_device.cc b/ucm/store/nfsstore/device/simu/simu_device.cc similarity index 99% rename from ucm/store/device/simu/simu_device.cc rename to ucm/store/nfsstore/device/simu/simu_device.cc index a26b71744..c16c62d05 100644 --- a/ucm/store/device/simu/simu_device.cc +++ b/ucm/store/nfsstore/device/simu/simu_device.cc @@ -79,7 +79,8 @@ class SimuDevice : public IBufferedDevice { } Status Synchronized() override { - Latch waiter{1}; + Latch waiter; + waiter.Up(); this->backend_.Push([&] { waiter.Done(nullptr); }); waiter.Wait(); return Status::OK(); diff --git a/ucm/store/pcstore/CMakeLists.txt b/ucm/store/pcstore/CMakeLists.txt index fe1c98d86..e0a5b40b3 100644 --- a/ucm/store/pcstore/CMakeLists.txt +++ b/ucm/store/pcstore/CMakeLists.txt @@ -4,7 +4,7 @@ target_include_directories(pcstore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/cc/api ${CMAKE_CURRENT_SOURCE_DIR}/cc/domain ) -target_link_libraries(pcstore PUBLIC trans storeinfra) +target_link_libraries(pcstore PUBLIC storeintf trans infra_logger) file(GLOB_RECURSE UCMSTORE_PC_CPY_SOURCE_FILES "./cpy/*.cc") pybind11_add_module(ucmpcstore ${UCMSTORE_PC_CPY_SOURCE_FILES}) diff --git a/ucm/store/pcstore/cc/domain/file/file.cc b/ucm/store/pcstore/cc/domain/file/file.cc new file mode 100644 index 000000000..8a52074f0 --- /dev/null +++ b/ucm/store/pcstore/cc/domain/file/file.cc @@ -0,0 +1,97 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#include "file.h" +#include "logger/logger.h" +#include "posix_file.h" + +namespace UC { + +using FileImpl = PosixFile; + +std::unique_ptr File::Make(const std::string& path) +{ + try { + return std::make_unique(path); + } catch (const std::exception& e) { + UC_ERROR("Failed({}) to make file({}) pointer.", e.what(), path); + return nullptr; + } +} + +Status File::MkDir(const std::string& path) { return FileImpl{path}.MkDir(); } + +Status File::RmDir(const std::string& path) { return FileImpl{path}.RmDir(); } + +Status File::Rename(const std::string& path, const std::string& newName) +{ + return FileImpl{path}.Rename(newName); +} + +Status File::Access(const std::string& path, const int32_t mode) +{ + return FileImpl{path}.Access(mode); +} + +Status File::Stat(const std::string& path, IFile::FileStat& st) +{ + FileImpl file{path}; + auto status = file.Open(IFile::OpenFlag::READ_ONLY); + if (status.Failure()) { return status; } + status = file.Stat(st); + file.Close(); + return status; +} + +Status File::Read(const std::string& path, const size_t offset, const size_t length, + uintptr_t address, const bool directIo) +{ + FileImpl file{path}; + Status status = Status::OK(); + auto flags = directIo ? IFile::OpenFlag::READ_ONLY | IFile::OpenFlag::DIRECT + : IFile::OpenFlag::READ_ONLY; + if ((status = file.Open(flags)).Failure()) { return status; } + if ((status = file.Read((void*)address, length, offset)).Failure()) { return status; } + return status; +} + +Status File::Write(const std::string& path, const size_t offset, const size_t length, + const uintptr_t address, const bool directIo, const bool create) +{ + FileImpl file{path}; + Status status = Status::OK(); + auto flags = IFile::OpenFlag::WRITE_ONLY; + if (directIo) { flags |= IFile::OpenFlag::DIRECT; } + if (create) { flags |= IFile::OpenFlag::CREATE; } + if ((status = file.Open(flags)).Failure()) { return status; } + if ((status = file.Write((const void*)address, length, offset)).Failure()) { return status; } + return status; +} + +void File::MUnmap(void* addr, size_t size) { FileImpl{{}}.MUnmap(addr, size); } + +void File::ShmUnlink(const std::string& path) { FileImpl{path}.ShmUnlink(); } + +void File::Remove(const std::string& path) { FileImpl{path}.Remove(); } + +} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_hash.h b/ucm/store/pcstore/cc/domain/file/file.h similarity index 54% rename from ucm/store/localstore/cc/domain/cache/cache_hash.h rename to ucm/store/pcstore/cc/domain/file/file.h index e37ef79e1..086518e3a 100644 --- a/ucm/store/localstore/cc/domain/cache/cache_hash.h +++ b/ucm/store/pcstore/cc/domain/file/file.h @@ -21,32 +21,30 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ -#ifndef UNIFIEDCACHE_CACHE_HASH_H -#define UNIFIEDCACHE_CACHE_HASH_H +#ifndef UNIFIEDCACHE_FILE_H +#define UNIFIEDCACHE_FILE_H -#include -#include -#include -#include +#include +#include "ifile.h" namespace UC { -class CacheHash { - uint32_t capacity_; - void* addr_; - +class File { public: - static constexpr uint32_t npos = std::numeric_limits::max(); - CacheHash() : capacity_{0}, addr_{nullptr} {} - void Setup(const uint32_t capacity) noexcept { this->capacity_ = capacity; } - size_t MemorySize() const noexcept; - void Setup(void* addr) noexcept; - void Insert(const std::string& id, const size_t offset, const uint32_t index) noexcept; - uint32_t Find(const std::string& id, const size_t offset) noexcept; - void PutRef(const uint32_t index) noexcept; - void PutRef(const std::string& id, const size_t offset) noexcept; - void Remove(const std::string& id, const size_t offset) noexcept; - uint32_t Evict() noexcept; + static std::unique_ptr Make(const std::string& path); + static Status MkDir(const std::string& path); + static Status RmDir(const std::string& path); + static Status Rename(const std::string& path, const std::string& newName); + static Status Access(const std::string& path, const int32_t mode); + static Status Stat(const std::string& path, IFile::FileStat& st); + static Status Read(const std::string& path, const size_t offset, const size_t length, + uintptr_t address, const bool directIo = false); + static Status Write(const std::string& path, const size_t offset, const size_t length, + const uintptr_t address, const bool directIo = false, + const bool create = false); + static void MUnmap(void* addr, size_t size); + static void ShmUnlink(const std::string& path); + static void Remove(const std::string& path); }; } // namespace UC diff --git a/ucm/store/pcstore/cc/domain/file/ifile.h b/ucm/store/pcstore/cc/domain/file/ifile.h new file mode 100644 index 000000000..74b77cba6 --- /dev/null +++ b/ucm/store/pcstore/cc/domain/file/ifile.h @@ -0,0 +1,81 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#ifndef UNIFIEDCACHE_IFILE_H +#define UNIFIEDCACHE_IFILE_H + +#include +#include +#include "status/status.h" + +namespace UC { + +class IFile { +public: + class AccessMode { + public: + static constexpr int32_t READ = R_OK; + static constexpr int32_t WRITE = W_OK; + static constexpr int32_t EXIST = F_OK; + static constexpr int32_t EXECUTE = X_OK; + }; + class OpenFlag { + public: + static constexpr uint32_t READ_ONLY = O_RDONLY; + static constexpr uint32_t WRITE_ONLY = O_WRONLY; + static constexpr uint32_t READ_WRITE = O_RDWR; + static constexpr uint32_t CREATE = O_CREAT; + static constexpr uint32_t DIRECT = O_DIRECT; + static constexpr uint32_t APPEND = O_APPEND; + static constexpr uint32_t EXCL = O_EXCL; + }; + using FileStat = struct stat64; + +public: + IFile(const std::string& path) : path_{path} {} + virtual ~IFile() = default; + const std::string& Path() const { return this->path_; } + virtual Status MkDir() = 0; + virtual Status RmDir() = 0; + virtual Status Rename(const std::string& newName) = 0; + virtual Status Access(const int32_t mode) = 0; + virtual Status Open(const uint32_t flags) = 0; + virtual void Close() = 0; + virtual void Remove() = 0; + virtual Status Read(void* buffer, size_t size, off64_t offset = -1) = 0; + virtual Status Write(const void* buffer, size_t size, off64_t offset = -1) = 0; + virtual Status Truncate(size_t length) = 0; + virtual Status Stat(FileStat& st) = 0; + virtual Status ShmOpen(const uint32_t flags) = 0; + virtual Status MMap(void*& addr, size_t size, bool write, bool read, bool shared) = 0; + virtual void MUnmap(void* addr, size_t size) = 0; + virtual void ShmUnlink() = 0; + virtual Status UpdateTime() = 0; + +private: + std::string path_; +}; + +} // namespace UC + +#endif diff --git a/ucm/store/pcstore/cc/domain/file/posix_file.cc b/ucm/store/pcstore/cc/domain/file/posix_file.cc new file mode 100644 index 000000000..bc697f392 --- /dev/null +++ b/ucm/store/pcstore/cc/domain/file/posix_file.cc @@ -0,0 +1,245 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#include "posix_file.h" +#include +#include +#include +#include +#include +#include "logger/logger.h" + +namespace UC { + +static constexpr auto NewFilePerm = (S_IREAD | S_IWRITE | S_IRGRP | S_IROTH); + +PosixFile::~PosixFile() { this->Close(); } + +Status PosixFile::MkDir() +{ + constexpr auto permission = (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH); + auto ret = mkdir(this->Path().c_str(), permission); + auto eno = errno; + if (ret != 0) { + if (eno == EEXIST) { + return Status::DuplicateKey(); + } else { + UC_ERROR("Failed to create directory, path: {}, errcode: {}, errno: {}.", this->Path(), + ret, eno); + return Status::OsApiError(); + } + } + return Status::OK(); +} + +Status PosixFile::RmDir() +{ + auto ret = rmdir(this->Path().c_str()); + auto eno = errno; + if (ret != 0) { + if (eno != ENOTEMPTY) { UC_WARN("Failed to remove directory, path: {}.", this->Path()); } + return Status::OsApiError(); + } + return Status::OK(); +} + +Status PosixFile::Rename(const std::string& newName) +{ + auto ret = rename(this->Path().c_str(), newName.c_str()); + auto eno = errno; + if (ret != 0) { + if (eno == ENOENT) { + return Status::NotFound(); + } else { + UC_ERROR("Failed to rename file, old path: {}, new path: {}, errno: {}.", this->Path(), + newName, eno); + return Status::OsApiError(); + } + } + return Status::OK(); +} + +Status PosixFile::Access(const int32_t mode) +{ + auto ret = access(this->Path().c_str(), mode); + auto eno = errno; + if (ret != 0) { + if (eno == ENOENT) { + return Status::NotFound(); + } else { + UC_ERROR("Failed to access file, path: {}, mode: {}, errcode: {}, errno: {}.", + this->Path(), mode, ret, eno); + return Status::OsApiError(); + } + } + return Status::OK(); +} + +Status PosixFile::Open(const uint32_t flags) +{ + this->handle_ = open(this->Path().c_str(), flags, NewFilePerm); + auto eno = errno; + auto status = this->handle_ >= 0 ? Status::OK() : Status::OsApiError(); + if (status.Failure()) { + if (eno == EEXIST) { + status = Status::DuplicateKey(); + } else { + UC_ERROR("Failed({},{}) to open file({}) with flags({}).", eno, status, this->Path(), + flags); + } + } + return status; +} + +void PosixFile::Close() +{ + if (this->handle_ != -1) { close(this->handle_); } + this->handle_ = -1; +} + +void PosixFile::Remove() +{ + auto ret = remove(this->Path().c_str()); + auto eno = errno; + if (ret != 0) { + if (eno != ENOENT) { UC_WARN("Failed({},{}) to remove file({}).", ret, eno, this->Path()); } + } +} + +Status PosixFile::Read(void* buffer, size_t size, off64_t offset) +{ + ssize_t nBytes = -1; + if (offset != -1) { + nBytes = pread(this->handle_, buffer, size, offset); + } else { + nBytes = read(this->handle_, buffer, size); + } + auto eno = errno; + if (nBytes != static_cast(size)) { + UC_ERROR("Failed to read file, path: {}, size: {}, offset: {}, errno: {}.", this->Path(), + size, offset, eno); + return Status::OsApiError(); + } + return Status::OK(); +} + +Status PosixFile::Write(const void* buffer, size_t size, off64_t offset) +{ + ssize_t nBytes = -1; + if (offset != -1) { + nBytes = pwrite(this->handle_, buffer, size, offset); + } else { + nBytes = write(this->handle_, buffer, size); + } + auto eno = errno; + if (nBytes != static_cast(size)) { + UC_ERROR("Failed to write file, path: {}, size: {}, offset: {}, errno: {}.", this->Path(), + size, offset, eno); + return Status::OsApiError(); + } + return Status::OK(); +} + +Status PosixFile::Truncate(size_t length) +{ + auto ret = ftruncate(this->handle_, length); + auto eno = errno; + if (ret != 0) { + UC_ERROR("Failed to truncate file, path: {}, length: {}, errno: {}.", this->Path(), length, + eno); + return Status::OsApiError(); + } + return Status::OK(); +} + +Status PosixFile::Stat(FileStat& st) +{ + auto ret = fstat64(this->handle_, &st); + auto eno = errno; + if (ret != 0) { + UC_ERROR("Failed({},{}) to stat file({}).", ret, eno, this->Path()); + return Status::OsApiError(); + } + return Status::OK(); +} + +Status PosixFile::ShmOpen(const uint32_t flags) +{ + this->handle_ = shm_open(this->Path().c_str(), flags, NewFilePerm); + auto eno = errno; + auto status = this->handle_ >= 0 ? Status::OK() : Status::OsApiError(); + if (status.Failure()) { + if (eno == EEXIST) { + status = Status::DuplicateKey(); + } else { + UC_ERROR("Failed({},{}) to shm_open file({}) with flags({}).", eno, status, + this->Path(), flags); + } + } + return status; +} + +Status PosixFile::MMap(void*& addr, size_t size, bool write, bool read, bool shared) +{ + auto prot = PROT_NONE; + if (write) { prot |= PROT_WRITE; } + if (read) { prot |= PROT_READ; } + auto flags = 0; + if (shared) { flags |= MAP_SHARED; } + addr = mmap(nullptr, size, prot, flags, this->handle_, 0); + auto eno = errno; + if (addr == MAP_FAILED) { + UC_ERROR("Failed({}) to mmap file({}) with flags({},{}).", eno, this->Path(), prot, flags); + return Status::OsApiError(); + } + return Status::OK(); +} + +void PosixFile::MUnmap(void* addr, size_t size) +{ + auto ret = munmap(addr, size); + auto eno = errno; + if (ret < 0) { UC_WARN("Failed({},{}) to unmap memory({}).", ret, eno, size); } +} + +void PosixFile::ShmUnlink() +{ + auto ret = shm_unlink(this->Path().c_str()); + auto eno = errno; + if (ret < 0) { + if (eno != ENOENT) { UC_WARN("Failed({},{}) to unlink file({}).", ret, eno, this->Path()); } + } +} + +Status PosixFile::UpdateTime() +{ + auto ret = utime(this->Path().c_str(), nullptr); + auto eno = errno; + if (ret != 0) { + UC_ERROR("Failed({},{}) to update time file({}).", ret, eno, this->Path()); + return Status::OsApiError(); + } + return Status::OK(); +} + +} // namespace UC diff --git a/ucm/store/localstore/cc/domain/cache/cache_meta.h b/ucm/store/pcstore/cc/domain/file/posix_file.h similarity index 54% rename from ucm/store/localstore/cc/domain/cache/cache_meta.h rename to ucm/store/pcstore/cc/domain/file/posix_file.h index a205d0c11..becbd28a3 100644 --- a/ucm/store/localstore/cc/domain/cache/cache_meta.h +++ b/ucm/store/pcstore/cc/domain/file/posix_file.h @@ -21,38 +21,38 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ -#ifndef UNIFIEDCACHE_CACHE_META_H -#define UNIFIEDCACHE_CACHE_META_H +#ifndef UNIFIEDCACHE_POSIX_FILE_H +#define UNIFIEDCACHE_POSIX_FILE_H -#include "cache_hash.h" -#include "cache_index.h" -#include "file/ifile.h" -#include "status/status.h" +#include "ifile.h" namespace UC { -class CacheMeta { - using Index = uint32_t; - void* addr_; - size_t size_; - CacheIndex index_; - CacheHash hash_; - +class PosixFile : public IFile { public: - static constexpr Index npos = std::numeric_limits::max(); - CacheMeta() : addr_{nullptr}, size_{0} {} - ~CacheMeta(); - Status Setup(const Index capacity) noexcept; - Index Alloc(const std::string& id, const size_t offset) noexcept; - Index Find(const std::string& id, const size_t offset) noexcept; - void PutRef(const uint32_t index) noexcept; - void PutRef(const std::string& id, const size_t offset) noexcept; + PosixFile(const std::string& path) : IFile{path}, handle_{-1} {} + ~PosixFile() override; + Status MkDir() override; + Status RmDir() override; + Status Rename(const std::string& newName) override; + Status Access(const int32_t mode) override; + Status Open(const uint32_t flags) override; + void Close() override; + void Remove() override; + Status Read(void* buffer, size_t size, off64_t offset = -1) override; + Status Write(const void* buffer, size_t size, off64_t offset = -1) override; + Status Truncate(size_t length) override; + Status Stat(FileStat& st) override; + Status ShmOpen(const uint32_t flags) override; + Status MMap(void*& addr, size_t size, bool write, bool read, bool shared) override; + void MUnmap(void* addr, size_t size) override; + void ShmUnlink() override; + Status UpdateTime() override; private: - Status InitShmMeta(IFile* shmMetaFile); - Status LoadShmMeta(IFile* shmMetaFile); + int32_t handle_; }; } // namespace UC -#endif +#endif // UNIFIEDCACHE_POSIX_FILE_H diff --git a/ucm/store/pcstore/pcstore_connector_v1.py b/ucm/store/pcstore/pcstore_connector_v1.py new file mode 100644 index 000000000..48acb5100 --- /dev/null +++ b/ucm/store/pcstore/pcstore_connector_v1.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# +# MIT License +# +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +from dataclasses import dataclass +from typing import Dict, List, Tuple + +import numpy as np +import torch + +from ucm.store.pcstore import ucmpcstore +from ucm.store.ucmstore_v1 import Task, UcmKVStoreBaseV1 + + +@dataclass +class UcmPcTask(Task): + task_id: int + + +class UcmPcStoreV1(UcmKVStoreBaseV1): + def __init__(self, config: Dict): + super().__init__(config) + self.store = ucmpcstore.PcStore() + storage_backends = [ + path for path in config["storage_backends"].split(":") if path + ] + block_size = int(config["kv_block_size"]) + transfer_enable = True if config["role"] == "worker" else False + param = ucmpcstore.PcStore.Config(storage_backends, block_size, transfer_enable) + if transfer_enable: + param.transferDeviceId = config["device"] + param.transferIoSize = config["io_size"] + param.transferIoDirect = config.get("use_direct", False) + param.transferStreamNumber = config.get("stream_number", 8) + param.transferBufferNumber = config.get("buffer_number", 4096) + param.transferLocalRankSize = config.get("local_rank_size", 8) + param.transferScatterGatherEnable = config.get("use_scatter_gatter", False) + ret = self.store.Setup(param) + if ret != 0: + msg = f"Failed to initialize ucmpcstore, errcode: {ret}." + raise RuntimeError(msg) + + def cc_store(self) -> int: + """Return a low-level C/C++ pointer to the underlying store. + + Returns: + An opaque ``int`` representing the ``Store*`` instance that can + be passed to native code. + """ + return self.store.CCStoreImpl() + + def lookup(self, block_ids: List[bytes]) -> List[bool]: + """Check presence of blocks in external storage. + + Args: + block_ids: List of vLLM block hashes (raw bytes). + + Returns: + A list of booleans, ``True`` if the corresponding block exists in + storage, ``False`` otherwise. The order matches ``block_ids``. + """ + return self.store.LookupBatch(block_ids) + + def prefetch(self, block_ids: List[bytes]) -> None: + """Asynchronously prefetch blocks into high-speed cache. + + Args: + block_ids: List of vLLM block hashes to prefetch. + """ + pass + + def _flatten_tensor_ptrs( + self, block_ids: List[bytes], tensors: List[List[torch.Tensor]] + ) -> Tuple[List[bytes], List[int]]: + n_blocks = len(block_ids) + m_addrs = len(tensors[0]) + total_len = n_blocks * m_addrs + flat_ids = [None] * total_len + flat_ptrs = [None] * total_len + ids_arr = flat_ids + ptrs_arr = flat_ptrs + data_ptr_method = torch.Tensor.data_ptr + idx = 0 + for bid, tensor_list in zip(block_ids, tensors): + for t in tensor_list: + ids_arr[idx] = bid + ptrs_arr[idx] = data_ptr_method(t) + idx += 1 + return flat_ids, flat_ptrs + + def load( + self, + block_ids: List[bytes], + shard_index: List[int], + dst_tensor: List[List[torch.Tensor]], + ) -> Task: + """Initiate transfer of KV cache from storage to device. + + Args: + block_ids: Hashes of the blocks to load. + shard_index: Shard index for each block. + dst_tensor: Double-list structure where ``dst_tensor[i][j]`` is the + destination PyTorch tensor on device for block ``i``, tensor ``j``. + + Returns: + A ``Task`` handle that can be used to check or wait for completion. + """ + ids, addrs = self._flatten_tensor_ptrs(block_ids, dst_tensor) + task_id = self.store.LoadToDevice(ids, addrs) + return UcmPcTask(task_id=task_id) + + def dump( + self, + block_ids: List[bytes], + shard_index: List[int], + src_tensor: List[List[torch.Tensor]], + ) -> Task: + """Initiate transfer of KV cache from device to storage. + + Args: + block_ids: Hashes of the blocks to write. + shard_index: Shard index for each block. + src_tensor: Double-list structure where ``src_tensor[i][j]`` is the + source PyTorch tensor on device for block ``i``, tensor ``j``. + + Returns: + A ``Task`` handle that can be used to check or wait for completion. + """ + ids, addrs = self._flatten_tensor_ptrs(block_ids, src_tensor) + task_id = self.store.DumpFromDevice(ids, addrs) + return UcmPcTask(task_id=task_id) + + def load_data( + self, + block_ids: List[bytes], + shard_index: List[int], + dst_addr: List[List[int]], + ) -> Task: + """Low-level fetch: copy KV data to device pointers. + + Args: + block_ids: Block hashes to load. + shard_index: Shard index for each block. + dst_addr: Double-list of ``int`` pointers (as Python ``int``) to + pre-allocated device buffers. + + Returns: + A ``Task`` handle for the asynchronous copy. + """ + block_ids_np = np.array(block_ids, dtype=object) + dst_addr_np = np.array(dst_addr, dtype=int) + ids = np.repeat(block_ids_np, dst_addr_np.shape[1]) + addrs = dst_addr_np.ravel() + task_id = self.store.LoadToDevice(ids, addrs) + return UcmPcTask(task_id=task_id) + + def dump_data( + self, + block_ids: List[bytes], + shard_index: List[int], + src_addr: List[List[int]], + ) -> Task: + """Low-level dump: copy KV data from device pointers. + + Args: + block_ids: Block hashes to store. + shard_index: Shard index for each block. + src_addr: Double-list of ``int`` pointers to device buffers. + + Returns: + A ``Task`` handle for the asynchronous copy. + """ + block_ids_np = np.array(block_ids, dtype=object) + src_addr_np = np.array(src_addr, dtype=int) + ids = np.repeat(block_ids_np, src_addr_np.shape[1]) + addrs = src_addr_np.ravel() + task_id = self.store.DumpFromDevice(ids, addrs) + return UcmPcTask(task_id=task_id) + + def wait(self, task: Task) -> None: + """Block until the given transfer task completes. + + Args: + task: Task handle returned by ``load``, ``dump``, ``load_data``, + or ``dump_data``. + """ + ret = self.store.Wait(task.task_id) + if ret != 0: + msg = f"Failed to wait task({task.task_id}), errcode: {ret}." + raise RuntimeError(msg) + + def check(self, task: Task) -> bool: + """Non-blocking poll for task completion. + + Args: + task: Task handle returned by any transfer method. + + Returns: + ``True`` if the task has finished, ``False`` if still in-flight. + """ + return self.store.Check(task.task_id) diff --git a/ucm/store/task/CMakeLists.txt b/ucm/store/task/CMakeLists.txt deleted file mode 100644 index 543e78bc7..000000000 --- a/ucm/store/task/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_library(storetask STATIC task_manager.cc) -target_include_directories(storetask PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(storetask PUBLIC storeinfra) diff --git a/ucm/store/task/task_manager.cc b/ucm/store/task/task_manager.cc deleted file mode 100644 index 38c780ccb..000000000 --- a/ucm/store/task/task_manager.cc +++ /dev/null @@ -1,98 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "task_manager.h" - -namespace UC { - -Status TaskManager::Submit(Task&& task, size_t& taskId) noexcept -{ - taskId = task.Id(); - const auto taskStr = task.Str(); - TaskPtr taskPtr = nullptr; - WaiterPtr waiterPtr = nullptr; - try { - taskPtr = std::make_shared(std::move(task)); - waiterPtr = std::make_shared(0, task.StartTp()); - } catch (const std::exception& e) { - UC_ERROR("Failed({}) to submit task({}).", e.what(), taskStr); - return Status::OutOfMemory(); - } - std::lock_guard lg(mutex_); - const auto& [iter, success] = - tasks_.emplace(taskId, std::make_pair(std::move(taskPtr), std::move(waiterPtr))); - if (!success) { - UC_ERROR("Failed to submit task({}).", taskStr); - return Status::OutOfMemory(); - } - auto shards = iter->second.first->Split(queues_.size(), iter->second.second); - for (auto& shard : shards) { - auto& q = queues_[qIndex_++]; - if (qIndex_ == queues_.size()) { qIndex_ = 0; } - q->Push(shard); - } - return Status::OK(); -} - -Status TaskManager::Wait(const size_t taskId) noexcept -{ - TaskPtr task = nullptr; - WaiterPtr waiter = nullptr; - { - std::lock_guard lg(mutex_); - auto iter = tasks_.find(taskId); - if (iter == tasks_.end()) { - UC_ERROR("Not found task by id({}).", taskId); - return Status::NotFound(); - } - task = iter->second.first; - waiter = iter->second.second; - tasks_.erase(iter); - } - if (!waiter->Wait(timeoutMs_)) { - UC_ERROR("Task({}) timeout({}).", task->Str(), timeoutMs_); - failureSet_.Insert(taskId); - waiter->Wait(); - } - auto failure = failureSet_.Contains(taskId); - if (failure) { - failureSet_.Remove(taskId); - UC_ERROR("Task({}) failed.", task->Str()); - return Status::Error(); - } - return Status::OK(); -} - -Status TaskManager::Check(const size_t taskId, bool& finish) noexcept -{ - std::lock_guard lg(mutex_); - auto iter = tasks_.find(taskId); - if (iter == tasks_.end()) { - UC_ERROR("Not found task by id({}).", taskId); - return Status::NotFound(); - } - finish = iter->second.second->Finish(); - return Status::OK(); -} - -} // namespace UC diff --git a/ucm/store/task/task_manager.h b/ucm/store/task/task_manager.h deleted file mode 100644 index 513e3d65c..000000000 --- a/ucm/store/task/task_manager.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_TASK_MANAGER_H -#define UNIFIEDCACHE_TASK_MANAGER_H - -#include -#include "status/status.h" -#include "task_queue.h" -#include "task_set.h" - -namespace UC { - -class TaskManager { - using TaskPtr = std::shared_ptr; - using WaiterPtr = std::shared_ptr; - using TaskPair = std::pair; - using QueuePtr = std::shared_ptr; - -public: - virtual ~TaskManager() = default; - virtual Status Submit(Task&& task, size_t& taskId) noexcept; - virtual Status Wait(const size_t taskId) noexcept; - virtual Status Check(const size_t taskId, bool& finish) noexcept; - -protected: - std::mutex mutex_; - std::unordered_map tasks_; - size_t qIndex_{0}; - std::vector queues_; - size_t timeoutMs_{0}; - TaskSet failureSet_; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/task/task_waiter.h b/ucm/store/task/task_waiter.h deleted file mode 100644 index 96358b8db..000000000 --- a/ucm/store/task/task_waiter.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_ITASK_WAITER_H -#define UNIFIEDCACHE_ITASK_WAITER_H - -#include -#include "thread/latch.h" - -namespace UC { - -class TaskWaiter : public Latch { -protected: - double startTp_; - -public: - TaskWaiter(const size_t expected, const double startTp) : Latch{expected}, startTp_{startTp} {} - virtual ~TaskWaiter() = default; - virtual void Set(const size_t expected) noexcept { this->counter_.store(expected); } - using Latch::Wait; - virtual bool Wait(const size_t timeoutMs) noexcept - { - if (timeoutMs == 0) { - this->Wait(); - return true; - } - std::unique_lock ul(this->mutex_); - if (this->counter_ == 0) { return true; } - auto elapsed = std::chrono::duration(NowTp() - startTp_); - auto elapsedMs = std::chrono::duration_cast(elapsed); - auto timeMs = std::chrono::milliseconds(timeoutMs); - if (timeMs <= elapsedMs) { return false; } - auto remainMs = timeMs - elapsedMs; - return this->cv_.wait_for(ul, remainMs, [this] { return this->counter_ == 0; }); - } - virtual bool Finish() noexcept { return this->counter_ == 0; } - -private: - static double NowTp() noexcept - { - auto now = std::chrono::steady_clock::now().time_since_epoch(); - return std::chrono::duration(now).count(); - } -}; - -} // namespace UC - -#endif diff --git a/ucm/store/test/CMakeLists.txt b/ucm/store/test/CMakeLists.txt index 0c4974efd..0a93239ec 100644 --- a/ucm/store/test/CMakeLists.txt +++ b/ucm/store/test/CMakeLists.txt @@ -1,11 +1,3 @@ if(BUILD_UNIT_TESTS) include(GoogleTest) - file(GLOB_RECURSE UCMSTORE_TEST_SOURCE_FILES "./case/*.cc") - add_executable(ucmstore.test ${UCMSTORE_TEST_SOURCE_FILES}) - target_include_directories(ucmstore.test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/case) - target_link_libraries(ucmstore.test PRIVATE - nfsstore localstore storeinfra storedevice - gtest_main gtest mockcpp - ) - gtest_discover_tests(ucmstore.test) endif() diff --git a/ucm/store/test/case/cmn/path_base.h b/ucm/store/test/case/cmn/path_base.h deleted file mode 100644 index ec709e577..000000000 --- a/ucm/store/test/case/cmn/path_base.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_TEST_PATH_BASE_H -#define UNIFIEDCACHE_TEST_PATH_BASE_H - -#include -#include "random.h" - -namespace UC { - -class PathBase : public ::testing::Test { -public: - void SetUp() override - { - testing::Test::SetUp(); - const auto info = testing::UnitTest::GetInstance()->current_test_info(); - std::string testCaceName = info->test_case_name(); - std::string testName = info->name(); - this->path_ = "./" + testCaceName + "_" + testName + "_" + this->rd_.RandomString(20) + "/"; - system((std::string("rm -rf ") + this->path_).c_str()); - system((std::string("mkdir -p ") + this->path_).c_str()); - } - void TearDown() override - { - system((std::string("rm -rf ") + this->path_).c_str()); - testing::Test::TearDown(); - } - std::string Path() const { return this->path_; } - -private: - Random rd_; - std::string path_; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/test/case/cmn/random.h b/ucm/store/test/case/cmn/random.h deleted file mode 100644 index e0fd69a74..000000000 --- a/ucm/store/test/case/cmn/random.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#ifndef UNIFIEDCACHE_TEST_RANDOM_H -#define UNIFIEDCACHE_TEST_RANDOM_H - -#include - -namespace UC { - -class Random { -public: - std::string RandomString(const size_t length) - { - const std::string allowedChars = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - std::mt19937 gen(this->rd_()); - std::uniform_int_distribution<> dis(0, allowedChars.length() - 1); - std::string randomString(length, 0); - for (size_t i = 0; i < length; i++) { randomString[i] = allowedChars[dis(gen)]; } - return randomString; - } - -private: - std::random_device rd_; -}; - -} // namespace UC - -#endif diff --git a/ucm/store/test/case/infra/file_test.cc b/ucm/store/test/case/infra/file_test.cc deleted file mode 100644 index 36f4b086a..000000000 --- a/ucm/store/test/case/infra/file_test.cc +++ /dev/null @@ -1,42 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cmn/path_base.h" -#include "file/file.h" - -class UCFileTest : public UC::PathBase {}; - -TEST_F(UCFileTest, DirCreateAndRemove) -{ - auto path1 = this->Path() + "dir1"; - ASSERT_EQ(UC::File::Access(path1, UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - ASSERT_EQ(UC::File::MkDir(path1), UC::Status::OK()); - ASSERT_EQ(UC::File::Access(path1, UC::IFile::AccessMode::EXIST), UC::Status::OK()); - auto path2 = this->Path() + "dir2"; - ASSERT_EQ(UC::File::Access(path2, UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - ASSERT_EQ(UC::File::Rename(path1, path2), UC::Status::OK()); - ASSERT_EQ(UC::File::Access(path1, UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - ASSERT_EQ(UC::File::Access(path2, UC::IFile::AccessMode::EXIST), UC::Status::OK()); - ASSERT_EQ(UC::File::RmDir(path2), UC::Status::OK()); - ASSERT_EQ(UC::File::Access(path2, UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); -} diff --git a/ucm/store/test/case/infra/posix_file_test.cc b/ucm/store/test/case/infra/posix_file_test.cc deleted file mode 100644 index bb92515d9..000000000 --- a/ucm/store/test/case/infra/posix_file_test.cc +++ /dev/null @@ -1,149 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cmn/path_base.h" -#include "file/posix_file.h" - -class UCPosixFileTest : public UC::PathBase { -public: - class Data { - public: - explicit Data(const size_t nPage, const size_t pageSize = 4096) - : nPage_{nPage}, pageSize_{pageSize}, data_{nullptr} - { - } - ~Data() - { - if (this->data_) { - free(this->data_); - this->data_ = nullptr; - } - } - void Generate() - { - this->data_ = malloc(this->Size()); - assert(this->data_ != nullptr); - } - void GenerateRandom() - { - this->Generate(); - for (size_t i = 0; i < this->nPage_; i++) { - *(size_t*)((char*)this->data_ + this->pageSize_ * i) = i; - } - } - int32_t Compare(const Data& other) - { - if (this->nPage_ < other.nPage_) { return -1; } - if (this->nPage_ < other.nPage_) { return 1; } - for (size_t i = 0; i < this->nPage_; i++) { - auto ret = memcmp((char*)this->data_ + this->pageSize_ * i, - (char*)other.data_ + this->pageSize_ * i, this->pageSize_); - if (ret != 0) { return ret; } - } - return 0; - } - size_t Size() const { return this->pageSize_ * this->nPage_; } - void* Buffer() const { return this->data_; } - - private: - size_t nPage_; - size_t pageSize_; - void* data_; - }; -}; - -TEST_F(UCPosixFileTest, DirCreateAndRemove) -{ - system((std::string("rm -rf ") + this->Path()).c_str()); - UC::PosixFile dir(this->Path()); - ASSERT_EQ(dir.Access(UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - ASSERT_EQ(dir.MkDir(), UC::Status::OK()); - ASSERT_EQ(dir.Access(UC::IFile::AccessMode::EXIST), UC::Status::OK()); - ASSERT_EQ(dir.Access(UC::IFile::AccessMode::READ), UC::Status::OK()); - ASSERT_EQ(dir.Access(UC::IFile::AccessMode::WRITE), UC::Status::OK()); - ASSERT_EQ(dir.MkDir(), UC::Status::DuplicateKey()); - ASSERT_EQ(dir.RmDir(), UC::Status::OK()); - ASSERT_EQ(dir.Access(UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); -} - -TEST_F(UCPosixFileTest, FileCreateAndRemove) -{ - UC::PosixFile file(this->Path() + "file"); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - ASSERT_EQ(file.Open(UC::IFile::OpenFlag::WRITE_ONLY), UC::Status::OsApiError()); - ASSERT_EQ(file.Open(UC::IFile::OpenFlag::WRITE_ONLY | UC::IFile::OpenFlag::CREATE), - UC::Status::OK()); - ASSERT_EQ(file.Open(UC::IFile::OpenFlag::WRITE_ONLY | UC::IFile::OpenFlag::CREATE | - UC::IFile::OpenFlag::EXCL), - UC::Status::DuplicateKey()); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::EXIST), UC::Status::OK()); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::READ), UC::Status::OK()); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::WRITE), UC::Status::OK()); - file.Remove(); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); -} - -TEST_F(UCPosixFileTest, FileWriteAndRead) -{ - UC::PosixFile file(this->Path() + "file"); - size_t nPage = 4; - UCPosixFileTest::Data data0{nPage}; - UCPosixFileTest::Data data1{nPage}; - data0.GenerateRandom(); - data1.Generate(); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - ASSERT_EQ(file.Open(UC::IFile::OpenFlag::WRITE_ONLY | UC::IFile::OpenFlag::CREATE), - UC::Status::OK()); - ASSERT_EQ(file.Write(data0.Buffer(), data0.Size(), 0), UC::Status::OK()); - file.Close(); - ASSERT_EQ(file.Open(UC::IFile::OpenFlag::READ_ONLY), UC::Status::OK()); - ASSERT_EQ(file.Read(data1.Buffer(), data1.Size(), 0), UC::Status::OK()); - file.Close(); - file.Remove(); - ASSERT_EQ(file.Access(UC::IFile::AccessMode::EXIST), UC::Status::NotFound()); - EXPECT_EQ(data0.Compare(data1), 0); -} - -TEST_F(UCPosixFileTest, FileMMapAndMUnmap) -{ - UC::Random rd; - auto fileName = rd.RandomString(20) + ".file"; - UC::PosixFile file1{fileName}; - UC::PosixFile file2{fileName}; - const size_t data = 0xfffffffe; - const auto openFlags = UC::IFile::OpenFlag::READ_WRITE | UC::IFile::OpenFlag::CREATE; - void* addr1 = nullptr; - void* addr2 = nullptr; - ASSERT_EQ(file1.Path(), file2.Path()); - ASSERT_TRUE(file1.ShmOpen(openFlags).Success()); - ASSERT_TRUE(file1.Truncate(sizeof(data)).Success()); - ASSERT_TRUE(file1.MMap(addr1, sizeof(data), true, true, true).Success()); - file1.Close(); - ASSERT_TRUE(file2.ShmOpen(openFlags).Success()); - ASSERT_TRUE(file2.MMap(addr2, sizeof(data), false, true, true).Success()); - file2.Close(); - *((size_t*)addr1) = data; - ASSERT_EQ(*(size_t*)addr2, data); - file1.ShmUnlink(); - file2.ShmUnlink(); -} diff --git a/ucm/store/test/case/localstore/cache_hash_test.cc b/ucm/store/test/case/localstore/cache_hash_test.cc deleted file mode 100644 index 2dedd28d0..000000000 --- a/ucm/store/test/case/localstore/cache_hash_test.cc +++ /dev/null @@ -1,88 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include -#include "cache/cache_hash.h" - -class UCCacheHashTest : public testing::Test {}; - -TEST_F(UCCacheHashTest, InsertAndFind) -{ - constexpr uint32_t capacity = 10; - const std::string id = "1234567890123456"; - UC::CacheHash hash; - hash.Setup(capacity); - auto buffer = new (std::nothrow) uint8_t[hash.MemorySize()]; - ASSERT_NE(buffer, nullptr); - hash.Setup(buffer); - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(hash.Find(id, i), UC::CacheHash::npos); } - for (uint32_t i = 0; i < capacity; i++) { hash.Insert(id, i, i); } - for (uint32_t i = 0; i < capacity; i++) { ASSERT_NE(hash.Find(id, i), UC::CacheHash::npos); } - for (uint32_t i = 0; i < capacity; i++) { hash.Remove(id, i); } - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(hash.Find(id, i), UC::CacheHash::npos); } - delete[] buffer; -} - -TEST_F(UCCacheHashTest, SetupAtSharedMemory) -{ - constexpr uint32_t capacity = 10; - const std::string id = "1234567890123456"; - UC::CacheHash hash1; - hash1.Setup(capacity); - UC::CacheHash hash2; - hash2.Setup(capacity); - auto buffer = new (std::nothrow) uint8_t[hash1.MemorySize()]; - ASSERT_NE(buffer, nullptr); - hash1.Setup(buffer); - hash2.Setup(buffer); - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(hash2.Find(id, i), UC::CacheHash::npos); } - for (uint32_t i = 0; i < capacity; i++) { hash1.Insert(id, i, i); } - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(hash2.Find(id, i), i); } - delete[] buffer; -} - -TEST_F(UCCacheHashTest, Evict) -{ - constexpr uint32_t capacity = 10; - const std::string id = "1234567890123456"; - UC::CacheHash hash; - hash.Setup(capacity); - auto buffer = new (std::nothrow) uint8_t[hash.MemorySize()]; - ASSERT_NE(buffer, nullptr); - hash.Setup(buffer); - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(hash.Find(id, i), UC::CacheHash::npos); } - for (uint32_t i = 0; i < capacity; i++) { - hash.Insert(id, i, i); - hash.PutRef(i); - } - for (uint32_t i = 0; i < capacity; i++) { ASSERT_NE(hash.Find(id, i), UC::CacheHash::npos); } - ASSERT_EQ(hash.Evict(), UC::CacheHash::npos); - hash.PutRef(0); - ASSERT_EQ(hash.Evict(), 0); - hash.PutRef(id, 2); - ASSERT_EQ(hash.Evict(), 2); - ASSERT_EQ(hash.Evict(), UC::CacheHash::npos); - for (uint32_t i = 0; i < capacity; i++) { hash.PutRef(i); } - ASSERT_EQ(hash.Evict(), 1); - delete[] buffer; -} diff --git a/ucm/store/test/case/localstore/cache_index_test.cc b/ucm/store/test/case/localstore/cache_index_test.cc deleted file mode 100644 index 669b53915..000000000 --- a/ucm/store/test/case/localstore/cache_index_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include -#include "cache/cache_index.h" - -class UCCacheIndexTest : public testing::Test {}; - -TEST_F(UCCacheIndexTest, AcquireAllIndex) -{ - constexpr uint32_t capacity = 10; - UC::CacheIndex index; - index.Setup(capacity); - auto size = index.MemorySize(); - ASSERT_GT(size, 0); - auto buffer = new (std::nothrow) uint8_t[size]; - ASSERT_NE(buffer, nullptr); - index.Setup(buffer); - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(index.Acquire(), i); } - ASSERT_EQ(index.Acquire(), UC::CacheIndex::npos); - delete[] buffer; -} - -TEST_F(UCCacheIndexTest, AtSharedMemory) -{ - constexpr uint32_t capacity = 10; - UC::CacheIndex index1; - index1.Setup(capacity); - auto size = index1.MemorySize(); - ASSERT_GT(size, 0); - auto buffer = new (std::nothrow) uint8_t[size]; - ASSERT_NE(buffer, nullptr); - index1.Setup(buffer); - for (uint32_t i = 0; i < capacity; i++) { ASSERT_EQ(index1.Acquire(), i); } - ASSERT_EQ(index1.Acquire(), UC::CacheIndex::npos); - UC::CacheIndex index2; - index2.Setup(capacity); - ASSERT_EQ(index1.MemorySize(), index2.MemorySize()); - index2.Setup(buffer); - ASSERT_EQ(index2.Acquire(), UC::CacheIndex::npos); - for (uint32_t i = 0; i < capacity; i++) { index2.Release(i); } - delete[] buffer; -} diff --git a/ucm/store/test/case/localstore/cache_instance_test.cc b/ucm/store/test/case/localstore/cache_instance_test.cc deleted file mode 100644 index 27ec8f5c6..000000000 --- a/ucm/store/test/case/localstore/cache_instance_test.cc +++ /dev/null @@ -1,82 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include -#include -#include "cache/cache_instance.h" - -class UCCacheInstanceTest : public testing::Test {}; - -TEST_F(UCCacheInstanceTest, StoreAndLoad) -{ - using Data = size_t; - constexpr Data data = 2048; - constexpr auto ioSize = sizeof(Data); - constexpr uint32_t capacity = 1024; - constexpr size_t totalSize = ioSize * capacity; - MOCKER(UC::CacheSegment::TotalSize).stubs().will(returnValue(totalSize)); - { - UC::CacheInstance instance; - ASSERT_TRUE(instance.Setup(ioSize, capacity).Success()); - const std::string id = "1234567890987654"; - const size_t offset = 1024; - ASSERT_EQ(instance.Find(id, offset), nullptr); - auto addr1 = instance.Alloc(id, offset); - ASSERT_NE(addr1, nullptr); - *(Data*)addr1 = data; - instance.PutRef(id, offset); - auto addr2 = instance.Find(id, offset); - ASSERT_NE(addr2, nullptr); - ASSERT_EQ(*(Data*)addr2, data); - instance.PutRef(id, offset); - } - GlobalMockObject::verify(); -} - -TEST_F(UCCacheInstanceTest, CacheShare) -{ - using Data = size_t; - constexpr Data data = 2048; - constexpr auto ioSize = sizeof(Data); - constexpr uint32_t capacity = 1024; - constexpr size_t totalSize = ioSize * capacity; - MOCKER(UC::CacheSegment::TotalSize).stubs().will(returnValue(totalSize)); - { - UC::CacheInstance instance1; - ASSERT_TRUE(instance1.Setup(ioSize, capacity).Success()); - UC::CacheInstance instance2; - ASSERT_TRUE(instance2.Setup(ioSize, capacity).Success()); - const std::string id = "1234567890987654"; - const size_t offset = 1024; - ASSERT_EQ(instance2.Find(id, offset), nullptr); - auto addr1 = instance1.Alloc(id, offset); - ASSERT_NE(addr1, nullptr); - *(Data*)addr1 = data; - instance1.PutRef(id, offset); - auto addr2 = instance2.Find(id, offset); - ASSERT_NE(addr2, nullptr); - ASSERT_EQ(*(Data*)addr2, data); - instance2.PutRef(id, offset); - } - GlobalMockObject::verify(); -} diff --git a/ucm/store/test/case/localstore/cache_layout_test.cc b/ucm/store/test/case/localstore/cache_layout_test.cc deleted file mode 100644 index 12dd1fddd..000000000 --- a/ucm/store/test/case/localstore/cache_layout_test.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include -#include "cache/cache_layout.h" - -class UCCacheLayoutTest : public testing::Test {}; - -TEST_F(UCCacheLayoutTest, Layout) -{ - ASSERT_EQ(UC::CacheLayout::MetaShmFile(), "/ucmlocal.meta"); - ASSERT_EQ(UC::CacheLayout::DataShmFile(0), "/ucmlocal.dat000000"); - ASSERT_EQ(UC::CacheLayout::DataShmFile(11), "/ucmlocal.dat000011"); - ASSERT_EQ(UC::CacheLayout::DataShmFile(101), "/ucmlocal.dat000101"); -} diff --git a/ucm/store/test/case/localstore/cache_segment_test.cc b/ucm/store/test/case/localstore/cache_segment_test.cc deleted file mode 100644 index 80014ea7e..000000000 --- a/ucm/store/test/case/localstore/cache_segment_test.cc +++ /dev/null @@ -1,66 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include -#include -#include -#include "cache/cache_segment.h" - -class UCCacheSegmentTest : public testing::Test {}; - -TEST_F(UCCacheSegmentTest, SetupOnSharedMemory) -{ - using T = size_t; - constexpr T data = 100; - constexpr size_t ioSize = sizeof(T); - constexpr size_t index = 15; - constexpr size_t ioNumber = index + 1; - constexpr size_t totalSize = ioSize * ioNumber; - MOCKER(UC::CacheSegment::TotalSize).stubs().will(returnValue(totalSize)); - auto seg1 = new (std::nothrow) UC::CacheSegment(); - auto seg2 = new (std::nothrow) UC::CacheSegment(); - ASSERT_NE(seg1, nullptr); - ASSERT_NE(seg2, nullptr); - ASSERT_TRUE(seg1->Setup(10, ioSize).Success()); - ASSERT_TRUE(seg2->Setup(10, ioSize).Success()); - ASSERT_NE(*(T*)seg2->At(index), data); - *(T*)(seg1->At(index)) = data; - delete seg1; - ASSERT_EQ(*(T*)seg2->At(index), data); - delete seg2; - GlobalMockObject::verify(); -} - -TEST_F(UCCacheSegmentTest, SetupWhileShmOpenFailed) -{ - using T = size_t; - constexpr size_t ioSize = sizeof(T); - constexpr size_t index = 15; - constexpr size_t ioNumber = index + 1; - constexpr size_t totalSize = ioSize * ioNumber; - MOCKER(UC::CacheSegment::TotalSize).stubs().will(returnValue(totalSize)); - MOCKER(shm_open).stubs().will(returnValue(-1)); - UC::CacheSegment seg; - ASSERT_TRUE(seg.Setup(0, ioSize).Failure()); - GlobalMockObject::verify(); -} diff --git a/ucm/store/test/case/nfsstore/hotness_test.cc b/ucm/store/test/case/nfsstore/hotness_test.cc deleted file mode 100644 index 21cc83cdd..000000000 --- a/ucm/store/test/case/nfsstore/hotness_test.cc +++ /dev/null @@ -1,54 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ - -#include -#include -#include "hotness/hotness_set.h" -#include "hotness/hotness_timer.h" -#include "cmn/path_base.h" -#include "file/file.h" -#include "space/space_manager.h" - -class UCHotnessTest : public UC::PathBase {}; - -TEST_F(UCHotnessTest, UpdateHotness) -{ - UC::SpaceManager mgr; - ASSERT_EQ(mgr.Setup({this->Path()}, 1024 * 1024, false), UC::Status::OK()); - - std::string block1 = "a1b2c3d4e5f6789012345678901234ab"; - ASSERT_EQ(mgr.NewBlock(block1), UC::Status::OK()); - ASSERT_EQ(mgr.CommitBlock(block1), UC::Status::OK()); - - UC::HotnessSet hotness_set; - hotness_set.Insert(block1); - auto space_layout = mgr.GetSpaceLayout(); - auto path = space_layout->DataFilePath(block1, false); - auto currentTime = std::filesystem::last_write_time(path); - std::filesystem::last_write_time(path, currentTime - std::chrono::seconds(2)); - auto lastTime = std::filesystem::last_write_time(path); - hotness_set.UpdateHotness(space_layout); - auto newTime = std::filesystem::last_write_time(path); - ASSERT_GT(newTime, lastTime); -} \ No newline at end of file diff --git a/ucm/store/test/case/nfsstore/space_manager_test.cc b/ucm/store/test/case/nfsstore/space_manager_test.cc deleted file mode 100644 index 958f6464a..000000000 --- a/ucm/store/test/case/nfsstore/space_manager_test.cc +++ /dev/null @@ -1,125 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include -#include "cmn/path_base.h" -#include "space/space_manager.h" -#include "file/file.h" - -class UCSpaceManagerTest : public UC::PathBase {}; - -TEST_F(UCSpaceManagerTest, NewBlockTwice) -{ - UC::SpaceManager spaceMgr; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, 1024 * 1024, false), UC::Status::OK()); - const std::string block1 = "block1"; - ASSERT_FALSE(spaceMgr.LookupBlock(block1)); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::OK()); - ASSERT_FALSE(spaceMgr.LookupBlock(block1)); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::DuplicateKey()); - ASSERT_EQ(spaceMgr.CommitBlock(block1), UC::Status::OK()); - ASSERT_TRUE(spaceMgr.LookupBlock(block1)); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::DuplicateKey()); -} - -TEST_F(UCSpaceManagerTest, NewBlockTwiceWithTempDir) -{ - UC::SpaceManager spaceMgr; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, 1024 * 1024, true), UC::Status::OK()); - const std::string block1 = "block1"; - ASSERT_FALSE(spaceMgr.LookupBlock(block1)); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::OK()); - ASSERT_FALSE(spaceMgr.LookupBlock(block1)); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::DuplicateKey()); - ASSERT_EQ(spaceMgr.CommitBlock(block1), UC::Status::OK()); - ASSERT_TRUE(spaceMgr.LookupBlock(block1)); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::DuplicateKey()); -} - -TEST_F(UCSpaceManagerTest, CreateBlockWhenNoSpace) -{ - UC::SpaceManager spaceMgr; - size_t blockSize = 1024 * 1024; - size_t capacity = blockSize; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, blockSize, false, capacity), UC::Status::OK()); - ASSERT_EQ(spaceMgr.NewBlock("block3"), UC::Status::OK()); - ASSERT_EQ(spaceMgr.NewBlock("block4"), UC::Status::NoSpace()); -} - -TEST_F(UCSpaceManagerTest, IterAllBlockFile) -{ - constexpr size_t blockSize = 1024 * 1024; - constexpr size_t capacity = blockSize * 1024; - UC::SpaceManager spaceMgr; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, blockSize, false, capacity), UC::Status::OK()); - const std::string block1 = "a1b2c3d4e5f6789012345678901234ab"; - const std::string block2 = "a2b2c3d4e5f6789012345678901234ab"; - const std::string block3 = "a3b2c3d4e5f6789012345678901234ab"; - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::OK()); - ASSERT_EQ(spaceMgr.NewBlock(block2), UC::Status::OK()); - ASSERT_EQ(spaceMgr.NewBlock(block3), UC::Status::OK()); - auto layout = spaceMgr.GetSpaceLayout(); - auto iter = layout->CreateFilePathIterator(); - size_t count = 0; - while (!layout->NextDataFilePath(iter).empty()) { count++; } - ASSERT_EQ(count, 0); - ASSERT_EQ(spaceMgr.CommitBlock(block1), UC::Status::OK()); - ASSERT_EQ(spaceMgr.CommitBlock(block2), UC::Status::OK()); - ASSERT_EQ(spaceMgr.CommitBlock(block3), UC::Status::OK()); - iter = layout->CreateFilePathIterator(); - count = 0; - while (!layout->NextDataFilePath(iter).empty()) { count++; } - ASSERT_EQ(count, 3); -} - -TEST_F(UCSpaceManagerTest, NewBlockReuseIfActiveAccessedLongAgo) -{ - UC::SpaceManager spaceMgr; - constexpr size_t blockSize = 1024 * 1024; - constexpr size_t capacity = blockSize * 1024; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, blockSize, false, capacity), UC::Status::OK()); - const auto* layout = spaceMgr.GetSpaceLayout(); - ASSERT_NE(layout, nullptr); - - const std::string block1 = "a1b2c3d4e5f6789012345678901234ab"; - auto parent = UC::File::Make(layout->DataFileParent(block1, /*activated=*/true)); - ASSERT_NE(parent, nullptr); - ASSERT_EQ(parent->MkDir(), UC::Status::OK()); - - const auto activePath = layout->DataFilePath(block1, /*activated=*/true); - auto activeFile = UC::File::Make(activePath); - ASSERT_NE(activeFile, nullptr); - ASSERT_EQ(activeFile->Open(UC::IFile::OpenFlag::CREATE | UC::IFile::OpenFlag::READ_WRITE), UC::Status::OK()); - activeFile->Close(); - - // NewBlock should return DuplicateKey because the file is recent - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::DuplicateKey()); - - // Set atime to 10 minutes ago so it is not considered recent - struct utimbuf newTime; - auto tp = time(nullptr) - 600; - newTime.modtime = tp; - newTime.actime = tp; - utime(activePath.c_str(), &newTime); - ASSERT_EQ(spaceMgr.NewBlock(block1), UC::Status::OK()); -} \ No newline at end of file diff --git a/ucm/store/test/case/nfsstore/space_property_test.cc b/ucm/store/test/case/nfsstore/space_property_test.cc deleted file mode 100644 index 5b5beb619..000000000 --- a/ucm/store/test/case/nfsstore/space_property_test.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ -#include "cmn/path_base.h" -#include "space/space_property.h" -#include "space/space_layout.h" -#include "space/space_shard_layout.h" -#include "space/space_manager.h" - -class UCSpacePropertyTest : public UC::PathBase {}; - -/* -* check the persistence of property -*/ -TEST_F(UCSpacePropertyTest, CapacityPersistence) -{ - size_t blocksize = 1024 * 1024; - UC::SpaceManager spaceMgr; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, blocksize, false, blocksize * 5), UC::Status::OK()); - const UC::SpaceLayout* layout = spaceMgr.GetSpaceLayout(); - - const std::string path = layout->ClusterPropertyFilePath(); - - UC::SpaceProperty spaceProperty; - ASSERT_EQ(spaceProperty.Setup(path), UC::Status::OK()); - ASSERT_EQ(spaceProperty.GetCapacity(), 0); - - spaceProperty.IncreaseCapacity(blocksize * 2); - ASSERT_EQ(spaceProperty.GetCapacity(), blocksize*2); - - UC::SpaceProperty spaceProperty2; - ASSERT_EQ(spaceProperty2.Setup(path), UC::Status::OK()); - ASSERT_EQ(spaceProperty2.GetCapacity(), blocksize*2); - - spaceProperty2.DecreaseCapacity(blocksize); - ASSERT_EQ(spaceProperty2.GetCapacity(), blocksize); - - UC::SpaceProperty spaceProperty3; - ASSERT_EQ(spaceProperty3.Setup(path), UC::Status::OK()); - ASSERT_EQ(spaceProperty3.GetCapacity(), blocksize); - } \ No newline at end of file diff --git a/ucm/store/test/case/nfsstore/space_recycle_test.cc b/ucm/store/test/case/nfsstore/space_recycle_test.cc deleted file mode 100644 index a9fb862f7..000000000 --- a/ucm/store/test/case/nfsstore/space_recycle_test.cc +++ /dev/null @@ -1,124 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ - -#include -#include "cmn/path_base.h" -#include "file/file.h" -#include "space/space_recycle.h" -#include "space/space_manager.h" -#include "thread/latch.h" - -namespace UC { - -void DoRecycle(const SpaceLayout* layout, const uint32_t recycleNum, - SpaceRecycle::RecycleOneBlockDone done); -} - -class UCSpaceRecycleTest : public UC::PathBase { -protected: - using OpenFlag = UC::IFile::OpenFlag; - using AccessMode = UC::IFile::AccessMode; - - void NewBlock(const UC::SpaceLayout* layout, const std::string& id) - { - std::string parent = layout->DataFileParent(id, false); - UC::File::MkDir(parent); - std::string path = layout->DataFilePath(id, false); - auto f = UC::File::Make(path); - f->Open(OpenFlag::CREATE | OpenFlag::READ_WRITE); - } - - bool ExistBlock(const UC::SpaceLayout* layout, const std::string& id) - { - std::string path = layout->DataFilePath(id, false); - return UC::File::Access(path, AccessMode::EXIST).Success(); - } - - void UpdateBlock(const UC::SpaceLayout* layout, const std::string& id) - { - struct utimbuf newTime; - auto tp = time(nullptr) + 3600; - newTime.modtime = tp; - newTime.actime = tp; - std::string path = layout->DataFilePath(id, false); - utime(path.c_str(), &newTime); - } -}; - -TEST_F(UCSpaceRecycleTest, TriggerRecycle) -{ - size_t blocksize = 1024 * 1024; - UC::SpaceManager spaceMgr; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, blocksize, false, blocksize * 5), UC::Status::OK()); - const UC::SpaceLayout* layout = spaceMgr.GetSpaceLayout(); - std::string block1 = "a1b2c3d4e5f6789012345678901234ab"; - NewBlock(layout, block1); - ASSERT_TRUE(ExistBlock(layout, block1)); - - std::string block2 = "a2b2c3d4e5f6789012345678901234ab"; - NewBlock(layout, block2); - ASSERT_TRUE(ExistBlock(layout, block2)); - - UpdateBlock(layout, block1); - UC::SpaceRecycle recycle; - UC::Latch waiter{1}; - - ASSERT_TRUE(recycle.Setup(layout, 10, [&waiter] { waiter.Done([]{}); }).Success()); - recycle.Trigger(); - waiter.Wait(); - EXPECT_TRUE(ExistBlock(layout, block1)); - EXPECT_FALSE(ExistBlock(layout, block2)); -} - -TEST_F(UCSpaceRecycleTest, DoRecycle) -{ - size_t blocksize = 1024 * 1024; - UC::SpaceManager spaceMgr; - ASSERT_EQ(spaceMgr.Setup({this->Path()}, blocksize, false, blocksize * 5), UC::Status::OK()); - const UC::SpaceLayout* layout = spaceMgr.GetSpaceLayout(); - std::string recycleBlocks[] = { - "a1b2c3d4e5f6789012345678901234ab", - "a2b2c3d4e5f6789012345678901234ab", - "a3b2c3d4e5f6789012345678901234ab" - }; - std::string remainBlocks[] = { - "b1b2c3d4e5f6789012345678901234ab", - "b2b2c3d4e5f6789012345678901234ab", - "b3b2c3d4e5f6789012345678901234ab" - }; - for (auto &id: remainBlocks) - { - NewBlock(layout, id); - ASSERT_TRUE(ExistBlock(layout, id)); - } - for (auto &id: recycleBlocks) - { - NewBlock(layout, id); - ASSERT_TRUE(ExistBlock(layout, id)); - } - for (auto &id: remainBlocks) { UpdateBlock(layout, id); } - UC::DoRecycle(layout, 3, nullptr); - for (auto &id: remainBlocks) { EXPECT_TRUE(ExistBlock(layout, id)); } - for (auto &id: recycleBlocks) { EXPECT_FALSE(ExistBlock(layout, id)); } -} \ No newline at end of file diff --git a/ucm/store/test/e2e/dramstore_embed_and_fetch.py b/ucm/store/test/e2e/pcstore_embed_v1.py similarity index 54% rename from ucm/store/test/e2e/dramstore_embed_and_fetch.py rename to ucm/store/test/e2e/pcstore_embed_v1.py index 4f9acda19..b8533d202 100644 --- a/ucm/store/test/e2e/dramstore_embed_and_fetch.py +++ b/ucm/store/test/e2e/pcstore_embed_v1.py @@ -24,30 +24,29 @@ # import os import secrets +import time from typing import List import torch -from ucm.store.dramstore.dramstore_connector import UcmDramStore -from ucm.store.ucmstore import UcmKVStoreBase +from ucm.store.pcstore.pcstore_connector_v1 import UcmPcStoreV1 +from ucm.store.ucmstore_v1 import UcmKVStoreBaseV1 -def setup_store( - capacity, block_size, stream_number, device_id, timeout_ms -) -> UcmKVStoreBase: +def setup_store(storage_backends, block_size, device_id, io_size) -> UcmKVStoreBaseV1: config = {} - config["capacity"] = capacity + config["storage_backends"] = storage_backends config["kv_block_size"] = block_size - config["stream_number"] = stream_number - config["device_id"] = device_id - config["timeout_ms"] = timeout_ms - return UcmDramStore(config) + config["role"] = "worker" + config["device"] = device_id + config["io_size"] = io_size + return UcmPcStoreV1(config) def make_buffers( block_number, device_id, batch_size, block_dim, block_len, block_layer ): - hashes = [secrets.token_hex(16) for _ in range(block_number)] + hashes = [secrets.token_bytes(16) for _ in range(block_number)] tensors = [ [ torch.rand( @@ -62,79 +61,62 @@ def make_buffers( return hashes, tensors -def embed(store: UcmKVStoreBase, hashes: List[str], tensors: List[List[torch.Tensor]]): - results = store.create(hashes) - assert sum(results) == 0 - block_ids = [] - offsets = [] - layers = [] - for hash_id, block in zip(hashes, tensors): - offset = 0 - for layer in block: - block_ids.append(hash_id) - offsets.append(offset) - layers.append(layer) - offset += layer.untyped_storage().size() - task = store.dump(block_ids, offsets, layers) +def embed( + store: UcmKVStoreBaseV1, hashes: List[bytes], tensors: List[List[torch.Tensor]] +): + task = store.dump(hashes, [], tensors) assert task.task_id > 0 - ret = store.wait(task) - assert ret == 0 - store.commit(hashes, True) + store.wait(task) -def fetch(store: UcmKVStoreBase, hashes: List[str], tensors: List[List[torch.Tensor]]): +def fetch( + store: UcmKVStoreBaseV1, hashes: List[bytes], tensors: List[List[torch.Tensor]] +): founds = store.lookup(hashes) for found in founds: assert found - block_ids = [] - offsets = [] - layers = [] - for hash_id, block in zip(hashes, tensors): - offset = 0 - for layer in block: - block_ids.append(hash_id) - offsets.append(offset) - layers.append(layer) - offset += layer.untyped_storage().size() - task = store.load(block_ids, offsets, layers) + task = store.load(hashes, [], tensors) assert task.task_id > 0 - ret = store.wait(task) - assert ret == 0 + store.wait(task) + + +def cmp_and_print_diff(a, b, rtol=0.0, atol=0.0): + for r, (row_a, row_b) in enumerate(zip(a, b)): + for c, (ta, tb) in enumerate(zip(row_a, row_b)): + if not torch.allclose(ta, tb, rtol=rtol, atol=atol): + mask = ~torch.isclose(ta, tb, rtol=rtol, atol=atol) + diff_a = ta[mask].cpu() + diff_b = tb[mask].cpu() + print(f"DIFF at [{r}][{c}] total {mask.sum().item()} element(s)") + print(" a val:", diff_a.flatten()) + print(" b val:", diff_b.flatten()) + assert False def main(): + storage_backends = "." block_number = 4096 device_id = 1 block_dim = 576 - block_len = 128 + block_len = 64 block_elem_size = 2 block_layer = 61 io_size = block_dim * block_len * block_elem_size block_size = io_size * block_layer - batch_size = 256 - stream_number = 10 - timeout_ms = 1000000 - capacity = block_number * block_size * 2 - batch_number = 64 - - store = setup_store(capacity, block_size, stream_number, device_id, timeout_ms) + batch_size = 64 + store = setup_store(storage_backends, block_size, device_id, io_size) hashes, tensors = make_buffers( block_number, device_id, batch_size, block_dim, block_len, block_layer ) total_batches = (block_number + batch_size - 1) // batch_size - for batch in range(total_batches): start = batch_size * batch end = min(start + batch_size, block_number) + tensors2 = [[torch.empty_like(t) for t in row] for row in tensors] embed(store, hashes[start:end], tensors) - - _, new_tensors = make_buffers( - block_number, device_id, batch_size, block_dim, block_len, block_layer - ) - for batch in range(total_batches): - start = batch_size * batch - end = start + batch_size - fetch(store, hashes[start:end], new_tensors) + time.sleep(1) + fetch(store, hashes[start:end], tensors2) + cmp_and_print_diff(tensors, tensors2) if __name__ == "__main__": diff --git a/ucm/store/ucmstore_v1.h b/ucm/store/ucmstore_v1.h new file mode 100644 index 000000000..0f6324c60 --- /dev/null +++ b/ucm/store/ucmstore_v1.h @@ -0,0 +1,122 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#ifndef UNIFIEDCACHE_STORE_V1_H +#define UNIFIEDCACHE_STORE_V1_H + +#include "status/status.h" +#include "type/types.h" + +namespace UC { + +/** + * @brief Abstract interface for a key-value store that supports + * asynchronous load/dump of cached blocks. + * + * Thread safety: All public methods must be thread-safe. Concurrent calls + * are allowed; implementations are responsible for internal synchronization. + */ +class StoreV1 { +public: + virtual ~StoreV1() = default; + + /** + * @brief Check whether the given blocks exist in storage. + * + * @param blocks Array of block identifiers to test. + * @param num Number of block identifiers to test. + * @return Expected> + * - On success: a vector whose i-th element is **true** if blocks[i] + * is present, otherwise **false**. + * - On failure: appropriate Status code. + */ + virtual Expected> Lookup(const Detail::BlockId* blocks, size_t num) = 0; + + /** + * @brief Hint the store to prefetch given blocks into high-speed cache. + * + * This call is **non-blocking** and **fire-and-forget**; it returns + * immediately and carries no completion guarantee. Implementations may + * ignore the hint if prefetching is not supported or resources are + * unavailable. + * + * @param blocks Array of block identifiers to be prefetched. + * @param num Number of block identifiers to be prefetched. + * + * @note Thread-safe; may be called concurrently with other operations. + * @note Default implementation does nothing. + */ + virtual void Prefetch(const Detail::BlockId* blocks, size_t num) = 0; + + /** + * @brief Start an asynchronous load (storage → device) transfer. + * + * @param task Description of shards to be loaded. + * @return Expected + * - On success: a task handle that can be passed to Wait() or Check(). + * - On failure: relevant Status code. + */ + virtual Expected Load(Detail::TaskDesc task) = 0; + + /** + * @brief Start an asynchronous dump (device → storage) transfer. + * + * @param task Description of shards to be stored. + * @return Expected + * - On success: a task handle that can be passed to Wait() or Check(). + * - On failure: relevant Status code. + */ + virtual Expected Dump(Detail::TaskDesc task) = 0; + + /** + * @brief Poll for task completion without blocking. + * + * @param taskId Task handle returned by Load() or Dump(). + * @return Expected + * - **true** if the task has finished (successfully or with an error). + * - **false** if the task is still running. + * - Any other value indicates an error in the poll itself. + */ + virtual Expected Check(Detail::TaskHandle taskId) = 0; + + /** + * @brief Block until the specified task completes. + * + * @param taskId Task handle returned by Load() or Dump(). + * @return Status::OK on successful completion, otherwise an error code + * describing the failure. + */ + virtual Status Wait(Detail::TaskHandle taskId) = 0; + +protected: + /** + * @brief Protected default constructor. + * + * Prevents direct instantiation and enforces derivation. + */ + StoreV1() = default; +}; + +} // namespace UC + +#endif diff --git a/ucm/store/ucmstore_v1.py b/ucm/store/ucmstore_v1.py new file mode 100644 index 000000000..19438781a --- /dev/null +++ b/ucm/store/ucmstore_v1.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# +# MIT License +# +# Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +from abc import ABC, abstractmethod +from typing import Dict, List + +import torch + + +class Task(ABC): + """Asynchronous task handle returned by transfer operations. + + This is an opaque token that can be polled or awaited. + """ + + pass + + +class UcmKVStoreBaseV1(ABC): + """Abstract base class for KV-cache-centric storage backends. + + A concrete storage vendor must implement this interface to participate in + the unified-cache-management (UCM) system. + """ + + def __init__(self, config: Dict[str, object]) -> None: + """Initialize the store with vendor-specific configuration. + + Args: + config: Key-value mapping containing vendor-specific parameters + (e.g., connection string, cache size, compression level). + """ + self.config = config + + @abstractmethod + def cc_store(self) -> int: + """Return a low-level C/C++ pointer to the underlying store. + + Returns: + An opaque ``int`` representing the ``Store*`` instance that can + be passed to native code. + """ + pass + + @abstractmethod + def lookup(self, block_ids: List[bytes]) -> List[bool]: + """Check presence of blocks in external storage. + + Args: + block_ids: List of vLLM block hashes (raw bytes). + + Returns: + A list of booleans, ``True`` if the corresponding block exists in + storage, ``False`` otherwise. The order matches ``block_ids``. + """ + pass + + @abstractmethod + def prefetch(self, block_ids: List[bytes]) -> None: + """Asynchronously prefetch blocks into high-speed cache. + + Args: + block_ids: List of vLLM block hashes to prefetch. + """ + pass + + @abstractmethod + def load( + self, + block_ids: List[bytes], + shard_index: List[int], + dst_tensor: List[List[torch.Tensor]], + ) -> Task: + """Initiate transfer of KV cache from storage to device. + + Args: + block_ids: Hashes of the blocks to load. + shard_index: Shard index for each block. + dst_tensor: Double-list structure where ``dst_tensor[i][j]`` is the + destination PyTorch tensor on device for block ``i``, tensor ``j``. + + Returns: + A ``Task`` handle that can be used to check or wait for completion. + """ + pass + + @abstractmethod + def dump( + self, + block_ids: List[bytes], + shard_index: List[int], + src_tensor: List[List[torch.Tensor]], + ) -> Task: + """Initiate transfer of KV cache from device to storage. + + Args: + block_ids: Hashes of the blocks to write. + shard_index: Shard index for each block. + src_tensor: Double-list structure where ``src_tensor[i][j]`` is the + source PyTorch tensor on device for block ``i``, tensor ``j``. + + Returns: + A ``Task`` handle that can be used to check or wait for completion. + """ + pass + + @abstractmethod + def load_data( + self, + block_ids: List[bytes], + shard_index: List[int], + dst_addr: List[List[int]], + ) -> Task: + """Low-level fetch: copy KV data to device pointers. + + Args: + block_ids: Block hashes to load. + shard_index: Shard index for each block. + dst_addr: Double-list of ``int`` pointers (as Python ``int``) to + pre-allocated device buffers. + + Returns: + A ``Task`` handle for the asynchronous copy. + """ + pass + + @abstractmethod + def dump_data( + self, + block_ids: List[bytes], + shard_index: List[int], + src_addr: List[List[int]], + ) -> Task: + """Low-level dump: copy KV data from device pointers. + + Args: + block_ids: Block hashes to store. + shard_index: Shard index for each block. + src_addr: Double-list of ``int`` pointers to device buffers. + + Returns: + A ``Task`` handle for the asynchronous copy. + """ + pass + + @abstractmethod + def wait(self, task: Task) -> None: + """Block until the given transfer task completes. + + Args: + task: Task handle returned by ``load``, ``dump``, ``load_data``, + or ``dump_data``. + """ + pass + + @abstractmethod + def check(self, task: Task) -> bool: + """Non-blocking poll for task completion. + + Args: + task: Task handle returned by any transfer method. + + Returns: + ``True`` if the task has finished, ``False`` if still in-flight. + """ + pass From 45e11a5f7a334056dbf5b5267d399ca3d3f083c5 Mon Sep 17 00:00:00 2001 From: qyh111 Date: Sat, 13 Dec 2025 14:34:28 +0800 Subject: [PATCH 02/10] [bugfix]add init device (#511) add init device --- ucm/store/pcstore/cc/domain/trans/trans_queue.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ucm/store/pcstore/cc/domain/trans/trans_queue.cc b/ucm/store/pcstore/cc/domain/trans/trans_queue.cc index b69ebfe29..993b60299 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_queue.cc +++ b/ucm/store/pcstore/cc/domain/trans/trans_queue.cc @@ -105,9 +105,14 @@ Status TransQueue::Setup(const int32_t deviceId, const size_t streamNumber, cons UC_ERROR("Failed({}) to make host buffer({},{}).", ts.ToString(), blockSize, bufferNumber); return Status::Error(); } - auto success = - this->devPool_.SetWorkerFn([this](auto t, auto) { this->DeviceWorker(std::move(t)); }) - .Run(); + auto success = this->devPool_ + .SetWorkerInitFn([deviceId](auto&) { + Trans::Device device; + auto ts = device.Setup(deviceId); + return ts.Success(); + }) + .SetWorkerFn([this](auto t, auto) { this->DeviceWorker(std::move(t)); }) + .Run(); if (!success) { return Status::Error(); } success = this->filePool_.SetWorkerFn([this](auto t, auto) { this->FileWorker(std::move(t)); }) .SetNWorker(streamNumber) From 799932b01bb0cf9ba539b627fa84f0f3e8678b55 Mon Sep 17 00:00:00 2001 From: "Mag1c.H" Date: Sat, 13 Dec 2025 15:29:17 +0800 Subject: [PATCH 03/10] [bugfix] clean up shm remnants to fix "BufferOut" error in PCStore (#512) PCStore uses shared memory to share data in DRAM. If the service terminates abnormally and residual files of the shared data are not cleaned up, it will cause newly started services to report BufferOut errors. --- ucm/store/pcstore/cc/api/pcstore.cc | 9 ++++-- ucm/store/pcstore/cc/api/pcstore.h | 6 ++-- ucm/store/pcstore/cc/domain/file/file.cc | 2 +- ucm/store/pcstore/cc/domain/file/file.h | 2 +- ucm/store/pcstore/cc/domain/file/ifile.h | 2 +- .../pcstore/cc/domain/file/posix_file.cc | 4 +-- ucm/store/pcstore/cc/domain/file/posix_file.h | 4 +-- .../pcstore/cc/domain/trans/share_buffer.cc | 28 +++++++++++++++---- .../pcstore/cc/domain/trans/share_buffer.h | 10 +++++-- .../pcstore/cc/domain/trans/trans_manager.cc | 7 +++-- .../pcstore/cc/domain/trans/trans_manager.h | 4 +-- .../cc/domain/trans/trans_share_queue.cc | 7 +++-- .../cc/domain/trans/trans_share_queue.h | 5 ++-- ucm/store/pcstore/cpy/pcstore.py.cc | 1 + ucm/store/pcstore/pcstore_connector.py | 1 + ucm/store/pcstore/pcstore_connector_v1.py | 1 + ucm/store/test/e2e/pcstore_embed.py | 1 + ucm/store/test/e2e/pcstore_embed_v1.py | 1 + ucm/store/test/e2e/pcstore_fetch.py | 2 ++ 19 files changed, 67 insertions(+), 30 deletions(-) diff --git a/ucm/store/pcstore/cc/api/pcstore.cc b/ucm/store/pcstore/cc/api/pcstore.cc index 81b954812..691354865 100644 --- a/ucm/store/pcstore/cc/api/pcstore.cc +++ b/ucm/store/pcstore/cc/api/pcstore.cc @@ -37,11 +37,15 @@ class PcStoreImpl : public PcStore { auto status = this->spaceMgr_.Setup(config.storageBackends, config.kvcacheBlockSize); if (status.Failure()) { return status.Underlying(); } if (config.transferEnable) { + if (config.uniqueId.empty()) { + UC_ERROR("UniqueId is required."); + return Status::InvalidParam().Underlying(); + } status = this->transMgr_.Setup( config.transferLocalRankSize, config.transferDeviceId, config.transferStreamNumber, config.kvcacheBlockSize, config.transferIoSize, config.transferIoDirect, config.transferBufferNumber, this->spaceMgr_.GetSpaceLayout(), - config.transferTimeoutMs, config.transferScatterGatherEnable); + config.transferTimeoutMs, config.transferScatterGatherEnable, config.uniqueId); if (status.Failure()) { return status.Underlying(); } } this->ShowConfig(config); @@ -86,6 +90,7 @@ class PcStoreImpl : public PcStore { UC_INFO("Set UC::BlockSize to {}.", config.kvcacheBlockSize); UC_INFO("Set UC::TransferEnable to {}.", config.transferEnable); if (!config.transferEnable) { return; } + UC_INFO("Set UC::UniqueId to {}.", config.uniqueId); UC_INFO("Set UC::IoSize to {}.", config.transferIoSize); UC_INFO("Set UC::IoDirect to {}.", config.transferIoDirect); UC_INFO("Set UC::LocalRankSize to {}.", config.transferLocalRankSize); @@ -112,4 +117,4 @@ int32_t PcStore::Setup(const Config& config) return impl->Setup(config); } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/api/pcstore.h b/ucm/store/pcstore/cc/api/pcstore.h index 93177a961..5db446ad9 100644 --- a/ucm/store/pcstore/cc/api/pcstore.h +++ b/ucm/store/pcstore/cc/api/pcstore.h @@ -35,6 +35,7 @@ class PcStore : CCStore { std::vector storageBackends; size_t kvcacheBlockSize; bool transferEnable; + std::string uniqueId{}; size_t transferIoSize{262144}; bool transferIoDirect{false}; size_t transferLocalRankSize{1}; @@ -46,7 +47,8 @@ class PcStore : CCStore { Config(const std::vector& storageBackends, const size_t kvcacheBlockSize, const bool transferEnable) - : storageBackends{storageBackends}, kvcacheBlockSize{kvcacheBlockSize}, + : storageBackends{storageBackends}, + kvcacheBlockSize{kvcacheBlockSize}, transferEnable{transferEnable} { } @@ -87,6 +89,6 @@ class PcStore : CCStore { PcStore* impl_{nullptr}; }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/pcstore/cc/domain/file/file.cc b/ucm/store/pcstore/cc/domain/file/file.cc index 8a52074f0..95c33a654 100644 --- a/ucm/store/pcstore/cc/domain/file/file.cc +++ b/ucm/store/pcstore/cc/domain/file/file.cc @@ -94,4 +94,4 @@ void File::ShmUnlink(const std::string& path) { FileImpl{path}.ShmUnlink(); } void File::Remove(const std::string& path) { FileImpl{path}.Remove(); } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/domain/file/file.h b/ucm/store/pcstore/cc/domain/file/file.h index 086518e3a..4bd1291a4 100644 --- a/ucm/store/pcstore/cc/domain/file/file.h +++ b/ucm/store/pcstore/cc/domain/file/file.h @@ -47,6 +47,6 @@ class File { static void Remove(const std::string& path); }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/pcstore/cc/domain/file/ifile.h b/ucm/store/pcstore/cc/domain/file/ifile.h index 74b77cba6..512c33ba4 100644 --- a/ucm/store/pcstore/cc/domain/file/ifile.h +++ b/ucm/store/pcstore/cc/domain/file/ifile.h @@ -76,6 +76,6 @@ class IFile { std::string path_; }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/pcstore/cc/domain/file/posix_file.cc b/ucm/store/pcstore/cc/domain/file/posix_file.cc index bc697f392..b550c0a43 100644 --- a/ucm/store/pcstore/cc/domain/file/posix_file.cc +++ b/ucm/store/pcstore/cc/domain/file/posix_file.cc @@ -25,8 +25,8 @@ #include #include #include -#include #include +#include #include "logger/logger.h" namespace UC { @@ -242,4 +242,4 @@ Status PosixFile::UpdateTime() return Status::OK(); } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/domain/file/posix_file.h b/ucm/store/pcstore/cc/domain/file/posix_file.h index becbd28a3..75c6f0271 100644 --- a/ucm/store/pcstore/cc/domain/file/posix_file.h +++ b/ucm/store/pcstore/cc/domain/file/posix_file.h @@ -53,6 +53,6 @@ class PosixFile : public IFile { int32_t handle_; }; -} // namespace UC +} // namespace UC -#endif // UNIFIEDCACHE_POSIX_FILE_H +#endif // UNIFIEDCACHE_POSIX_FILE_H diff --git a/ucm/store/pcstore/cc/domain/trans/share_buffer.cc b/ucm/store/pcstore/cc/domain/trans/share_buffer.cc index 35e9ae3da..541da9404 100644 --- a/ucm/store/pcstore/cc/domain/trans/share_buffer.cc +++ b/ucm/store/pcstore/cc/domain/trans/share_buffer.cc @@ -24,6 +24,7 @@ #include "share_buffer.h" #include #include +#include #include #include #include "file/file.h" @@ -97,21 +98,36 @@ struct ShareBufferHeader { ShareBlockHeader headers[0]; }; -inline std::string GenShareBufferName(const size_t blockSize, const size_t blockNumber, - const bool ioDirect, const size_t nSharer) +const inline std::string& ShmPrefix() noexcept { - return fmt::format("uc.buf-{}-{}-{}-{:04x}", blockSize, blockNumber, ioDirect, nSharer); + static std::string prefix{"uc_shm_pcstore_"}; + return prefix; +} +void CleanUpShmFileExceptMe(const std::string& me) +{ + namespace fs = std::filesystem; + std::string_view prefix = ShmPrefix(); + fs::path shmDir = "/dev/shm"; + if (!fs::exists(shmDir)) { return; } + for (const auto& entry : fs::directory_iterator(shmDir)) { + const auto& name = entry.path().filename().string(); + if (entry.is_regular_file() && (name.compare(0, prefix.length(), prefix) == 0) && + name != me) { + fs::remove(entry.path()); + } + } } Status ShareBuffer::Setup(const size_t blockSize, const size_t blockNumber, const bool ioDirect, - const size_t nSharer) + const size_t nSharer, const std::string& uniqueId) { this->blockSize_ = blockSize; this->blockNumber_ = blockNumber; this->ioDirect_ = ioDirect; this->nSharer_ = nSharer; this->addr_ = nullptr; - this->shmName_ = GenShareBufferName(blockSize, blockNumber, ioDirect, nSharer); + this->shmName_ = ShmPrefix() + uniqueId; + CleanUpShmFileExceptMe(this->shmName_); auto file = File::Make(this->shmName_); if (!file) { return Status::OutOfMemory(); } auto flags = IFile::OpenFlag::CREATE | IFile::OpenFlag::EXCL | IFile::OpenFlag::READ_WRITE; @@ -305,4 +321,4 @@ uintptr_t ShareBuffer::Reader::GetData() return (uintptr_t)header->Data(); } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/domain/trans/share_buffer.h b/ucm/store/pcstore/cc/domain/trans/share_buffer.h index 3fce7a87c..8827468d0 100644 --- a/ucm/store/pcstore/cc/domain/trans/share_buffer.h +++ b/ucm/store/pcstore/cc/domain/trans/share_buffer.h @@ -49,7 +49,11 @@ class ShareBuffer { private: Reader(const std::string& block, const std::string& path, const size_t length, const bool ioDirect, const size_t nSharer, void* addr) - : block_{block}, path_{path}, length_{length}, ioDirect_{ioDirect}, nSharer_{nSharer}, + : block_{block}, + path_{path}, + length_{length}, + ioDirect_{ioDirect}, + nSharer_{nSharer}, addr_{addr} { } @@ -58,7 +62,7 @@ class ShareBuffer { public: Status Setup(const size_t blockSize, const size_t blockNumber, const bool ioDirect, - const size_t nSharer); + const size_t nSharer, const std::string& uniqueId); ~ShareBuffer(); std::shared_ptr MakeReader(const std::string& block, const std::string& path); @@ -80,6 +84,6 @@ class ShareBuffer { void* addr_; }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/pcstore/cc/domain/trans/trans_manager.cc b/ucm/store/pcstore/cc/domain/trans/trans_manager.cc index 9ef3370f8..d2106ab63 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_manager.cc +++ b/ucm/store/pcstore/cc/domain/trans/trans_manager.cc @@ -29,12 +29,13 @@ namespace UC { Status TransManager::Setup(const size_t rankSize, const int32_t deviceId, const size_t streamNumber, const size_t blockSize, const size_t ioSize, const bool ioDirect, const size_t bufferNumber, const SpaceLayout* layout, - const size_t timeoutMs, const bool scatterGatherEnable) + const size_t timeoutMs, const bool scatterGatherEnable, + const std::string& uniqueId) { auto s = Status::OK(); if (rankSize > 1) { s = this->shareQueue_.Setup(rankSize, deviceId, streamNumber, blockSize, ioSize, ioDirect, - bufferNumber, layout, &this->failureSet_); + bufferNumber, layout, &this->failureSet_, uniqueId); if (s.Failure()) { return s; } } s = this->queue_.Setup(deviceId, streamNumber, blockSize, ioSize, ioDirect, bufferNumber, @@ -115,4 +116,4 @@ Status TransManager::Check(const size_t taskId, bool& finish) noexcept return Status::OK(); } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/domain/trans/trans_manager.h b/ucm/store/pcstore/cc/domain/trans/trans_manager.h index 9a806e6f3..8f14c257a 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_manager.h +++ b/ucm/store/pcstore/cc/domain/trans/trans_manager.h @@ -34,7 +34,7 @@ class TransManager { Status Setup(const size_t rankSize, const int32_t deviceId, const size_t streamNumber, const size_t blockSize, const size_t ioSize, const bool ioDirect, const size_t bufferNumber, const SpaceLayout* layout, const size_t timeoutMs, - const bool scatterGatherEnable); + const bool scatterGatherEnable, const std::string& uniqueId); Status Submit(TransTask task, size_t& taskId) noexcept; Status Wait(const size_t taskId) noexcept; Status Check(const size_t taskId, bool& finish) noexcept; @@ -52,6 +52,6 @@ class TransManager { TaskSet failureSet_; }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/pcstore/cc/domain/trans/trans_share_queue.cc b/ucm/store/pcstore/cc/domain/trans/trans_share_queue.cc index c43d16a85..840bb6b5e 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_share_queue.cc +++ b/ucm/store/pcstore/cc/domain/trans/trans_share_queue.cc @@ -42,14 +42,15 @@ TransShareQueue::~TransShareQueue() Status TransShareQueue::Setup(const size_t nSharer, const int32_t deviceId, const size_t streamNumber, const size_t blockSize, const size_t ioSize, const bool ioDirect, const size_t bufferNumber, - const SpaceLayout* layout, TaskSet* failureSet) + const SpaceLayout* layout, TaskSet* failureSet, + const std::string& uniqueId) { this->deviceId_ = deviceId; this->streamNumber_ = streamNumber; this->ioSize_ = ioSize; this->layout_ = layout; this->failureSet_ = failureSet; - auto status = this->buffer_.Setup(blockSize, bufferNumber, ioDirect, nSharer); + auto status = this->buffer_.Setup(blockSize, bufferNumber, ioDirect, nSharer, uniqueId); if (status.Failure()) { return status; } std::list> start(streamNumber); std::list> fut; @@ -166,4 +167,4 @@ void TransShareQueue::HandleLoadTask(BlockTask& task, Trans::Stream& stream) this->HandleReadyTask(s, task, stream); } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/domain/trans/trans_share_queue.h b/ucm/store/pcstore/cc/domain/trans/trans_share_queue.h index 7c40b0542..7d76cad0a 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_share_queue.h +++ b/ucm/store/pcstore/cc/domain/trans/trans_share_queue.h @@ -63,7 +63,8 @@ class TransShareQueue { ~TransShareQueue(); Status Setup(const size_t nSharer, const int32_t deviceId, const size_t streamNumber, const size_t blockSize, const size_t ioSize, const bool ioDirect, - const size_t bufferNumber, const SpaceLayout* layout, TaskSet* failureSet); + const size_t bufferNumber, const SpaceLayout* layout, TaskSet* failureSet, + const std::string& uniqueId); void Dispatch(TaskPtr task, WaiterPtr waiter); private: @@ -73,6 +74,6 @@ class TransShareQueue { void HandleLoadTask(BlockTask& task, Trans::Stream& stream); }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/store/pcstore/cpy/pcstore.py.cc b/ucm/store/pcstore/cpy/pcstore.py.cc index e8a11346c..cadb1ebe2 100644 --- a/ucm/store/pcstore/cpy/pcstore.py.cc +++ b/ucm/store/pcstore/cpy/pcstore.py.cc @@ -94,6 +94,7 @@ PYBIND11_MODULE(ucmpcstore, module) config.def_readwrite("storageBackends", &UC::PcStorePy::Config::storageBackends); config.def_readwrite("kvcacheBlockSize", &UC::PcStorePy::Config::kvcacheBlockSize); config.def_readwrite("transferEnable", &UC::PcStorePy::Config::transferEnable); + config.def_readwrite("uniqueId", &UC::PcStorePy::Config::uniqueId); config.def_readwrite("transferIoDirect", &UC::PcStorePy::Config::transferIoDirect); config.def_readwrite("transferLocalRankSize", &UC::PcStorePy::Config::transferLocalRankSize); config.def_readwrite("transferDeviceId", &UC::PcStorePy::Config::transferDeviceId); diff --git a/ucm/store/pcstore/pcstore_connector.py b/ucm/store/pcstore/pcstore_connector.py index 13b0b0b6e..00dffa850 100644 --- a/ucm/store/pcstore/pcstore_connector.py +++ b/ucm/store/pcstore/pcstore_connector.py @@ -47,6 +47,7 @@ def __init__(self, config: Dict): transfer_enable = True if config["role"] == "worker" else False param = ucmpcstore.PcStore.Config(storage_backends, block_size, transfer_enable) if transfer_enable: + param.uniqueId = config["unique_id"] param.transferDeviceId = config["device"] param.transferIoSize = config["io_size"] param.transferIoDirect = config.get("use_direct", False) diff --git a/ucm/store/pcstore/pcstore_connector_v1.py b/ucm/store/pcstore/pcstore_connector_v1.py index 48acb5100..d6fcac1bd 100644 --- a/ucm/store/pcstore/pcstore_connector_v1.py +++ b/ucm/store/pcstore/pcstore_connector_v1.py @@ -48,6 +48,7 @@ def __init__(self, config: Dict): transfer_enable = True if config["role"] == "worker" else False param = ucmpcstore.PcStore.Config(storage_backends, block_size, transfer_enable) if transfer_enable: + param.uniqueId = config["unique_id"] param.transferDeviceId = config["device"] param.transferIoSize = config["io_size"] param.transferIoDirect = config.get("use_direct", False) diff --git a/ucm/store/test/e2e/pcstore_embed.py b/ucm/store/test/e2e/pcstore_embed.py index da3e9de86..3681ae2af 100644 --- a/ucm/store/test/e2e/pcstore_embed.py +++ b/ucm/store/test/e2e/pcstore_embed.py @@ -40,6 +40,7 @@ def setup_store(storage_backends, block_size, device_id, io_size) -> UcmKVStoreB config["role"] = "worker" config["device"] = device_id config["io_size"] = io_size + config["unique_id"] = secrets.token_hex(8) return UcmPcStore(config) diff --git a/ucm/store/test/e2e/pcstore_embed_v1.py b/ucm/store/test/e2e/pcstore_embed_v1.py index b8533d202..68e5ffdc2 100644 --- a/ucm/store/test/e2e/pcstore_embed_v1.py +++ b/ucm/store/test/e2e/pcstore_embed_v1.py @@ -40,6 +40,7 @@ def setup_store(storage_backends, block_size, device_id, io_size) -> UcmKVStoreB config["role"] = "worker" config["device"] = device_id config["io_size"] = io_size + config["unique_id"] = secrets.token_hex(8) return UcmPcStoreV1(config) diff --git a/ucm/store/test/e2e/pcstore_fetch.py b/ucm/store/test/e2e/pcstore_fetch.py index 6299d387d..0b3cf1d2e 100644 --- a/ucm/store/test/e2e/pcstore_fetch.py +++ b/ucm/store/test/e2e/pcstore_fetch.py @@ -24,6 +24,7 @@ # import os import random +import secrets from typing import List import torch @@ -39,6 +40,7 @@ def setup_store(storage_backends, block_size, device_id, io_size) -> UcmKVStoreB config["role"] = "worker" config["device"] = device_id config["io_size"] = io_size + config["unique_id"] = secrets.token_hex(8) return UcmPcStore(config) From 0e25822167f05ab2539680849eca3299625de383 Mon Sep 17 00:00:00 2001 From: "Mag1c.H" Date: Sat, 13 Dec 2025 15:57:24 +0800 Subject: [PATCH 04/10] [Misc] enable CI in feature branch (#513) enable CI in feature branch --- .github/workflows/cpp-linter.yml | 2 +- .github/workflows/ucmstore.yml | 2 +- .github/workflows/unifiedcache_test.yml | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml index 189683d5d..7e9525b6c 100644 --- a/.github/workflows/cpp-linter.yml +++ b/.github/workflows/cpp-linter.yml @@ -4,7 +4,7 @@ on: push: branches: [ "*" ] pull_request: - branches: [ "dev*", "main", "*release" ] + branches: [ "dev*", "main", "*release", "feature*" ] jobs: diff --git a/.github/workflows/ucmstore.yml b/.github/workflows/ucmstore.yml index 08c52aad2..5d6ce9b47 100644 --- a/.github/workflows/ucmstore.yml +++ b/.github/workflows/ucmstore.yml @@ -6,7 +6,7 @@ on: push: branches: [ "*" ] pull_request: - branches: [ "dev*", "main", "*release" ] + branches: [ "dev*", "main", "*release", "feature*" ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) diff --git a/.github/workflows/unifiedcache_test.yml b/.github/workflows/unifiedcache_test.yml index ead608ba8..9fa8d1a84 100644 --- a/.github/workflows/unifiedcache_test.yml +++ b/.github/workflows/unifiedcache_test.yml @@ -6,11 +6,13 @@ on: - 'main' - 'dev*' - '*release' + - 'feature*' pull_request: branches: - 'main' - 'dev*' - '*release' + - 'feature*' jobs: # gpu-test: From b8c25c486be2981fd82c97e74ab21e0e83ccbdb6 Mon Sep 17 00:00:00 2001 From: qyh111 Date: Sat, 13 Dec 2025 16:22:46 +0800 Subject: [PATCH 05/10] [feat]adapt v1 store (#514) * adapt v1 store * code style --- ucm/integration/vllm/ucm_connector.py | 353 +++++++++++----------- ucm/store/pcstore/pcstore_connector_v1.py | 2 +- 2 files changed, 179 insertions(+), 176 deletions(-) diff --git a/ucm/integration/vllm/ucm_connector.py b/ucm/integration/vllm/ucm_connector.py index 66216a255..ee8558e84 100644 --- a/ucm/integration/vllm/ucm_connector.py +++ b/ucm/integration/vllm/ucm_connector.py @@ -4,7 +4,7 @@ import pickle import time from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Callable, List, Optional +from typing import TYPE_CHECKING, Callable, List, Optional, Tuple import torch from vllm.config import VllmConfig @@ -20,8 +20,8 @@ from ucm.logger import init_logger from ucm.shared.metrics import ucmmonitor from ucm.shared.metrics.observability import UCMStatsLogger -from ucm.store.factory import UcmConnectorFactory -from ucm.store.ucmstore import Task, UcmKVStoreBase +from ucm.store.factory_v1 import UcmConnectorFactoryV1 +from ucm.store.ucmstore_v1 import Task, UcmKVStoreBaseV1 from ucm.utils import Config if TYPE_CHECKING: @@ -35,7 +35,7 @@ @dataclass class RequestMeta: - ucm_block_ids: list[str] = field(default_factory=list) + ucm_block_ids: list[bytes] = field(default_factory=list) hbm_hit_block_num: int = 0 # local_computed_block + external_computed_block total_hit_block_num: int = 0 @@ -47,9 +47,9 @@ class RequestMeta: @dataclass class RequestDispatchMeta: load_block_ids: tuple[ - list[str], list[int] + list[bytes], list[int] ] # [0] mean ucm_block_ids, [1] means vllm_block_ids - dump_block_ids: tuple[list[str], list[int]] + dump_block_ids: tuple[list[bytes], list[int]] @dataclass @@ -69,14 +69,14 @@ def __init__(self, vllm_config, rank_id): if RequestHasher._SEED_HASH is None: RequestHasher._SEED_HASH = self("UCM_HASH_SEED") - def __call__(self, input_data) -> int: - if isinstance(input_data, str): - input_bytes = input_data.encode("utf-8") + def __call__(self, input_data) -> bytes: + if isinstance(input_data, bytes): + input_bytes = input_data else: input_bytes = pickle.dumps(input_data, protocol=pickle.HIGHEST_PROTOCOL) h = hashlib.md5(self.meta_bytes + input_bytes) - return int.from_bytes(h.digest(), byteorder="big") + return h.digest() class UCMDirectConnector(KVConnectorBase_V1): @@ -95,6 +95,10 @@ def __init__(self, vllm_config: "VllmConfig", role: KVConnectorRole): self.block_size = self._vllm_config.cache_config.block_size self.is_mla = self._vllm_config.model_config.is_deepseek_mla self.is_dsa = False + self.num_layers = self._vllm_config.model_config.get_num_layers( + self._vllm_config.parallel_config + ) + self.tp_size = self._vllm_config.parallel_config.tensor_parallel_size self.kv_cache_dtype: torch.dtype = None if current_platform.is_cuda_alike(): @@ -110,21 +114,19 @@ def __init__(self, vllm_config: "VllmConfig", role: KVConnectorRole): if self.local_rank >= 0: self.device = torch_dev.device(f"{dev_name}:{self.local_rank}") - self._layer_offset_cache = {} - - self.store: UcmKVStoreBase - if role == KVConnectorRole.SCHEDULER: - self.request_hasher = RequestHasher(vllm_config, 0) - else: - self.request_hasher = RequestHasher(vllm_config, self.global_rank) + self.k_store: UcmKVStoreBaseV1 + self.v_store: Optional[UcmKVStoreBaseV1] = None # save block info, avoid hash request twice, and track them until request finished self.requests_meta: dict[str, RequestMeta] = {} ucm_config = Config(vllm_config.kv_transfer_config) + self.engine_id = vllm_config.kv_transfer_config.engine_id self.launch_config = ucm_config.get_config() - + logger.info(f"self.launch_config: {self.launch_config}") + self.connector_configs = self.launch_config.get("ucm_connectors", []) + assert len(self.connector_configs) > 0, "no storage connector name in config." self.load_only_first_rank: bool = ( self.launch_config.get("load_only_first_rank", self.is_mla) and self.is_mla ) @@ -134,42 +136,28 @@ def __init__(self, vllm_config: "VllmConfig", role: KVConnectorRole): self.broadcast_fn = self.group_coordinator.broadcast self.broadcast_stream = torch.cuda.Stream() - logger.info(f"self.launch_config: {self.launch_config}") - connector_configs = self.launch_config.get("ucm_connectors", []) - assert len(connector_configs) > 0, "no storage connector name in config." - - name = connector_configs[0].get("ucm_connector_name") - config = connector_configs[0].get("ucm_connector_config") or {} - config["device"] = self.local_rank - config["role"] = "scheduler" if role == KVConnectorRole.SCHEDULER else "worker" - element_size = vllm_config.model_config.dtype.itemsize - single_head_dim = vllm_config.model_config.get_head_size() - num_head_per_tp = vllm_config.model_config.get_num_kv_heads( - vllm_config.parallel_config - ) - total_tp_size = vllm_config.parallel_config.tensor_parallel_size - num_layers = vllm_config.model_config.get_num_layers( - vllm_config.parallel_config - ) - block_size_per_layer = self.block_size * element_size * single_head_dim - config["kv_block_size"] = ( - block_size_per_layer - * num_layers - * (1 if self.is_mla else num_head_per_tp * 2) - ) - config["io_size"] = block_size_per_layer * ( - 1 if self.is_mla else num_head_per_tp - ) - self.store = UcmConnectorFactory.create_connector(name, config) - self.block_data_size = config["kv_block_size"] - - logger.info("init UCConnectorImpl, connector: %s", name) + name = self.connector_configs[0].get("ucm_connector_name") + config = self.connector_configs[0].get("ucm_connector_config") or {} + storage_backends = [ + path for path in config["storage_backends"].split(":") if path + ] + self.k_storage_backends = [os.path.join(p, "k") for p in storage_backends] + self.v_storage_backends = [os.path.join(p, "v") for p in storage_backends] + os.makedirs(self.k_storage_backends[0], exist_ok=True) + os.makedirs(self.v_storage_backends[0], exist_ok=True) logger.info( - "single file size = %d MB, io_size = %d KB,", - config["kv_block_size"] / 1024 / 1024, - config["io_size"] / 1024, + f"Created subdirectories: {self.k_storage_backends}, {self.v_storage_backends}" ) + if role == KVConnectorRole.SCHEDULER: + self.request_hasher = RequestHasher(vllm_config, 0) + # init scheduler-size connector + config["storage_backends"] = ":".join(self.k_storage_backends) + config["role"] = "scheduler" + self.k_store = UcmConnectorFactoryV1.create_connector(name, config) + else: + self.request_hasher = RequestHasher(vllm_config, self.global_rank) + self.metrics_config = self.launch_config.get("metrics_config_path", "") if self.metrics_config: self.stats_logger = UCMStatsLogger( @@ -188,7 +176,7 @@ def __init__(self, vllm_config: "VllmConfig", role: KVConnectorRole): # invlalid block ids due to load errors self._invalid_block_ids: set[int] = set() - def generate_hash(self, block_size: int, request: "Request") -> list[str]: + def generate_hash(self, block_size: int, request: "Request") -> list[bytes]: token_ids = request.all_token_ids ret = [] @@ -205,10 +193,81 @@ def generate_hash(self, block_size: int, request: "Request") -> list[str]: (parent_block_hash_value, block_token_ids_tuple) ) parent_block_hash_value = hash_value - ret.append(str(hash_value)) + ret.append(hash_value) return ret + def register_kv_caches(self, kv_caches: dict[str, torch.Tensor]): + self.kv_caches = kv_caches + sample_kv_layer = next(iter(self.kv_caches.values())) + if self.kv_cache_dtype is None: + self.kv_cache_dtype = sample_kv_layer[0].dtype + if isinstance(sample_kv_layer, torch.Tensor): + logger.info(f"kv cache shape {sample_kv_layer.shape}") + elif isinstance(sample_kv_layer, Tuple): + # Since vllm_ascend >= 0.10.0, the MLA model's tensor shape has changed to Tuple + # [(num_blocks, block_size, num_kv_heads, nope_dim/rope_dim)] + # Currently, we treat it as GQA, and use is_dsa to mark it + for i, tensor in enumerate(sample_kv_layer): + logger.info(f"kv cache shape {i}: {tensor.shape}") + if self.is_mla: + self.is_mla = False + self.is_dsa = True + logger.info(f"use mla: {self.is_mla}, use dsa: {self.is_dsa}") + + # init work-side connector + # When handling the GQA case, we will separately dump the k_cache and v_cache. + name = self.connector_configs[0].get("ucm_connector_name") + config = self.connector_configs[0].get("ucm_connector_config") or {} + config["device"] = self.local_rank + config["role"] = "worker" + config["local_rank_size"] = self.tp_size if self.is_mla or self.is_dsa else 1 + if len(sample_kv_layer) == 2: + k_io_size = ( + sample_kv_layer[0][0].numel() * sample_kv_layer[0][0].element_size() + ) + config["io_size"] = k_io_size + config["kv_block_size"] = k_io_size * self.num_layers + config["storage_backends"] = ":".join(self.k_storage_backends) + config["unique_id"] = self.engine_id + "k" + self.k_store = UcmConnectorFactoryV1.create_connector(name, config) + logger.info("init UCConnectorImpl, k_connector: %s", name) + logger.info( + "single file size = %.3f MB, io_size = %d KB,", + config["kv_block_size"] / 1024 / 1024, + config["io_size"] / 1024, + ) + + v_io_size = ( + sample_kv_layer[1][0].numel() * sample_kv_layer[1][0].element_size() + ) + config["io_size"] = v_io_size + config["kv_block_size"] = v_io_size * self.num_layers + config["storage_backends"] = ":".join(self.v_storage_backends) + config["unique_id"] = self.engine_id + "v" + self.v_store = UcmConnectorFactoryV1.create_connector(name, config) + logger.info("init UCConnectorImpl, v_connector: %s", name) + logger.info( + "single file size = %.3f MB, io_size = %d KB,", + config["kv_block_size"] / 1024 / 1024, + config["io_size"] / 1024, + ) + self.block_data_size = (k_io_size + v_io_size) * self.num_layers + else: + k_io_size = sample_kv_layer[0].numel() * sample_kv_layer[0].element_size() + config["io_size"] = k_io_size + config["kv_block_size"] = k_io_size * self.num_layers + config["storage_backends"] = ":".join(self.k_storage_backends) + config["unique_id"] = self.engine_id + "k" + self.k_store = UcmConnectorFactoryV1.create_connector(name, config) + logger.info("init UCConnectorImpl, k_connector: %s", name) + logger.info( + "single file size = %.3f MB, io_size = %d KB,", + config["kv_block_size"] / 1024 / 1024, + config["io_size"] / 1024, + ) + self.block_data_size = k_io_size * self.num_layers + def get_num_new_matched_tokens( self, request: "Request", @@ -223,7 +282,7 @@ def get_num_new_matched_tokens( if not external_block_ids: return 0, False - lookup_results = self.store.lookup(external_block_ids) + lookup_results = self.k_store.lookup(external_block_ids) external_hit_blocks = 0 for i, hit in enumerate(lookup_results): if not hit: @@ -361,30 +420,6 @@ def build_connector_meta( return UCMConnectorMetadata(requests_dispatch_meta) - def _init_kv_caches_from_forward_context(self, forward_context: "ForwardContext"): - if len(self.kv_caches) > 0: - return - for layer_name in forward_context.no_compile_layers: - attn_layer = forward_context.no_compile_layers[layer_name] - if not hasattr(attn_layer, "kv_cache"): - continue - - if layer_name not in self.kv_caches: - self.kv_caches[layer_name] = attn_layer.kv_cache[ - forward_context.virtual_engine - ] - # Since vllm_ascend >= 0.10.0, the MLA model's tensor shape has changed to - # (2, num_blocks, block_size, num_kv_heads, nope_dim/rope_dim). - # Currently, we treat it as GQA, and use is_dsa to mark it, - # which works but leads to space inefficiency. - # TODO: Optimize this to avoid unnecessary space usage. - sample_kv_layer = next(iter(self.kv_caches.values())) - if self.is_mla and len(sample_kv_layer) == 2: - self.is_mla = False - self.is_dsa = True - if self.kv_cache_dtype is None: - self.kv_cache_dtype = sample_kv_layer[0].dtype - @staticmethod def _extract_layer_index(layer_name: str) -> Optional[int]: """ @@ -395,70 +430,36 @@ def _extract_layer_index(layer_name: str) -> Optional[int]: return int(chunk) return None - def _precompute_layer_offsets(self): - if not self.kv_caches: - return - - sample_kv_layer = next(iter(self.kv_caches.values())) - elem_size = sample_kv_layer[0].element_size() - block_data_size = ( - sample_kv_layer[0].numel() if self.is_mla else sample_kv_layer[0][0].numel() - ) * elem_size - layer_data_size = block_data_size if self.is_mla else block_data_size * 2 - - # precompute all layers offset - for layer_name, _ in self.kv_caches.items(): - layer_id = self._extract_layer_index(layer_name) - assert layer_id is not None - k_offset = layer_data_size * layer_id - v_offset = k_offset + block_data_size if not self.is_mla else 0 - self._layer_offset_cache[layer_name] = (k_offset, v_offset) - - def _get_tensor_and_offset( - self, vllm_block_ids: list[int], kv_layer: torch.Tensor, layer_name: str - ) -> tuple[list[torch.Tensor], list[int]]: + def _get_tensors( + self, vllm_block_id: int + ) -> Tuple[List[torch.Tensor], List[torch.Tensor]]: """ GQA/MHA: one layer shape is (2, num_blocks, block_size, num_kv_heads, head_size) MLA: one layer shape is (num_blocks, block_size, head_size) """ - k_tensors, k_offsets = [], [] - v_tensors, v_offsets = [], [] - k_offset, v_offset = self._layer_offset_cache[layer_name] - - for vllm_block_id in vllm_block_ids: + k_tensors, v_tensors = [], [] + for _, kv_layer in self.kv_caches.items(): k_tensors.append( kv_layer[vllm_block_id] if self.is_mla else kv_layer[0][vllm_block_id] ) - k_offsets.append(k_offset) if not self.is_mla: v_tensors.append(kv_layer[1][vllm_block_id]) - v_offsets.append(v_offset) - return k_tensors + v_tensors, k_offsets + v_offsets - - def _generate_task(self, vllm_block_ids: List[int], ucm_block_ids: List[str]): - if not self._layer_offset_cache: - self._precompute_layer_offsets() - - num_layers = len(self.kv_caches) - num_blocks_per_layer = len(vllm_block_ids) - num_tensors_per_layer = num_blocks_per_layer * (1 if self.is_mla else 2) - dst_tensor_addr = [None] * (num_layers * num_tensors_per_layer) - ucm_offsets = [0] * (num_layers * num_tensors_per_layer) - - idx = 0 - for layer_name, one_layer_kv_cache in self.kv_caches.items(): - tensors, offsets = self._get_tensor_and_offset( - vllm_block_ids, one_layer_kv_cache, layer_name - ) - dst_tensor_addr[idx : idx + len(tensors)] = tensors - ucm_offsets[idx : idx + len(offsets)] = offsets - idx += len(tensors) - - repeat_times = len(self.kv_caches) * (1 if self.is_mla else 2) - ucm_total_block_ids = ucm_block_ids * repeat_times - - assert len(ucm_total_block_ids) == len(ucm_offsets) == len(dst_tensor_addr) - return ucm_total_block_ids, ucm_offsets, dst_tensor_addr + return k_tensors, v_tensors + + def _generate_task( + self, vllm_block_ids: List[int], ucm_block_ids: List[bytes] + ) -> Tuple[ + List[bytes], List[int], List[List[torch.Tensor]], List[List[torch.Tensor]] + ]: + block_ids, shard_indexs, total_k_tensors, total_v_tensors = [], [], [], [] + for i, vllm_block_id in enumerate(vllm_block_ids): + k_tensors, v_tensors = self._get_tensors(vllm_block_id) + block_ids.append(ucm_block_ids[i]) + total_k_tensors.append(k_tensors) + total_v_tensors.append(v_tensors) + shard_indexs.append(0) + + return block_ids, shard_indexs, total_k_tensors, total_v_tensors def _broadcast(self, dst_tensor_addr: list[torch.Tensor]): rec_tensor: torch.Tensor = None @@ -483,9 +484,7 @@ def start_load_kv(self, forward_context: "ForwardContext", **kwargs) -> None: metadata = self._get_connector_metadata() assert isinstance(metadata, UCMConnectorMetadata) - self._init_kv_caches_from_forward_context(forward_context) - - request_to_task: dict[str, Optional[Task]] = {} + request_to_task: dict[str, Optional[List[Task]]] = {} req_broadcast_addr = {} is_load = False num_loaded_block = 0 @@ -501,26 +500,34 @@ def start_load_kv(self, forward_context: "ForwardContext", **kwargs) -> None: ucm_block_ids, vllm_block_ids = request.load_block_ids if self.global_rank != 0 and not self.is_mla and not self.is_dsa: for i, ucm_block_id in enumerate(ucm_block_ids): - ucm_block_ids[i] = str(self.request_hasher(ucm_block_id)) - ucm_total_block_ids, ucm_offsets, dst_tensor_addr = self._generate_task( + ucm_block_ids[i] = self.request_hasher(ucm_block_id) + block_ids, shard_indexs, k_tensors, v_tensors = self._generate_task( vllm_block_ids, ucm_block_ids ) if self.global_rank == 0 or not self.load_only_first_rank: - request_to_task[request_id] = self.store.load( - ucm_total_block_ids, ucm_offsets, dst_tensor_addr - ) + k_task = self.k_store.load(block_ids, shard_indexs, k_tensors) + request_to_task[request_id] = [k_task] + if v_tensors and self.v_store: + v_task = self.v_store.load(block_ids, shard_indexs, v_tensors) + request_to_task[request_id].append(v_task) else: request_to_task[request_id] = None - req_broadcast_addr[request_id] = dst_tensor_addr + req_broadcast_addr[request_id] = [t for row in k_tensors for t in row] + [ + t for row in v_tensors for t in row + ] - for request_id, task in request_to_task.items(): + for request_id, tasks in request_to_task.items(): # TODO error handling if self.global_rank == 0 or not self.load_only_first_rank: - if self.store.wait(task) != 0: + try: + self.k_store.wait(tasks[0]) + if len(tasks) > 1 and self.v_store: + self.v_store.wait(tasks[1]) + except RuntimeError as e: + logger.error("request {request_id} load kv cache failed.:", e) self._invalid_block_ids.update( metadata.request_meta[request_id].load_block_ids[1] ) - logger.error(f"request {request_id} load kv cache failed.") if self.load_only_first_rank: self._broadcast(req_broadcast_addr[request_id]) load_end_time = time.perf_counter() * 1000 @@ -567,8 +574,7 @@ def wait_for_save(self) -> None: metadata = self._get_connector_metadata() assert isinstance(metadata, UCMConnectorMetadata) - request_to_task: dict[str, Task] = {} - request_to_blocks: dict[str, list[str]] = {} + request_to_task: dict[str, List[Task]] = {} is_save = False num_saved_block = 0 num_saved_request = 0 @@ -583,36 +589,23 @@ def wait_for_save(self) -> None: ucm_block_ids, vllm_block_ids = request.dump_block_ids if self.global_rank != 0: for i, ucm_block_id in enumerate(ucm_block_ids): - ucm_block_ids[i] = str(self.request_hasher(ucm_block_id)) - rets = self.store.create(ucm_block_ids) - end = 0 - for i, ret in enumerate(rets): - if ret != 0: - logger.error( - f"create blocks for {request_id} failed, block index: {i}, ret code: {ret}" - ) - break - end += 1 - - if end == 0: - continue - ucm_block_ids = ucm_block_ids[:end] - vllm_block_ids = vllm_block_ids[:end] - ucm_total_block_ids, ucm_offsets, dst_tensor_addr = self._generate_task( + ucm_block_ids[i] = self.request_hasher(ucm_block_id) + block_ids, shard_indexs, k_tensors, v_tensors = self._generate_task( vllm_block_ids, ucm_block_ids ) - request_to_task[request_id] = self.store.dump( - ucm_total_block_ids, ucm_offsets, dst_tensor_addr - ) - request_to_blocks[request_id] = ucm_block_ids - - for request_id, task in request_to_task.items(): - ucm_block_ids = request_to_blocks[request_id] - if self.store.wait(task) == 0: - self.store.commit(ucm_block_ids, True) - else: - logger.error(f"request {request_id} dump kv cache failed.") - self.store.commit(ucm_block_ids, False) + k_task = self.k_store.dump(block_ids, shard_indexs, k_tensors) + request_to_task[request_id] = [k_task] + if v_tensors and self.v_store: + v_task = self.v_store.dump(block_ids, shard_indexs, v_tensors) + request_to_task[request_id].append(v_task) + + for request_id, tasks in request_to_task.items(): + try: + self.k_store.wait(tasks[0]) + if len(tasks) > 1 and self.v_store: + self.v_store.wait(tasks[1]) + except RuntimeError as e: + logger.error("request {request_id} dump kv cache failed.:", e) save_end_time = time.perf_counter() * 1000 save_speed = ( num_saved_block @@ -793,6 +786,16 @@ def update_state_after_alloc( """ self.connector.update_state_after_alloc(request, blocks, num_external_tokens) + def register_kv_caches(self, kv_caches: dict[str, torch.Tensor]): + """ + Initialize with the KV caches. Useful for pre-registering the + KV Caches in the KVConnector (e.g. for NIXL). + + Args: kv_caches: + dictionary of layer names, kv cache + """ + self.connector.register_kv_caches(kv_caches) + def build_connector_meta( self, scheduler_output: SchedulerOutput ) -> KVConnectorMetadata: diff --git a/ucm/store/pcstore/pcstore_connector_v1.py b/ucm/store/pcstore/pcstore_connector_v1.py index d6fcac1bd..3cf964fea 100644 --- a/ucm/store/pcstore/pcstore_connector_v1.py +++ b/ucm/store/pcstore/pcstore_connector_v1.py @@ -44,7 +44,7 @@ def __init__(self, config: Dict): storage_backends = [ path for path in config["storage_backends"].split(":") if path ] - block_size = int(config["kv_block_size"]) + block_size = config.get("kv_block_size", 33554432) transfer_enable = True if config["role"] == "worker" else False param = ucmpcstore.PcStore.Config(storage_backends, block_size, transfer_enable) if transfer_enable: From f570c069955fb541d6510f9dd19daec2f1fa1769 Mon Sep 17 00:00:00 2001 From: qyh111 Date: Sat, 13 Dec 2025 17:05:14 +0800 Subject: [PATCH 06/10] set default local_rank_size to 1 (#515) --- ucm/store/pcstore/pcstore_connector.py | 2 +- ucm/store/pcstore/pcstore_connector_v1.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ucm/store/pcstore/pcstore_connector.py b/ucm/store/pcstore/pcstore_connector.py index 00dffa850..7217620ec 100644 --- a/ucm/store/pcstore/pcstore_connector.py +++ b/ucm/store/pcstore/pcstore_connector.py @@ -53,7 +53,7 @@ def __init__(self, config: Dict): param.transferIoDirect = config.get("use_direct", False) param.transferStreamNumber = config.get("stream_number", 8) param.transferBufferNumber = config.get("buffer_number", 4096) - param.transferLocalRankSize = config.get("local_rank_size", 8) + param.transferLocalRankSize = config.get("local_rank_size", 1) param.transferScatterGatherEnable = config.get("use_scatter_gatter", False) ret = self.store.Setup(param) if ret != 0: diff --git a/ucm/store/pcstore/pcstore_connector_v1.py b/ucm/store/pcstore/pcstore_connector_v1.py index 3cf964fea..39f0589ff 100644 --- a/ucm/store/pcstore/pcstore_connector_v1.py +++ b/ucm/store/pcstore/pcstore_connector_v1.py @@ -54,7 +54,7 @@ def __init__(self, config: Dict): param.transferIoDirect = config.get("use_direct", False) param.transferStreamNumber = config.get("stream_number", 8) param.transferBufferNumber = config.get("buffer_number", 4096) - param.transferLocalRankSize = config.get("local_rank_size", 8) + param.transferLocalRankSize = config.get("local_rank_size", 1) param.transferScatterGatherEnable = config.get("use_scatter_gatter", False) ret = self.store.Setup(param) if ret != 0: From 09ef857e97c4aeb010c41a6c4c240f8ee9681893 Mon Sep 17 00:00:00 2001 From: Lijiachen1018 <30387633+Lijiachen1018@users.noreply.github.com> Date: Sat, 13 Dec 2025 17:49:00 +0800 Subject: [PATCH 07/10] [feat]threadpool monitor (#500) threadpool monitor Co-authored-by: lijiachen19 --- ucm/shared/infra/thread/thread_pool.h | 155 ++++++++++++++---- .../test/case/infra/thread_pool_test.cc | 101 ++++++++++++ .../pcstore/cc/domain/trans/trans_manager.cc | 2 +- .../pcstore/cc/domain/trans/trans_queue.cc | 29 +++- .../pcstore/cc/domain/trans/trans_queue.h | 8 +- 5 files changed, 253 insertions(+), 42 deletions(-) create mode 100644 ucm/shared/test/case/infra/thread_pool_test.cc diff --git a/ucm/shared/infra/thread/thread_pool.h b/ucm/shared/infra/thread/thread_pool.h index baa514ed7..e2aecdef2 100644 --- a/ucm/shared/infra/thread/thread_pool.h +++ b/ucm/shared/infra/thread/thread_pool.h @@ -24,12 +24,17 @@ #ifndef UNIFIEDCACHE_INFRA_THREAD_POOL_H #define UNIFIEDCACHE_INFRA_THREAD_POOL_H +#include #include #include #include #include +#include #include +#include #include +#include +#include namespace UC { @@ -37,8 +42,25 @@ template class ThreadPool { using WorkerInitFn = std::function; using WorkerFn = std::function; + using WorkerTimeoutFn = std::function; using WorkerExitFn = std::function; + class StopToken { + std::shared_ptr> flag_ = std::make_shared>(false); + + public: + void RequestStop() noexcept { this->flag_->store(true, std::memory_order_relaxed); } + bool StopRequested() const noexcept { return this->flag_->load(std::memory_order_relaxed); } + }; + + struct Worker { + ssize_t tid; + std::thread th; + StopToken stop; + std::weak_ptr current; + std::atomic tp{}; + }; + public: ThreadPool() = default; ThreadPool(const ThreadPool&) = delete; @@ -46,12 +68,13 @@ class ThreadPool { ~ThreadPool() { { - std::unique_lock lk(this->mtx_); + std::lock_guard lock(this->taskMtx_); this->stop_ = true; this->cv_.notify_all(); } - for (auto& w : this->workers_) { - if (w.joinable()) { w.join(); } + if (this->monitor_.joinable()) { this->monitor_.join(); } + for (auto& worker : this->workers_) { + if (worker->th.joinable()) { worker->th.join(); } } } ThreadPool& SetWorkerFn(WorkerFn&& fn) @@ -69,6 +92,14 @@ class ThreadPool { this->exitFn_ = std::move(fn); return *this; } + ThreadPool& SetWorkerTimeoutFn(WorkerTimeoutFn&& fn, const size_t timeoutMs, + const size_t intervalMs = 1000) + { + this->timeoutFn_ = std::move(fn); + this->timeoutMs_ = timeoutMs; + this->intervalMs_ = intervalMs; + return *this; + } ThreadPool& SetNWorker(const size_t nWorker) { this->nWorker_ = nWorker; @@ -77,64 +108,126 @@ class ThreadPool { bool Run() { if (this->nWorker_ == 0) { return false; } - if (!this->fn_) { return false; } - std::list> start(this->nWorker_); - std::list> fut; - for (auto& s : start) { - fut.push_back(s.get_future()); - this->workers_.emplace_back([&] { this->Worker(s); }); + if (this->fn_ == nullptr) { return false; } + this->workers_.reserve(this->nWorker_); + for (size_t i = 0; i < this->nWorker_; i++) { + if (!this->AddOneWorker()) { return false; } } - auto success = true; - for (auto& f : fut) { - if (!f.get()) { success = false; } + if (this->timeoutMs_ > 0) { + this->monitor_ = std::thread([this] { this->MonitorLoop(); }); } - return success; + return true; } void Push(std::list& tasks) noexcept { - std::unique_lock lk(this->mtx_); + std::unique_lock lock(this->taskMtx_); this->taskQ_.splice(this->taskQ_.end(), tasks); this->cv_.notify_all(); } void Push(Task&& task) noexcept { - std::unique_lock lk(this->mtx_); + std::unique_lock lock(this->taskMtx_); this->taskQ_.push_back(std::move(task)); this->cv_.notify_one(); } private: - void Worker(std::promise& started) noexcept + bool AddOneWorker() + { + try { + auto worker = std::make_shared(); + std::promise prom; + auto fut = prom.get_future(); + worker->th = std::thread([this, worker, &prom] { this->WorkerLoop(prom, worker); }); + auto success = fut.get(); + if (!success) { return false; } + this->workers_.push_back(worker); + return true; + } catch (...) { + return false; + } + } + void WorkerLoop(std::promise& prom, std::shared_ptr worker) { + worker->tid = syscall(SYS_gettid); WorkerArgs args = nullptr; auto success = true; if (this->initFn_) { success = this->initFn_(args); } - started.set_value(success); + prom.set_value(success); while (success) { - std::unique_lock lk(this->mtx_); - this->cv_.wait(lk, [this] { return this->stop_ || !this->taskQ_.empty(); }); - if (this->stop_) { break; } - if (this->taskQ_.empty()) { continue; } - auto task = std::make_shared(std::move(this->taskQ_.front())); - this->taskQ_.pop_front(); - lk.unlock(); + std::shared_ptr task = nullptr; + { + std::unique_lock lock(this->taskMtx_); + this->cv_.wait(lock, [this, worker] { + return this->stop_ || worker->stop.StopRequested() || !this->taskQ_.empty(); + }); + if (this->stop_ || worker->stop.StopRequested()) { break; } + if (this->taskQ_.empty()) { continue; } + task = std::make_shared(std::move(this->taskQ_.front())); + this->taskQ_.pop_front(); + } + worker->current = task; + worker->tp.store(std::chrono::steady_clock::now(), std::memory_order_relaxed); this->fn_(*task, args); + if (worker->stop.StopRequested()) { break; } + worker->current.reset(); + worker->tp.store({}, std::memory_order_relaxed); } if (this->exitFn_) { this->exitFn_(args); } } + void MonitorLoop() + { + const auto interval = std::chrono::milliseconds(this->intervalMs_); + while (true) { + { + std::unique_lock lock(this->taskMtx_); + this->cv_.wait_for(lock, interval, [this] { return this->stop_; }); + if (this->stop_) { break; } + } + size_t nWorker = this->Monitor(); + for (size_t i = nWorker; i < this->nWorker_; i++) { (void)this->AddOneWorker(); } + } + } + + size_t Monitor() + { + using namespace std::chrono; + const auto timeout = milliseconds(this->timeoutMs_); + size_t nWorker = 0; + for (auto it = this->workers_.begin(); it != this->workers_.end();) { + auto tp = (*it)->tp.load(std::memory_order_relaxed); + auto task = (*it)->current.lock(); + auto now = steady_clock::now(); + if (task && tp != steady_clock::time_point{} && now - tp > timeout) { + if (this->timeoutFn_) { this->timeoutFn_(*task, (*it)->tid); } + (*it)->stop.RequestStop(); + if ((*it)->th.joinable()) { (*it)->th.detach(); } + it = this->workers_.erase(it); + } else { + it++; + } + nWorker++; + } + return nWorker; + } + private: + WorkerInitFn initFn_{nullptr}; + WorkerFn fn_{nullptr}; + WorkerTimeoutFn timeoutFn_{nullptr}; + WorkerExitFn exitFn_{nullptr}; + size_t timeoutMs_{0}; + size_t intervalMs_{0}; + size_t nWorker_{0}; bool stop_{false}; - size_t nWorker_{1}; - std::list workers_; - WorkerInitFn initFn_; - WorkerFn fn_; - WorkerExitFn exitFn_; + std::vector> workers_; + std::thread monitor_; + std::mutex taskMtx_; std::list taskQ_; - std::mutex mtx_; std::condition_variable cv_; }; -} // namespace UC +} // namespace UC #endif diff --git a/ucm/shared/test/case/infra/thread_pool_test.cc b/ucm/shared/test/case/infra/thread_pool_test.cc new file mode 100644 index 000000000..c3805c5dd --- /dev/null +++ b/ucm/shared/test/case/infra/thread_pool_test.cc @@ -0,0 +1,101 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ + +#include "thread/thread_pool.h" +#include +#include +#include +#include +#include "thread/latch.h" + +class UCThreadPoolTest : public ::testing::Test {}; + +TEST_F(UCThreadPoolTest, TimeoutDetection) +{ + struct TestTask { + int taskId; + std::atomic* finished; + std::atomic* timeout; + }; + + constexpr size_t nWorker = 2; + constexpr size_t timeoutMs = 20; + std::atomic timeoutCount{0}; + std::atomic taskFinished{false}; + std::atomic taskTimeout{false}; + + UC::ThreadPool threadPool; + threadPool.SetNWorker(nWorker) + .SetWorkerFn([](TestTask& task, const auto&) { + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + *(task.finished) = true; + }) + .SetWorkerTimeoutFn( + [&](TestTask& task, const auto) { + timeoutCount++; + task.timeout->store(true); + }, + timeoutMs, 10) + .Run(); + std::list tasks{ + {1, &taskFinished, &taskTimeout} + }; + threadPool.Push(tasks); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + ASSERT_GT(timeoutCount.load(), 0); + ASSERT_TRUE(taskTimeout.load()); +} + +TEST_F(UCThreadPoolTest, SimulatedFileSystemHang) +{ + struct TestTask { + std::atomic* simulatingHang; + }; + + std::atomic hangDetected{0}; + constexpr size_t hangTimeoutMs = 20; + std::atomic taskHang{true}; + + UC::ThreadPool threadPool; + threadPool.SetNWorker(1) + .SetWorkerFn([](TestTask& task, const auto&) { + std::mutex fakeMutex; + std::unique_lock fakelock(fakeMutex); + std::condition_variable fakeCond; + while (*(task.simulatingHang)) { + fakeCond.wait_for(fakelock, std::chrono::milliseconds(10)); // waiting forever + } + }) + .SetWorkerTimeoutFn( + [&](TestTask& task, const auto) { + hangDetected++; + *(task.simulatingHang) = false; // stop simulating hang + }, + hangTimeoutMs, 10) + .Run(); + std::list tasks{{&taskHang}}; + threadPool.Push(tasks); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT_GT(hangDetected.load(), 0); +} \ No newline at end of file diff --git a/ucm/store/pcstore/cc/domain/trans/trans_manager.cc b/ucm/store/pcstore/cc/domain/trans/trans_manager.cc index d2106ab63..aeb30543b 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_manager.cc +++ b/ucm/store/pcstore/cc/domain/trans/trans_manager.cc @@ -39,7 +39,7 @@ Status TransManager::Setup(const size_t rankSize, const int32_t deviceId, const if (s.Failure()) { return s; } } s = this->queue_.Setup(deviceId, streamNumber, blockSize, ioSize, ioDirect, bufferNumber, - layout, &this->failureSet_, scatterGatherEnable); + layout, &this->failureSet_, scatterGatherEnable, timeoutMs); if (s.Failure()) { return s; } this->rankSize_ = rankSize; this->timeoutMs_ = timeoutMs; diff --git a/ucm/store/pcstore/cc/domain/trans/trans_queue.cc b/ucm/store/pcstore/cc/domain/trans/trans_queue.cc index 993b60299..a58a196aa 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_queue.cc +++ b/ucm/store/pcstore/cc/domain/trans/trans_queue.cc @@ -51,10 +51,10 @@ void TransQueue::DeviceWorker(BlockTask&& task) return; } -void TransQueue::FileWorker(BlockTask&& task) +void TransQueue::FileWorker(BlockTask& task) { if (this->failureSet_->Contains(task.owner)) { - task.done(false); + if (task.type != TransTask::Type::DUMP) { task.done(false); } return; } auto hostPtr = (uintptr_t)task.buffer.get(); @@ -75,10 +75,22 @@ void TransQueue::FileWorker(BlockTask&& task) task.done(false); } +void TransQueue::FileWorkerTimeout(BlockTask& task) +{ + static size_t lastTaskId = 0; + if (lastTaskId != task.owner) { + lastTaskId = task.owner; + UC_WARN("Task({}) timeout.", task.owner); + } + + if (task.type != TransTask::Type::DUMP) { this->failureSet_->Insert(task.owner); } + if (task.done) { task.done(false); } +} + Status TransQueue::Setup(const int32_t deviceId, const size_t streamNumber, const size_t blockSize, const size_t ioSize, const bool ioDirect, const size_t bufferNumber, const SpaceLayout* layout, TaskSet* failureSet_, - const bool scatterGatherEnable) + const bool scatterGatherEnable, const size_t timeoutMs) { Trans::Device device; auto ts = device.Setup(deviceId); @@ -112,11 +124,14 @@ Status TransQueue::Setup(const int32_t deviceId, const size_t streamNumber, cons return ts.Success(); }) .SetWorkerFn([this](auto t, auto) { this->DeviceWorker(std::move(t)); }) + .SetNWorker(streamNumber) .Run(); if (!success) { return Status::Error(); } - success = this->filePool_.SetWorkerFn([this](auto t, auto) { this->FileWorker(std::move(t)); }) - .SetNWorker(streamNumber) - .Run(); + success = + this->filePool_.SetWorkerFn([this](auto t, auto) { this->FileWorker(t); }) + .SetWorkerTimeoutFn([this](auto t, auto) { this->FileWorkerTimeout(t); }, timeoutMs) + .SetNWorker(streamNumber) + .Run(); if (!success) { return Status::Error(); } this->layout_ = layout; this->ioSize_ = ioSize; @@ -226,4 +241,4 @@ void TransQueue::DispatchSatterGatherDump(TaskPtr task, WaiterPtr waiter) } } -} // namespace UC +} // namespace UC diff --git a/ucm/store/pcstore/cc/domain/trans/trans_queue.h b/ucm/store/pcstore/cc/domain/trans/trans_queue.h index 377f09d5d..abf6b1f78 100644 --- a/ucm/store/pcstore/cc/domain/trans/trans_queue.h +++ b/ucm/store/pcstore/cc/domain/trans/trans_queue.h @@ -46,12 +46,14 @@ class TransQueue { std::function done; }; void DeviceWorker(BlockTask&& task); - void FileWorker(BlockTask&& task); + void FileWorker(BlockTask& task); + void FileWorkerTimeout(BlockTask& task); public: Status Setup(const int32_t deviceId, const size_t streamNumber, const size_t blockSize, const size_t ioSize, const bool ioDirect, const size_t bufferNumber, - const SpaceLayout* layout, TaskSet* failureSet_, const bool scatterGatherEnable); + const SpaceLayout* layout, TaskSet* failureSet_, const bool scatterGatherEnable, + const size_t timeoutMs); void Dispatch(TaskPtr task, WaiterPtr waiter); void DispatchDump(TaskPtr task, WaiterPtr waiter); void DispatchSatterGatherDump(TaskPtr task, WaiterPtr waiter); @@ -70,6 +72,6 @@ class TransQueue { bool scatterGatherEnable_; }; -} // namespace UC +} // namespace UC #endif From 4e77428343a229711f90a3e06ebd3ff682c070a6 Mon Sep 17 00:00:00 2001 From: qyh111 Date: Sat, 13 Dec 2025 17:56:32 +0800 Subject: [PATCH 08/10] modify factory_v1 (#516) --- ucm/store/factory_v1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ucm/store/factory_v1.py b/ucm/store/factory_v1.py index 689fec7c9..6f1b7431a 100644 --- a/ucm/store/factory_v1.py +++ b/ucm/store/factory_v1.py @@ -58,5 +58,5 @@ def create_connector(cls, connector_name: str, config: dict) -> UcmKVStoreBaseV1 UcmConnectorFactoryV1.register_connector( - "UcmPcStoreV1", "ucm.store.pcstore.pcstore_connector_v1", "UcmPcStoreV1" + "UcmNfsStore", "ucm.store.pcstore.pcstore_connector_v1", "UcmPcStoreV1" ) From 8e4e2c747793dc9540ee3d1b341f716e2c18e64d Mon Sep 17 00:00:00 2001 From: qyh111 Date: Sat, 13 Dec 2025 18:12:03 +0800 Subject: [PATCH 09/10] set version to 0.2.0rc1 (#517) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0f22da523..362da1aa4 100644 --- a/setup.py +++ b/setup.py @@ -102,7 +102,7 @@ def build_cmake(self, ext: CMakeExtension): setup( name="uc-manager", - version="0.1.2", + version="0.2.0rc1", description="Unified Cache Management", author="Unified Cache Team", packages=find_packages(), From 7cd9d89dc67b4299ddf3027dcc4d6bc3417f6ad0 Mon Sep 17 00:00:00 2001 From: "Mag1c.H" Date: Sat, 13 Dec 2025 02:34:47 -0800 Subject: [PATCH 10/10] fix code style --- ucm/store/nfsstore/cc/domain/trans/trans_manager.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ucm/store/nfsstore/cc/domain/trans/trans_manager.h b/ucm/store/nfsstore/cc/domain/trans/trans_manager.h index 6d66ed302..5675c09b1 100644 --- a/ucm/store/nfsstore/cc/domain/trans/trans_manager.h +++ b/ucm/store/nfsstore/cc/domain/trans/trans_manager.h @@ -32,14 +32,15 @@ namespace UC { class TransManager : public TaskManager { public: Status Setup(const int32_t deviceId, const size_t streamNumber, const size_t ioSize, - const size_t bufferNumber, const SpaceLayout* layout, const size_t timeoutMs, bool useDirect = false) + const size_t bufferNumber, const SpaceLayout* layout, const size_t timeoutMs, + bool useDirect = false) { this->timeoutMs_ = timeoutMs; auto status = Status::OK(); for (size_t i = 0; i < streamNumber; i++) { auto q = std::make_shared(); - status = - q->Setup(deviceId, ioSize, bufferNumber, &this->failureSet_, layout, timeoutMs, useDirect); + status = q->Setup(deviceId, ioSize, bufferNumber, &this->failureSet_, layout, timeoutMs, + useDirect); if (status.Failure()) { break; } this->queues_.emplace_back(std::move(q)); } @@ -47,6 +48,6 @@ class TransManager : public TaskManager { } }; -} // namespace UC +} // namespace UC #endif