已开启
【WIP】LRU使能shard模式+新增meta_service_benchmark #401
chenxin创建于 7月23日
【WIP】LRU使能shard模式+新增meta_service_benchmark #401
已开启
chenxin创建于 7月23日
共 8 个文件变更+1406-11
@@ -247,6 +247,7 @@ add_subdirectory(src)
247 247 
248if (BUILD_CPP_EXAMPLE)248if (BUILD_CPP_EXAMPLE)
249 add_subdirectory(example/cpp)249 add_subdirectory(example/cpp)
250+ add_subdirectory(example/meta_service_bench)
250endif ()251endif ()
251 252 
252if (BUILD_TESTS STREQUAL "ON")253if (BUILD_TESTS STREQUAL "ON")
@@ -0,0 +1,105 @@
1+if (("${BUILD_CPP_EXAMPLE}" STREQUAL "") OR ("${BUILD_CPP_EXAMPLE}" STREQUAL "OFF"))
2+ message(STATUS "BUILD_CPP_EXAMPLE is OFF (bench_meta_service standalone mode).")
3+ add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)
4+ cmake_minimum_required(VERSION 3.12.0)
5+ project(bench_meta_service VERSION 1.0 LANGUAGES CXX C)
6+ set(CMAKE_CXX_STANDARD 17)
7+ set(CMAKE_SKIP_RPATH TRUE)
8+ 
9+ set(MF_PATH $ENV{MEMFABRIC_HYBRID_HOME_PATH})
10+ if ("${MF_PATH}" STREQUAL "")
11+ message(FATAL_ERROR "MEMFABRIC_HYBRID_HOME_PATH not set. Run: source /usr/local/memfabric_hybrid/set_env.sh")
12+ endif ()
13+ set(MCC_PATH $ENV{MEMCACHE_HYBRID_HOME_PATH})
14+ if ("${MCC_PATH}" STREQUAL "")
15+ message(FATAL_ERROR "MEMCACHE_HYBRID_HOME_PATH not set. Point to memcache source root.")
16+ endif ()
17+ 
18+ file(GLOB_RECURSE ALL_HEADERS
19+ "${CMAKE_CURRENT_SOURCE_DIR}/*.h"
20+ "${MF_PATH}/*.h"
21+ "${MCC_PATH}/*.h"
22+ )
23+ set(INCLUDE_DIRS "")
24+ foreach (header ${ALL_HEADERS})
25+ get_filename_component(HEADER_DIR ${header} PATH)
26+ list(APPEND INCLUDE_DIRS ${HEADER_DIR})
27+ endforeach ()
28+ list(REMOVE_DUPLICATES INCLUDE_DIRS)
29+ list(FILTER INCLUDE_DIRS EXCLUDE REGEX ".*/mockcpp/.*")
30+ list(FILTER INCLUDE_DIRS EXCLUDE REGEX ".*/build/3rdparty/.*")
31+ include_directories(${INCLUDE_DIRS})
32+ 
33+ include_directories(
34+ ${MCC_PATH}/3rdparty/nlohmann/json/single_include
35+ ${MCC_PATH}/3rdparty/prometheus-cpp-lite/simpleapi/include
36+ ${MCC_PATH}/3rdparty/prometheus-cpp-lite/core/include
37+ ${MCC_PATH}/3rdparty/log/spdlog/include
38+ ${MCC_PATH}/build/3rdparty/spdlog/include)
39+ 
40+ file(GLOB_RECURSE ALL_SO
41+ "${MF_PATH}/*.so"
42+ "${MCC_PATH}/*.so"
43+ "/usr/local/memcache_hybrid/*.so"
44+ "/usr/local/memcache_hybrid/*/*.so"
45+ )
46+ add_executable(bench_meta_service bench_meta_service.cpp)
47+ foreach (SO ${ALL_SO})
48+ get_filename_component(FILENAME ${SO} NAME)
49+ if ("${FILENAME}" MATCHES "^libmf_memcache")
50+ get_filename_component(FILEDIR ${SO} DIRECTORY)
51+ target_link_directories(bench_meta_service PUBLIC ${FILEDIR})
52+ target_link_libraries(bench_meta_service PUBLIC ${FILENAME})
53+ break()
54+ endif ()
55+ endforeach ()
56+ find_package(Python3 REQUIRED COMPONENTS Development)
57+ target_link_libraries(bench_meta_service PUBLIC Python3::Python pthread dl)
58+ target_compile_options(bench_meta_service PRIVATE -W -fpermissive)
59+else ()
60+ message(STATUS "BUILD_CPP_EXAMPLE is ON (bench_meta_service in-tree mode).")
61+ 
62+ add_executable(bench_meta_service bench_meta_service.cpp)
63+ 
64+ target_include_directories(bench_meta_service PRIVATE
65+ ${PROJECT_MMC_SRC_BASE}/include/
66+ ${PROJECT_MMC_SRC_BASE}/include/cpp/
67+ ${PROJECT_MMC_SRC_BASE}/csrc/
68+ ${PROJECT_MMC_SRC_BASE}/csrc/common
69+ ${PROJECT_MMC_SRC_BASE}/csrc/client
70+ ${PROJECT_MMC_SRC_BASE}/csrc/config
71+ ${PROJECT_MMC_SRC_BASE}/csrc/local_service
72+ ${PROJECT_MMC_SRC_BASE}/csrc/entities
73+ ${PROJECT_MMC_SRC_BASE}/csrc/ha
74+ ${PROJECT_MMC_SRC_BASE}/csrc/meta_service
75+ ${PROJECT_MMC_SRC_BASE}/csrc/kv_event
76+ ${PROJECT_MMC_SRC_BASE}/csrc/log
77+ ${PROJECT_MMC_SRC_BASE}/csrc/net
78+ ${PROJECT_MMC_SRC_BASE}/csrc/net/acc_links_impl
79+ ${PROJECT_MMC_SRC_BASE}/csrc/proto
80+ ${PROJECT_MMC_SRC_BASE}/csrc/under_api/mf_smem
81+ ${PROJECT_MMC_SRC_BASE}/csrc/under_api/ubs_io
82+ ${PROJECT_SMEM_SRC_BASE}/include/host
83+ ${PROJECT_SMEM_SRC_BASE}/csrc/common
84+ ${PROJECT_SMEM_SRC_BASE}/csrc/config_store
85+ ${PROJECT_SMEM_SRC_BASE}/csrc/net
86+ ${PROJECT_SMEM_SRC_BASE}/csrc/under_api/hybm_core
87+ ${PROJECT_ACCLINKS_SRC_BASE}/include
88+ ${PROJECT_3RDPARTY_BIN_DIR}/spdlog/include/
89+ ${PROJECT_3RDPARTY_SRC_DIR}/log/spdlog/include
90+ ${PROJECT_3RDPARTY_SRC_DIR}/nlohmann/json/single_include/
91+ ${PROJECT_3RDPARTY_SRC_DIR}/prometheus-cpp-lite/simpleapi/include
92+ ${PROJECT_3RDPARTY_SRC_DIR}/prometheus-cpp-lite/core/include
93+ )
94+ 
95+ target_link_libraries(bench_meta_service PUBLIC mmc_static pthread dl)
96+ target_link_directories(bench_meta_service PUBLIC
97+ ${PROJECT_OUTPUT_PATH}/acc_links/lib64/
98+ ${PROJECT_3RDPARTY_BIN_DIR}/spdlog/lib
99+ ${PROJECT_3RDPARTY_BIN_DIR}/spdlog/lib64)
100+ target_compile_options(bench_meta_service PRIVATE -W -fpermissive)
101+ 
102+ install(TARGETS bench_meta_service
103+ RUNTIME DESTINATION ${TARGET_INSTALL_DIR}/memcache/bin
104+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
105+endif ()
@@ -0,0 +1,246 @@
1+# meta_service 性能基准测试
2+ 
3+## 简介
4+ 
5+本 benchmark 用于测试 **meta_service 元数据处理性能**。它将 meta_service 和模拟 local_service 拆分为多个子进程,支持多进程并发访问 metaService,测试压力下的元数据处理性能。
6+ 
7+不使用真实的 local_service。每个客户端子进程模拟 local_service 行为:直接创建 `MetaNetClient`,注册 mock BM 资源(伪造 GVA 地址和容量),向 meta_service 发送 RPC 请求。所有 BM 数据读写操作均已打桩。
8+ 
9+### 多进程架构
10+ 
11+```
12+┌─────────────────────────────────────────────────────────────┐
13+│ Coordinator (main) │
14+│ 1. fork meta_service 子进程 │
15+│ 2. 等待 meta_service 就绪 │
16+│ 3. fork N 个 client 子进程 │
17+│ 4. 等待所有 client 完成 │
18+│ 5. 从共享内存收集结果 │
19+│ 6. 终止 meta_service,打印聚合报告 │
20+├──────────────┬──────────────┬──────────────┐ │
21+│ meta_service │ client[0] │ client[N-1] │ │
22+│ (子进程) │ (子进程) │ (子进程) │ │
23+│ │ MetaNetClient│ MetaNetClient│ │
24+│ MmcMetaService│ + mock BM │ + mock BM │ │
25+│ (进程内) │ + M 线程 │ + M 线程 │ │
26+└──────────────┴──────────────┴──────────────┘ │
27+└─────────────────────────────────────────────────────────────┘
28+```
29+ 
30+**关键设计:**
31+ 
32+- meta_service 运行在独立子进程中,通过 `fork()` 创建
33+- 每个客户端子进程独立创建 `MetaNetClient`,注册独立 rank 的 mock BM 资源
34+- 通过 `mmap(MAP_SHARED | MAP_ANONYMOUS)` 共享内存收集各进程结果
35+- 不初始化 BM 库,不需要 NPU 硬件
36+- 所有 BM 数据拷贝操作被跳过,仅测量 meta_service 端的元数据处理
37+ 
38+### 测试的操作类型
39+ 
40+| 单个操作 | 批量操作 |
41+|---------|---------|
42+| AllocRequest → AllocResponse | BatchAllocRequest → BatchAllocResponse |
43+| UpdateRequest → Response | BatchGetRequest → BatchAllocResponse |
44+| GetRequest → AllocResponse | BatchUpdateRequest → BatchUpdateResponse |
45+| QueryRequest → QueryResponse | BatchQueryRequest → BatchQueryResponse |
46+| IsExistRequest → IsExistResponse | BatchIsExistRequest → BatchIsExistResponse |
47+| RemoveRequest → Response | BatchRemoveRequest → BatchRemoveResponse |
48+ 
49+## 前置条件
50+ 
51+### 1. 编译安装 memcache
52+ 
53+在 memcache 源码根目录执行:
54+ 
55+```bash
56+bash script/build_and_pack_run.sh --build_mode DEBUG --incremental
57+```
58+ 
59+编译产物在 `output/` 目录下。
60+ 
61+### 2. 安装 memfabric_hybrid run 包
62+ 
63+```bash
64+bash memfabric_hybrid-*.run # 安装 memfabric_hybrid
65+source /usr/local/memfabric_hybrid/set_env.sh
66+```
67+ 
68+## 编译
69+ 
70+### 方式一:源码树内编译(推荐)
71+ 
72+在 memcache 源码根目录,通过 `BUILD_CPP_EXAMPLE=ON` 启用 benchmark 编译:
73+ 
74+```bash
75+# 1. CMake 配置
76+cmake -G Ninja \
77+ -DCMAKE_BUILD_TYPE=DEBUG \
78+ -DBUILD_CPP_EXAMPLE=ON \
79+ -S . -B build/
80+ 
81+# 2. 编译 benchmark 目标
82+ninja -C build/ bench_meta_service
83+```
84+ 
85+编译产物位于 `build/example/meta_service_bench/bench_meta_service`。
86+ 
87+### 方式二:独立编译
88+ 
89+在安装了 memcache 和 memfabric_hybrid 的 run 包后,可在本目录独立编译:
90+ 
91+```bash
92+# 设置环境变量
93+export MEMCACHE_HYBRID_HOME_PATH=/path/to/memcache # 源码根目录
94+source /usr/local/memfabric_hybrid/set_env.sh
95+ 
96+cd example/meta_service_bench
97+mkdir build && cd build
98+cmake ..
99+make -j$(nproc)
100+```
101+ 
102+编译产物为 `example/meta_service_bench/build/bench_meta_service`。
103+ 
104+> **注意:** 独立编译模式链接动态库 `libmf_memcache.so`,需要该库导出内部 C++ 符号。源码树内编译模式链接静态库 `mmc_static`,符号完整可用,推荐使用。
105+ 
106+## 运行
107+ 
108+```bash
109+# 设置动态库搜索路径(源码树内编译时)
110+export LD_LIBRARY_PATH=$(pwd)/build/3rdparty/spdlog/lib:$(pwd)/build/3rdparty/spdlog/lib64:$LD_LIBRARY_PATH
111+ 
112+# 运行
113+./build/example/meta_service_bench/bench_meta_service
114+```
115+ 
116+### 命令行参数
117+ 
118+```text
119+Usage: bench_meta_service [options]
120+Options:
121+ -p <processes> Client process count (default: 4)
122+ -t <threads> Threads per process (default: 4)
123+ -n <ops> Operations per thread (default: 5000)
124+ -k <keyspace> Key space size (default: 20000)
125+ -w <warmup> Warmup key count (default: 5000)
126+ -b <batchsize> Batch size for batch ops (default: 32)
127+ -s <blobsize> Blob size in bytes (default: 32768)
128+ --perf Enable perf profiling of meta_service (default: off)
129+ --perf-freq <hz> perf record sample frequency (default: 99)
130+```
131+ 
132+| 参数 | 说明 | 默认值 |
133+|------|------|--------|
134+| `-p` | 客户端进程数(每个进程独立 rank) | 4 |
135+| `-t` | 每个进程的工作线程数 | 4 |
136+| `-n` | 每个线程执行的操作数 | 5000 |
137+| `-k` | Key 空间大小(所有进程共享同一 key 空间) | 20000 |
138+| `-w` | 预热阶段总 key 数量 | 5000 |
139+| `-b` | 批量操作每批的 key 数量 | 32 |
140+| `-s` | 单个 blob 大小(字节) | 32768 (32KB) |
141+| `--perf` | 对 meta_service 子进程启用 perf 采样(attach 模式),输出固定为 `perf.data` | 关闭 |
142+| `--perf-freq` | perf 采样频率(Hz) | 99 |
143+ 
144+### 运行示例
145+ 
146+```bash
147+# 2 进程 x 2 线程快速验证
148+./build/example/meta_service_bench/bench_meta_service -p 2 -t 2 -n 500 -k 2000 -w 500
149+ 
150+# 8 进程 x 4 线程大规模压测(32 并发)
151+./build/example/meta_service_bench/bench_meta_service -p 8 -t 4 -n 10000 -k 50000 -w 20000
152+ 
153+# 对 meta_service 采集 perf 数据,压测结束后用 perf report 分析
154+./build/example/meta_service_bench/bench_meta_service -p 4 -t 4 -n 5000 --perf --perf-freq 999
155+perf report -i perf.data
156+ 
157+# 查看帮助
158+./build/example/meta_service_bench/bench_meta_service -h
159+```
160+ 
161+## 输出说明
162+ 
163+benchmark 执行分四个阶段:
164+ 
165+1. **启动 meta_service**:进程内启动 `MmcMetaService`,监听 TCP 端口
166+2. **创建模拟 local_service**:创建 `MetaNetClient`,连接到 meta_service
167+3. **注册 mock BM**:发送 `BmRegisterRequest`,注册伪造的 HBM 资源(1GB)
168+4. **预热 + 压测**:先 Alloc+Update 预热 key,再多线程跑混合读写和批量操作
169+ 
170+### 输出示例
171+ 
172+```
173+============================================================
174+ MetaService Multi-Process Benchmark
175+============================================================
176+Configuration:
177+ Processes: 2
178+ Threads/proc: 2
179+ Ops/thread: 200
180+ Total ops: 800
181+ Key space: 1000
182+ Blob size: 32768 bytes
183+ Warmup keys: 200
184+ Mock HBM/proc:1024 MB
185+ Workload: Alloc=10% Update=10% Get=30% Query=20% IsExist=20% Remove=10%
186+ 
187+[1/3] Starting meta_service process...
188+meta_service started (pid=47655)
189+ 
190+[2/3] Forking 2 client processes...
191+ 
192+[3/3] Waiting for 2 clients to finish...
193+ 
194+========== Multi-Process Aggregated Report ==========
195+ 
196+Processes: 2, Threads/proc: 2, Ops/thread: 200, Total ops: 800
197+ 
198++------------------------------------------------------------------------------------------------------------------------+
199+| Op | Success | Fail | Avg(us) | P50(us) | P95(us) | P99(us) | Min(us) | Max(us) |
200++------------------------------------------------------------------------------------------------------------------------+
201+| Alloc | 56 | 0 | 175.46 | 170.00 | 204.00 | 257.00 | 143.00 | 302.00 |
202+| Update | 88 | 0 | 153.48 | 120.00 | 162.50 | 1430.00 | 101.00 | 2671.00 |
203+| Get | 260 | 0 | 147.02 | 125.00 | 169.50 | 230.50 | 100.00 | 2600.00 |
204+| Query | 148 | 0 | 234.93 | 115.00 | 145.00 | 7662.00 | 98.00 | 7662.00 |
205+| IsExist | 170 | 0 | 113.66 | 111.00 | 134.00 | 178.50 | 94.00 | 185.00 |
206+| Remove | 78 | 0 | 320.47 | 122.50 | 185.50 | 7713.50 | 99.00 | 7714.00 |
207++------------------------------------------------------------------------------------------------------------------------+
208+ 
209+Total ops: 800 (success: 800, failure: 0)
210+Max duration (wall): 0.04 sec
211+Throughput: 22317.69 ops/sec
212+ 
213+Per-process summary:
214+ Rank | PID | Ops | Succ | Fail | Duration
215+ 0 | 47733 | 400 | 400 | 0 | 0.04
216+ 1 | 47734 | 400 | 400 | 0 | 0.04
217+ 
218+Benchmark finished.
219+```
220+ 
221+### 指标说明
222+ 
223+| 指标 | 说明 |
224+|------|------|
225+| Success / Fail | RPC 调用成功/失败次数(返回 `MMC_OK` 为成功) |
226+| Avg(us) | 平均延迟(微秒) |
227+| P50(us) | 中位数延迟(50 分位) |
228+| P95(us) | 95 分位延迟 |
229+| P99(us) | 99 分位延迟 |
230+| Min/Max(us) | 最小/最大延迟 |
231+| Throughput | 总吞吐量(ops/sec) |
232+ 
233+### 工作负载分布
234+ 
235+**混合读写(单操作):**
236+ 
237+| 操作 | 占比 | 说明 |
238+|------|------|------|
239+| Alloc | 10% | 向 meta_service 申请内存(分配器管理 + LRU 插入) |
240+| Update | 10% | 更新 blob 状态为已写入 |
241+| Get | 30% | 获取 blob 元数据(触发租约续期) |
242+| Query | 20% | 查询 key 的元数据信息 |
243+| IsExist | 20% | 检查 key 是否存在 |
244+| Remove | 10% | 删除元数据并释放内存(分配器回收 + LRU 擦除) |
245+ 
246+**批量操作:** 每轮依次执行 BatchAlloc / BatchGet / BatchUpdate / BatchQuery / BatchIsExist / BatchRemove,每批 `-b` 个 key。
@@ -305,17 +305,6 @@ private:
305 }305 }
306};306};
307 307 
308-// 显式实例化常用类型
309-template class MmcMetaContainer<std::string, MmcMemObjMetaPtr>;
310- 
311-// 工厂函数实现
312-template<typename Key, typename Value>
313-MmcRef<MmcMetaContainer<Key, Value>>
314-MmcMetaContainer<Key, Value>::Create(std::function<MediaType(const Value &)> GetTypeFunc)
315-{
316- return MmcMakeRef<MmcMetaContainerLRU<Key, Value>>(GetTypeFunc).Get();
317-}
318- 
319} // namespace mmc308} // namespace mmc
320} // namespace ock309} // namespace ock
321 310 
@@ -0,0 +1,136 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
3+ * MemCache_Hybrid is licensed under Mulan PSL v2.
4+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
5+ * You may obtain a copy of Mulan PSL v2 at:
6+ * http://license.coscl.org.cn/MulanPSL2
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
8+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
9+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10+ * See the Mulan PSL v2 for more details.
11+ */
12+ 
13+#ifndef MF_HYBRID_MMC_META_CONTAINER_SHARDED_LRU_H
14+#define MF_HYBRID_MMC_META_CONTAINER_SHARDED_LRU_H
15+ 
16+#include <array>
17+#include <cstdint>
18+#include <functional>
19+#include <map>
20+#include <vector>
21+ 
22+#include "mmc_meta_container_lru.cpp"
23+ 
24+namespace ock {
25+namespace mmc {
26+ 
27+constexpr uint64_t kShardHashBits = 64;
28+constexpr uint64_t kShardSelectBits = 4;
29+constexpr uint64_t kShardCount = 1ULL << kShardSelectBits;
30+constexpr uint64_t kShardSelectShift = kShardHashBits - kShardSelectBits;
31+ 
32+template<typename Key, typename Value>
33+class MmcMetaContainerShardedLRU : public MmcMetaContainer<Key, Value> {
34+public:
35+ explicit MmcMetaContainerShardedLRU(std::function<MediaType(const Value &)> getTypeFunc)
36+ {
37+ for (uint64_t i = 0; i < kShardCount; ++i) {
38+ shards_[i] = std::make_unique<MmcMetaContainerLRU<Key, Value>>(getTypeFunc);
39+ }
40+ }
41+ 
42+ Result Insert(const Key &key, const Value &value) override
43+ {
44+ return ShardOf(key).Insert(key, value);
45+ }
46+ 
47+ Result Get(const Key &key, Value &value) override
48+ {
49+ return ShardOf(key).Get(key, value);
50+ }
51+ 
52+ Result Erase(const Key &key) override
53+ {
54+ return ShardOf(key).Erase(key);
55+ }
56+ 
57+ Result Erase(const Key &key, Value &value) override
58+ {
59+ return ShardOf(key).Erase(key, value);
60+ }
61+ 
62+ Result EraseAll(std::function<void(const Key &, const Value &)> removeFunc) override
63+ {
64+ for (uint64_t i = 0; i < kShardCount; ++i) {
65+ shards_[i]->EraseAll(removeFunc);
66+ }
67+ return MMC_OK;
68+ }
69+ 
70+ void EraseIf(std::function<bool(const Key &, const Value &)> matchFunc) override
71+ {
72+ for (uint64_t i = 0; i < kShardCount; ++i) {
73+ shards_[i]->EraseIf(matchFunc);
74+ }
75+ }
76+ 
77+ void IterateIf(std::function<bool(const Key &, const Value &)> matchFunc,
78+ std::map<Key, Value> &matchedValues) override
79+ {
80+ for (uint64_t i = 0; i < kShardCount; ++i) {
81+ shards_[i]->IterateIf(matchFunc, matchedValues);
82+ }
83+ }
84+ 
85+ void GetAllKeys(std::vector<Key> &keys) override
86+ {
87+ for (uint64_t i = 0; i < kShardCount; ++i) {
88+ shards_[i]->GetAllKeys(keys);
89+ }
90+ }
91+ 
92+ Result Promote(const Key &key) override
93+ {
94+ return ShardOf(key).Promote(key);
95+ }
96+ 
97+ Result InsertLru(const Key &key, MediaType type) override
98+ {
99+ return ShardOf(key).InsertLru(key, type);
100+ }
101+ 
102+ void MultiLevelElimination(const std::vector<std::pair<uint16_t, uint16_t>> &evictWatermarks,
103+ const std::vector<MediaType> &needEvictList,
104+ const std::vector<uint16_t> &nowMemoryThresholds,
105+ std::function<EvictResult(const Key &, const Value &, MediaType)> moveFunc) override
106+ {
107+ for (uint64_t i = 0; i < kShardCount; ++i) {
108+ shards_[i]->MultiLevelElimination(evictWatermarks, needEvictList, nowMemoryThresholds, moveFunc);
109+ }
110+ }
111+ 
112+private:
113+ MmcMetaContainerLRU<Key, Value> &ShardOf(const Key &key)
114+ {
115+ uint64_t hash = static_cast<uint64_t>(std::hash<Key>{}(key));
116+ return *shards_[hash >> kShardSelectShift];
117+ }
118+ 
119+ std::array<std::unique_ptr<MmcMetaContainerLRU<Key, Value>>, kShardCount> shards_;
120+};
121+ 
122+template class MmcMetaContainerShardedLRU<std::string, MmcMemObjMetaPtr>;
123+ 
124+template<typename Key, typename Value>
125+MmcRef<MmcMetaContainer<Key, Value>>
126+MmcMetaContainer<Key, Value>::Create(std::function<MediaType(const Value &)> GetTypeFunc)
127+{
128+ return MmcMakeRef<MmcMetaContainerShardedLRU<Key, Value>>(GetTypeFunc).Get();
129+}
130+ 
131+template class MmcMetaContainer<std::string, MmcMemObjMetaPtr>;
132+ 
133+} // namespace mmc
134+} // namespace ock
135+ 
136+#endif // MF_HYBRID_MMC_META_CONTAINER_SHARDED_LRU_H
@@ -101,6 +101,16 @@ ADD_UNIT_TEST(test_meta_container_lru
101 BUILD_FLAGS -fpermissive --coverage101 BUILD_FLAGS -fpermissive --coverage
102)102)
103 103 
104+file(GLOB_RECURSE meta_container_sharded_lru_src
105+ ${CMAKE_CURRENT_SOURCE_DIR}/meta_container_sharded_lru_test.cpp
106+)
107+ADD_UNIT_TEST(test_meta_container_sharded_lru
108+ FILES ${meta_container_sharded_lru_src} ${TEST_SRC_PATHS}/ut_main.cpp
109+ INCLUDE_PATHS ${TEST_INCLUDE_PATHS}
110+ LINK_LIBRARIES ${TEST_DEPEND_LIBS}
111+ BUILD_FLAGS -fpermissive --coverage
112+)
113+ 
104file(GLOB_RECURSE ubs_io_enabled_src114file(GLOB_RECURSE ubs_io_enabled_src
105 ${CMAKE_CURRENT_SOURCE_DIR}/test_ubs_io_enabled.cpp115 ${CMAKE_CURRENT_SOURCE_DIR}/test_ubs_io_enabled.cpp
106 ${CMAKE_CURRENT_SOURCE_DIR}/mock_smem.cpp116 ${CMAKE_CURRENT_SOURCE_DIR}/mock_smem.cpp
@@ -0,0 +1,126 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
3+ * MemCache_Hybrid is licensed under Mulan PSL v2.
4+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
5+ * You may obtain a copy of Mulan PSL v2 at:
6+ * http://license.coscl.org.cn/MulanPSL2
7+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
8+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
9+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10+ * See the Mulan PSL v2 for more details.
11+ */
12+#include "mmc_meta_container_sharded_lru.cpp"
13+#include "mmc_mem_obj_meta.h"
14+#include "mmc_meta_container.h"
15+#include "gtest/gtest.h"
16+ 
17+#include <map>
18+#include <string>
19+#include <vector>
20+ 
21+using namespace ock::mmc;
22+using namespace testing;
23+ 
24+class TestMmcMetaContainerShardedLRU : public Test {
25+protected:
26+ using Container = MmcMetaContainerShardedLRU<std::string, int>;
27+ std::unique_ptr<Container> container;
28+ 
29+ void SetUp() override
30+ {
31+ auto getTypeFunc = [](const int &value) -> MediaType { return MEDIA_DRAM; };
32+ container = std::make_unique<Container>(getTypeFunc);
33+ }
34+};
35+ 
36+TEST_F(TestMmcMetaContainerShardedLRU, InsertAndGet)
37+{
38+ constexpr int valueFirst = 100;
39+ constexpr int valueSecond = 200;
40+ EXPECT_EQ(container->Insert("key1", valueFirst), MMC_OK);
41+ EXPECT_EQ(container->Insert("key2", valueSecond), MMC_OK);
42+ EXPECT_EQ(container->Insert("key1", valueSecond), MMC_DUPLICATED_OBJECT);
43+ 
44+ int value = 0;
45+ EXPECT_EQ(container->Get("key1", value), MMC_OK);
46+ EXPECT_EQ(value, valueFirst);
47+ EXPECT_EQ(container->Get("key2", value), MMC_OK);
48+ EXPECT_EQ(value, valueSecond);
49+ EXPECT_EQ(container->Get("key3", value), MMC_UNMATCHED_KEY);
50+}
51+ 
52+TEST_F(TestMmcMetaContainerShardedLRU, Erase)
53+{
54+ constexpr int valueFirst = 100;
55+ constexpr int valueSecond = 200;
56+ container->Insert("key1", valueFirst);
57+ container->Insert("key2", valueSecond);
58+ 
59+ EXPECT_EQ(container->Erase("key1"), MMC_OK);
60+ int value = 0;
61+ EXPECT_EQ(container->Get("key1", value), MMC_UNMATCHED_KEY);
62+ EXPECT_EQ(container->Erase("key3"), MMC_UNMATCHED_KEY);
63+ EXPECT_EQ(container->Get("key2", value), MMC_OK);
64+ EXPECT_EQ(value, valueSecond);
65+}
66+ 
67+TEST_F(TestMmcMetaContainerShardedLRU, GetAllKeysAndIterateIf)
68+{
69+ constexpr int keyCount = 50;
70+ constexpr int valueStep = 10;
71+ constexpr int valueThreshold = 200;
72+ for (int i = 0; i < keyCount; ++i) {
73+ container->Insert(std::string("key") + std::to_string(i), i * valueStep);
74+ }
75+ 
76+ std::vector<std::string> keys;
77+ container->GetAllKeys(keys);
78+ EXPECT_EQ(keys.size(), static_cast<size_t>(keyCount));
79+ 
80+ std::map<std::string, int> matched;
81+ container->IterateIf([valueThreshold](const std::string &, const int &value) { return value >= valueThreshold; },
82+ matched);
83+ EXPECT_EQ(matched.size(), static_cast<size_t>(keyCount - valueThreshold / valueStep));
84+}
85+ 
86+TEST_F(TestMmcMetaContainerShardedLRU, EraseIf)
87+{
88+ constexpr int keyCount = 40;
89+ constexpr int eraseBelow = 20;
90+ for (int i = 0; i < keyCount; ++i) {
91+ container->Insert(std::string("k") + std::to_string(i), i);
92+ }
93+ container->EraseIf([eraseBelow](const std::string &, const int &value) { return value < eraseBelow; });
94+ 
95+ std::vector<std::string> keys;
96+ container->GetAllKeys(keys);
97+ EXPECT_EQ(keys.size(), static_cast<size_t>(keyCount - eraseBelow));
98+}
99+ 
100+TEST_F(TestMmcMetaContainerShardedLRU, EraseAll)
101+{
102+ constexpr int keyCount = 30;
103+ for (int i = 0; i < keyCount; ++i) {
104+ container->Insert(std::string("k") + std::to_string(i), i);
105+ }
106+ 
107+ int removedCount = 0;
108+ container->EraseAll([&removedCount](const std::string &, const int &) { removedCount++; });
109+ EXPECT_EQ(removedCount, keyCount);
110+ 
111+ std::vector<std::string> keys;
112+ container->GetAllKeys(keys);
113+ EXPECT_TRUE(keys.empty());
114+}
115+ 
116+TEST_F(TestMmcMetaContainerShardedLRU, FactoryCreateReturnsShardedContainer)
117+{
118+ auto getTypeFunc = [](const int &value) -> MediaType { return MEDIA_DRAM; };
119+ MmcRef<MmcMetaContainer<std::string, int>> ref = MmcMetaContainer<std::string, int>::Create(getTypeFunc);
120+ ASSERT_NE(ref.Get(), nullptr);
121+ constexpr int valueFirst = 100;
122+ EXPECT_EQ(ref->Insert("factoryKey", valueFirst), MMC_OK);
123+ int value = 0;
124+ EXPECT_EQ(ref->Get("factoryKey", value), MMC_OK);
125+ EXPECT_EQ(value, valueFirst);
126+}