| 1260 | } |
| 1261 | |
| 1262 | bool Llm::setPrefixCacheFile(const std::string& filename, int flag) { |
| 1263 | // Prefix cache requires kvcache_mmap. Without it mKVCacheDir is unset and |
| 1264 | // the disk-backed alloc in CPUKVCacheManager::onAlloc crashes with a |
| 1265 | // nullptr mmap. Reject up-front instead of crashing deep in the backend. |
| 1266 | if (!mConfig->kvcache_mmap()) { |
| 1267 | MNN_ERROR( |
| 1268 | "setPrefixCacheFile requires kvcache_mmap=true in config. " |
| 1269 | "Prefix cache is a disk-backed feature; please enable kvcache_mmap.\n"); |
| 1270 | return false; |
| 1271 | } |
| 1272 | |
| 1273 | mPrefixCacheFileName = filename; |
| 1274 | mCallIndex = 0; |
| 1275 | mPrefixCacheMode = true; |
| 1276 | |
| 1277 | |
| 1278 | mIsPrefixFileExist = true; |
| 1279 | // check kvcache, validate file existence |
| 1280 | for(int i = 0; i < mConfig->layer_nums(); i++) { |
| 1281 | auto k_file = MNNFilePathConcat(mConfig->prefix_cache_path(), mPrefixCacheFileName) + "_" + std::to_string(i) + "_sync.k"; |
| 1282 | if(!MNNFileExist(k_file.c_str())) { |
| 1283 | mIsPrefixFileExist = false; |
| 1284 | break; |
| 1285 | } |
| 1286 | auto v_file = MNNFilePathConcat(mConfig->prefix_cache_path(), mPrefixCacheFileName) + "_" + std::to_string(i) + "_sync.v"; |
| 1287 | if(!MNNFileExist(v_file.c_str())) { |
| 1288 | mIsPrefixFileExist = false; |
| 1289 | break; |
| 1290 | } |
| 1291 | } |
| 1292 | |
| 1293 | // Further check: the real .k/.v data files must exist, be non-empty, and have consistent |
| 1294 | // sizes across layers. This guards against cases where _sync markers remain but the |
| 1295 | // actual data files were deleted / truncated / partially written, which would otherwise |
| 1296 | // lead to a crash in CPUKVCacheManager::onAlloc (e.g. memset on a null mmap address). |
| 1297 | if (mIsPrefixFileExist) { |
| 1298 | // Only "mix" (LA + regular attention) has intrinsically non-uniform |
| 1299 | // per-layer KV sizes. Everything else (full / sliding / unset → "full") |
| 1300 | // stays under the cross-layer uniformity check that catches partial or |
| 1301 | // corrupted writes. |
| 1302 | const bool isHybrid = (mConfig->attention_type() == "mix"); |
| 1303 | size_t refKeySize = 0; |
| 1304 | size_t refValueSize = 0; |
| 1305 | for (int i = 0; i < mConfig->layer_nums(); i++) { |
| 1306 | auto base = MNNFilePathConcat(mConfig->prefix_cache_path(), mPrefixCacheFileName) + "_" + std::to_string(i); |
| 1307 | auto k_data = base + ".k"; |
| 1308 | auto v_data = base + ".v"; |
| 1309 | if (!MNNFileExist(k_data.c_str()) || !MNNFileExist(v_data.c_str())) { |
| 1310 | MNN_PRINT("Prefix cache data file missing: %s or %s\n", k_data.c_str(), v_data.c_str()); |
| 1311 | mIsPrefixFileExist = false; |
| 1312 | break; |
| 1313 | } |
| 1314 | auto kfd = MNNOpenFile(k_data.c_str(), MNN_FILE_READ); |
| 1315 | auto vfd = MNNOpenFile(v_data.c_str(), MNN_FILE_READ); |
| 1316 | size_t kSize = (kfd == INVALID_FILE) ? INVALID_SIZE : MNNGetFileSize(kfd); |
| 1317 | size_t vSize = (vfd == INVALID_FILE) ? INVALID_SIZE : MNNGetFileSize(vfd); |
| 1318 | if (kfd != INVALID_FILE) |
| 1319 | MNNCloseFile(kfd); |
no test coverage detected