Files
KamaCache/testAllCachePolicy.cpp
2025-11-18 23:41:04 +08:00

287 lines
12 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <iostream>
#include <string>
#include <chrono>
#include <vector>
#include <iomanip>
#include <random>
#include <algorithm>
#include <array>
#include "KICachePolicy.h"
#include "KLfuCache.h"
#include "KLruCache.h"
#include "KArcCache/KArcCache.h"
class Timer {
public:
Timer() : start_(std::chrono::high_resolution_clock::now()) {}
double elapsed() {
auto now = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(now - start_).count();
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> start_;
};
// 辅助函数:打印结果
void printResults(const std::string& testName, int capacity,
const std::vector<int>& get_operations,
const std::vector<int>& hits) {
std::cout << "=== " << testName << " 结果汇总 ===" << std::endl;
std::cout << "缓存大小: " << capacity << std::endl;
// 假设对应的算法名称已在测试函数中定义
std::vector<std::string> names;
if (hits.size() == 3) {
names = {"LRU", "LFU", "ARC"};
} else if (hits.size() == 4) {
names = {"LRU", "LFU", "ARC", "LRU-K"};
} else if (hits.size() == 5) {
names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"};
}
for (size_t i = 0; i < hits.size(); ++i) {
double hitRate = 100.0 * hits[i] / get_operations[i];
std::cout << (i < names.size() ? names[i] : "Algorithm " + std::to_string(i+1))
<< " - 命中率: " << std::fixed << std::setprecision(2)
<< hitRate << "% ";
// 添加具体命中次数和总操作次数
std::cout << "(" << hits[i] << "/" << get_operations[i] << ")" << std::endl;
}
std::cout << std::endl; // 添加空行,使输出更清晰
}
void testHotDataAccess() {
std::cout << "\n=== 测试场景1热点数据访问测试 ===" << std::endl;
const int CAPACITY = 20; // 缓存容量
const int OPERATIONS = 500000; // 总操作次数
const int HOT_KEYS = 20; // 热点数据数量
const int COLD_KEYS = 5000; // 冷数据数量
KamaCache::KLruCache<int, std::string> lru(CAPACITY);
KamaCache::KLfuCache<int, std::string> lfu(CAPACITY);
KamaCache::KArcCache<int, std::string> arc(CAPACITY);
// 为LRU-K设置合适的参数
// - 主缓存容量与其他算法相同
// - 历史记录容量设为可能访问的所有键数量
// - k=2表示数据被访问2次后才会进入缓存适合区分热点和冷数据
KamaCache::KLruKCache<int, std::string> lruk(CAPACITY, HOT_KEYS + COLD_KEYS, 2);
KamaCache::KLfuCache<int, std::string> lfuAging(CAPACITY, 20000);
std::random_device rd;
std::mt19937 gen(rd());
// 基类指针指向派生类对象添加LFU-Aging
std::array<KamaCache::KICachePolicy<int, std::string>*, 5> caches = {&lru, &lfu, &arc, &lruk, &lfuAging};
std::vector<int> hits(5, 0);
std::vector<int> get_operations(5, 0);
std::vector<std::string> names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"};
// 为所有的缓存对象进行相同的操作序列测试
for (int i = 0; i < caches.size(); ++i) {
// 先预热缓存,插入一些数据
for (int key = 0; key < HOT_KEYS; ++key) {
std::string value = "value" + std::to_string(key);
caches[i]->put(key, value);
}
// 交替进行put和get操作模拟真实场景
for (int op = 0; op < OPERATIONS; ++op) {
// 大多数缓存系统中读操作比写操作频繁
// 所以设置30%概率进行写操作
bool isPut = (gen() % 100 < 30);
int key;
// 70%概率访问热点数据30%概率访问冷数据
if (gen() % 100 < 70) {
key = gen() % HOT_KEYS; // 热点数据
} else {
key = HOT_KEYS + (gen() % COLD_KEYS); // 冷数据
}
if (isPut) {
// 执行put操作
std::string value = "value" + std::to_string(key) + "_v" + std::to_string(op % 100);
caches[i]->put(key, value);
} else {
// 执行get操作并记录命中情况
std::string result;
get_operations[i]++;
if (caches[i]->get(key, result)) {
hits[i]++;
}
}
}
}
// 打印测试结果
printResults("热点数据访问测试", CAPACITY, get_operations, hits);
}
void testLoopPattern() {
std::cout << "\n=== 测试场景2循环扫描测试 ===" << std::endl;
const int CAPACITY = 50; // 缓存容量
const int LOOP_SIZE = 500; // 循环范围大小
const int OPERATIONS = 200000; // 总操作次数
KamaCache::KLruCache<int, std::string> lru(CAPACITY);
KamaCache::KLfuCache<int, std::string> lfu(CAPACITY);
KamaCache::KArcCache<int, std::string> arc(CAPACITY);
// 为LRU-K设置合适的参数
// - 历史记录容量设为总循环大小的两倍,覆盖范围内和范围外的数据
// - k=2对于循环访问这是一个合理的阈值
KamaCache::KLruKCache<int, std::string> lruk(CAPACITY, LOOP_SIZE * 2, 2);
KamaCache::KLfuCache<int, std::string> lfuAging(CAPACITY, 3000);
std::array<KamaCache::KICachePolicy<int, std::string>*, 5> caches = {&lru, &lfu, &arc, &lruk, &lfuAging};
std::vector<int> hits(5, 0);
std::vector<int> get_operations(5, 0);
std::vector<std::string> names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"};
std::random_device rd;
std::mt19937 gen(rd());
// 为每种缓存算法运行相同的测试
for (int i = 0; i < caches.size(); ++i) {
// 先预热一部分数据只加载20%的数据)
for (int key = 0; key < LOOP_SIZE / 5; ++key) {
std::string value = "loop" + std::to_string(key);
caches[i]->put(key, value);
}
// 设置循环扫描的当前位置
int current_pos = 0;
// 交替进行读写操作,模拟真实场景
for (int op = 0; op < OPERATIONS; ++op) {
// 20%概率是写操作80%概率是读操作
bool isPut = (gen() % 100 < 20);
int key;
// 按照不同模式选择键
if (op % 100 < 60) { // 60%顺序扫描
key = current_pos;
current_pos = (current_pos + 1) % LOOP_SIZE;
} else if (op % 100 < 90) { // 30%随机跳跃
key = gen() % LOOP_SIZE;
} else { // 10%访问范围外数据
key = LOOP_SIZE + (gen() % LOOP_SIZE);
}
if (isPut) {
// 执行put操作更新数据
std::string value = "loop" + std::to_string(key) + "_v" + std::to_string(op % 100);
caches[i]->put(key, value);
} else {
// 执行get操作并记录命中情况
std::string result;
get_operations[i]++;
if (caches[i]->get(key, result)) {
hits[i]++;
}
}
}
}
printResults("循环扫描测试", CAPACITY, get_operations, hits);
}
void testWorkloadShift() {
std::cout << "\n=== 测试场景3工作负载剧烈变化测试 ===" << std::endl;
const int CAPACITY = 30; // 缓存容量
const int OPERATIONS = 80000; // 总操作次数
const int PHASE_LENGTH = OPERATIONS / 5; // 每个阶段的长度
KamaCache::KLruCache<int, std::string> lru(CAPACITY);
KamaCache::KLfuCache<int, std::string> lfu(CAPACITY);
KamaCache::KArcCache<int, std::string> arc(CAPACITY);
KamaCache::KLruKCache<int, std::string> lruk(CAPACITY, 500, 2);
KamaCache::KLfuCache<int, std::string> lfuAging(CAPACITY, 10000);
std::random_device rd;
std::mt19937 gen(rd());
std::array<KamaCache::KICachePolicy<int, std::string>*, 5> caches = {&lru, &lfu, &arc, &lruk, &lfuAging};
std::vector<int> hits(5, 0);
std::vector<int> get_operations(5, 0);
std::vector<std::string> names = {"LRU", "LFU", "ARC", "LRU-K", "LFU-Aging"};
// 为每种缓存算法运行相同的测试
for (int i = 0; i < caches.size(); ++i) {
// 先预热缓存,只插入少量初始数据
for (int key = 0; key < 30; ++key) {
std::string value = "init" + std::to_string(key);
caches[i]->put(key, value);
}
// 进行多阶段测试,每个阶段有不同的访问模式
for (int op = 0; op < OPERATIONS; ++op) {
// 确定当前阶段
int phase = op / PHASE_LENGTH;
// 每个阶段的读写比例不同
int putProbability;
switch (phase) {
case 0: putProbability = 15; break; // 阶段1: 热点访问15%写入更合理
case 1: putProbability = 30; break; // 阶段2: 大范围随机写比例为30%
case 2: putProbability = 10; break; // 阶段3: 顺序扫描10%写入保持不变
case 3: putProbability = 25; break; // 阶段4: 局部性随机微调为25%
case 4: putProbability = 20; break; // 阶段5: 混合访问调整为20%
default: putProbability = 20;
}
// 确定是读还是写操作
bool isPut = (gen() % 100 < putProbability);
// 根据不同阶段选择不同的访问模式生成key - 优化后的访问范围
int key;
if (op < PHASE_LENGTH) { // 阶段1: 热点访问 - 热点数量5使热点更集中
key = gen() % 5;
} else if (op < PHASE_LENGTH * 2) { // 阶段2: 大范围随机 - 范围400更适合30大小的缓存
key = gen() % 400;
} else if (op < PHASE_LENGTH * 3) { // 阶段3: 顺序扫描 - 保持100个键
key = (op - PHASE_LENGTH * 2) % 100;
} else if (op < PHASE_LENGTH * 4) { // 阶段4: 局部性随机 - 优化局部性区域大小
// 产生5个局部区域每个区域大小为15个键与缓存大小20接近但略小
int locality = (op / 800) % 5; // 调整为5个局部区域
key = locality * 15 + (gen() % 15); // 每区域15个键
} else { // 阶段5: 混合访问 - 增加热点访问比例
int r = gen() % 100;
if (r < 40) { // 40%概率访问热点从30%增加)
key = gen() % 5; // 5个热点键
} else if (r < 70) { // 30%概率访问中等范围
key = 5 + (gen() % 45); // 缩小中等范围为50个键
} else { // 30%概率访问大范围从40%减少)
key = 50 + (gen() % 350); // 大范围也相应缩小
}
}
if (isPut) {
// 执行写操作
std::string value = "value" + std::to_string(key) + "_p" + std::to_string(phase);
caches[i]->put(key, value);
} else {
// 执行读操作并记录命中情况
std::string result;
get_operations[i]++;
if (caches[i]->get(key, result)) {
hits[i]++;
}
}
}
}
printResults("工作负载剧烈变化测试", CAPACITY, get_operations, hits);
}
int main() {
testHotDataAccess();
testLoopPattern();
testWorkloadShift();
return 0;
}