已开启
【WIP】LRU使能shard模式+新增meta_service_benchmark #401
chenxin创建于 7月23日
【WIP】LRU使能shard模式+新增meta_service_benchmark #401
已开启
共 8 个文件变更+1406-11
| @@ -247,6 +247,7 @@ add_subdirectory(src) | |||
| 247 | 247 | ||
| 248 | if (BUILD_CPP_EXAMPLE) | 248 | if (BUILD_CPP_EXAMPLE) |
| 249 | add_subdirectory(example/cpp) | 249 | add_subdirectory(example/cpp) |
| 250 | + add_subdirectory(example/meta_service_bench) | ||
| 250 | endif () | 251 | endif () |
| 251 | 252 | ||
| 252 | if (BUILD_TESTS STREQUAL "ON") | 253 | if (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 mmc | 308 | } // namespace mmc |
| 320 | } // namespace ock | 309 | } // 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 --coverage | 101 | 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 | + | ||
| 104 | file(GLOB_RECURSE ubs_io_enabled_src | 114 | file(GLOB_RECURSE ubs_io_enabled_src |
| 105 | ${CMAKE_CURRENT_SOURCE_DIR}/test_ubs_io_enabled.cpp | 115 | ${CMAKE_CURRENT_SOURCE_DIR}/test_ubs_io_enabled.cpp |
| 106 | ${CMAKE_CURRENT_SOURCE_DIR}/mock_smem.cpp | 116 | ${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 | +} | ||