|
6 | 6 | #include <vector> |
7 | 7 | #include <random> |
8 | 8 |
|
9 | | -// --- New Workload Simulations --- |
10 | | - |
11 | | -// Workload where recent items are accessed frequently (good for LRU) |
12 | | -void simulateRecencyWorkload(CacheManager& manager, int items, int accesses) { |
13 | | - std::cout << "\n>>> Starting Recency-Biased Workload (LRU should be better) <<<\n" << std::endl; |
14 | | - for (int i = 0; i < accesses; ++i) { |
15 | | - int key = i % items; |
16 | | - manager.put(key, key * 10); |
17 | | - manager.get(key); |
18 | | - std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
19 | | - } |
20 | | -} |
21 | | - |
22 | | -// Workload where a few items are accessed very frequently (good for LFU) |
23 | | -void simulateFrequencyWorkload(CacheManager& manager, int capacity, int accesses) { |
24 | | - std::cout << "\n>>> Starting Frequency-Biased Workload (LFU should be better) <<<\n" << std::endl; |
25 | | - std::random_device rd; |
26 | | - std::mt19937 gen(rd()); |
27 | | - // Popular keys are in the range [0, capacity/2) |
28 | | - // Unpopular keys are in the range [capacity, capacity*5) |
29 | | - std::uniform_int_distribution<> popular_dist(0, capacity / 2); |
30 | | - std::uniform_int_distribution<> unpopular_dist(capacity, capacity * 5); |
31 | | - |
32 | | - for (int i = 0; i < accesses; ++i) { |
33 | | - int key; |
34 | | - // 80% of accesses go to popular keys |
35 | | - if (i % 5 < 4) { |
36 | | - key = popular_dist(gen); |
37 | | - } else { |
38 | | - key = unpopular_dist(gen); |
39 | | - } |
40 | | - manager.put(key, key * 10); |
41 | | - manager.get(key); |
42 | | - std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
43 | | - } |
44 | | -} |
45 | | - |
46 | | - |
47 | 9 | int main(int argc, char* argv[]) { |
48 | 10 | if (argc < 2) { |
49 | 11 | std::cerr << "Usage: " << argv[0] << " <cache_capacity>" << std::endl; |
50 | 12 | return 1; |
51 | 13 | } |
52 | 14 | size_t capacity = std::stoull(argv[1]); |
| 15 | + if (capacity == 0) { |
| 16 | + std::cerr << "Capacity must be greater than 0." << std::endl; |
| 17 | + return 1; |
| 18 | + } |
53 | 19 |
|
54 | 20 | CacheManager manager(capacity); |
55 | 21 | std::atomic<bool> stop_flag(false); |
56 | 22 |
|
| 23 | + // Start the background thread that will check and switch policies |
57 | 24 | std::thread policyManagerThread(&CacheManager::switchPolicy, &manager, std::ref(stop_flag)); |
58 | 25 |
|
59 | | - // Run workload that favors LRU |
60 | | - simulateRecencyWorkload(manager, capacity + 5, 200); |
| 26 | + std::cout << "--- Starting Simulation ---" << std::endl; |
| 27 | + std::cout << "Initial Policy: " << manager.getCurrentPolicyName() << std::endl; |
| 28 | + std::cout << "---------------------------\n" << std::endl; |
61 | 29 |
|
62 | | - // Give time for policy check |
| 30 | + // ================================================================= |
| 31 | + // PHASE 1: LRU-biased Workload |
| 32 | + // Access a sequence of unique items. LRU excels because it remembers recent items. |
| 33 | + // LFU will struggle as every item has a frequency of 1. |
| 34 | + // ================================================================= |
| 35 | + std::cout << ">>> PHASE 1: Running LRU-Biased Workload (Sequential Access)... <<<\n" << std::endl; |
| 36 | + // Load more items than the cache can hold to force evictions |
| 37 | + for (int i = 0; i < capacity + 5; ++i) { |
| 38 | + manager.put(i, i * 10); |
| 39 | + } |
| 40 | + // Now, access the MOST RECENT items. LRU should have kept them. |
| 41 | + for (int i = 5; i < capacity + 5; ++i) { |
| 42 | + manager.get(i); |
| 43 | + } |
| 44 | + |
| 45 | + // Wait for the policy manager to run and evaluate |
63 | 46 | std::this_thread::sleep_for(std::chrono::seconds(6)); |
64 | 47 |
|
65 | | - // Run workload that favors LFU |
66 | | - simulateFrequencyWorkload(manager, capacity, 200); |
67 | 48 |
|
68 | | - // Let it run a bit longer to see the final switch |
69 | | - std::this_thread::sleep_for(std::chrono::seconds(6)); |
| 49 | + // ================================================================= |
| 50 | + // PHASE 2: LFU-biased Workload |
| 51 | + // Access a few "popular" items many times, and some "unpopular" items once. |
| 52 | + // LFU will keep the popular items. LRU will get tricked into evicting |
| 53 | + // them when an unpopular item is accessed more recently. |
| 54 | + // ================================================================= |
| 55 | + std::cout << "\n>>> PHASE 2: Running LFU-Biased Workload (Popular Items Access)... <<<\n" << std::endl; |
| 56 | + std::random_device rd; |
| 57 | + std::mt19937 gen(rd()); |
| 58 | + std::uniform_int_distribution<> unpopular_dist(capacity * 2, capacity * 10); |
| 59 | + |
| 60 | + // Access 3 "popular" keys 20 times each |
| 61 | + for (int i = 0; i < 20; ++i) { |
| 62 | + manager.get(0); |
| 63 | + manager.get(1); |
| 64 | + manager.get(2); |
| 65 | + // Occasionally access a new, "unpopular" item |
| 66 | + if (i % 4 == 0) { |
| 67 | + manager.get(unpopular_dist(gen)); |
| 68 | + } |
| 69 | + } |
70 | 70 |
|
| 71 | + // Wait for the policy manager to run and evaluate again |
| 72 | + std::this_thread::sleep_for(std::chrono::seconds(6)); |
| 73 | + |
| 74 | + // --- Shutdown --- |
| 75 | + std::cout << "\n--- Simulation Complete ---\n" << std::endl; |
71 | 76 | stop_flag.store(true); |
72 | 77 | policyManagerThread.join(); |
73 | 78 |
|
|
0 commit comments