You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Cache模拟器运行异常:命中数过低求排查

Cache模拟器计算逻辑问题排查

我为学习需求用C语言实现了一款Cache模拟器,无需实际存储数据,仅需统计hit(命中)、miss(未命中)和eviction(驱逐)次数。输入参数包括s(组索引位数)、E(每组行数)、b(块偏移位数),输入的trace文件格式为“操作符 地址, 大小”,操作符分为“L”(加载)、“S”(存储)、“M”(加载+存储)。

给定示例:

L 10,1 miss
M 20,1 miss hit
L 22,1 hit
S 18,1 hit
L 110,1 miss eviction
L 210,1 miss eviction
M 12,1 miss eviction hit
hits:4 misses:5 evicts:3

我的模拟器始终输出错误结果,多数情况下命中数远低于预期。请问我的计算逻辑是否存在问题?

附上实现代码:

#include "cache lab.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>

// Cache line structure
typedef struct {
    int valid;   // Valid bit
    int tag;     // Tag field
    int lru;     // LRU counter
    // Additional fields for data, depending on the cache structure
} CacheLine;

// Cache structure
typedef struct {
    int sets;        // Number of sets
    int lines;       // Number of lines per set
    int block_size;  // Block size (in bytes)
    CacheLine *cache; // Cache array
} Cache;

// Initialize the cache
void initCache(Cache *cache) {
    // Allocate memory for the cache array
    cache->cache = (CacheLine *)malloc(cache->sets * cache->lines * sizeof(CacheLine));

    // Initialize cache lines
    for (int i = 0; i < cache->sets * cache->lines; i++) {
        cache->cache[i].valid = 0;
        cache->cache[i].tag = 0;
        cache->cache[i].lru = 0;
    }
}

// Simulate a cache access
void accessCache(Cache *cache, char operation, unsigned address, int size, int *hits, int *misses, int *evicts) {
    int set_index = (address / cache->block_size) % cache->sets;
    int tag = address / (cache->block_size * cache->sets);

    // Check for a cache hit
    for (int i = 0; i < cache->lines; i++) {
        int index = set_index * cache->lines + i;

        if (cache->cache[index].valid && cache->cache[index].tag == tag) {
            // Cache hit
            (*hits)++;
            cache->cache[index].lru = 0;  // Reset LRU counter

            // For modify (M) operations, we count it as a hit and a miss
            if (operation == 'M') {
                (*misses)++;
            }

            return;
        }
    }

    // Cache miss
    (*misses)++;

    // Find an empty line or evict the least recently used line
    int lru_index = set_index * cache->lines;
    for (int i = 1; i < cache->lines; i++) {
        int index = set_index * cache->lines + i;
        if (!cache->cache[index].valid) {
            lru_index = index;
            break;
        } else if (cache->cache[index].lru > cache->cache[lru_index].lru) {
            lru_index = index;
        }
    }

    // Check if eviction is needed
    if (cache->cache[lru_index].valid) {
        (*evicts)++;
    }

    // Update the cache line
    cache->cache[lru_index].valid = 1;
    cache->cache[lru_index].tag = tag;
    cache->cache[lru_index].lru = 0;  // Reset LRU counter

    // Update LRU counters
    for (int i = 0; i < cache->lines; i++) {
        int index = set_index * cache->lines + i;
        if (index != lru_index) {
            cache->cache[index].lru++;
        }
    }
}

int main(int argc, char *argv[]) {
    int opt;
    int s = 0, b = 0, E = 0;
    char *t;

    // Parse command-line options
    while ((opt = getopt(argc, argv, "s:b:E:t:")) != -1) {
        switch (opt) {
            case 's':
                s = atoi(optarg);
                break;
            case 'b':
                b = atoi(optarg);
                break;
            case 'E':
                E = atoi(optarg);
                break;
            case 't':
                t = optarg;
                

                break;
            default:
                fprintf(stderr, "Usage: %s -s <s> -b <b> -E <E> -t <t>\n", argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    FILE *pfile = fopen(t, "r");
    char operation;
    unsigned address;
    int size;

    // Check if required options are provided
    if (s == 0 || b == 0 || E == 0) {
        fprintf(stderr, "Missing required options. Usage: %s -s <s> -b <b> -E <E>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    // Calculate the number of lines in each set (cache associativity)
    int lines_per_set = 1 << E;

    // Cache configuration
    Cache cache;
    cache.sets = 1 << s;
    cache.lines = lines_per_set;
    cache.block_size = 1 << b;

    // Cache statistics
    int hits = 0;
    int misses = 0;
    int evicts = 0;

    // Initialize the cache
    initCache(&cache);

    // Simulate cache accesses based on the trace file
    while (fscanf(pfile, " %c %x, %d", &operation, &address, &size) > 0) {
        accessCache(&cache, operation, address, size, &hits, &misses, &evicts);
    }
    fclose(pfile);

    // Output cache statistics
    printSummary(hits, misses, evicts);

    // Free allocated memory
    free(cache.cache);

    return 0;
}

核心问题分析

你的代码存在几个关键逻辑错误,直接导致命中数统计异常:

1. M操作统计逻辑完全错误

M操作代表加载+存储,正确规则是:

  • 若加载命中,存储必然命中,应统计为2次hit
  • 若加载miss,加载触发1次miss,后续存储命中,应统计为1次miss + 1次hit

但你的代码在hit分支中对M操作执行(*misses)++,把本该统计为hit的操作算成miss,直接拉低命中数。

2. 组内行数计算错误

E参数本身就是每组行数,你却用int lines_per_set = 1 << E;左移计算,导致每组行数翻倍,缓存结构完全错误,大量命中被误判为miss。正确写法应为int lines_per_set = E;。

3. Hit后的LRU更新不完整

发生hit时,你仅重置当前行的LRU为0,但未对同组内其他有效行的LRU进行递增,导致LRU顺序混乱,后续替换时错误选择非最久未使用的行,引发不必要的eviction和miss。

4. LRU更新未过滤无效行

在miss后的LRU更新中,你对同组所有行(包括未使用的无效行)递增LRU,这会导致无效行的LRU值异常,干扰后续空行判断和替换逻辑。


修正后的关键代码片段

修正M操作统计与Hit后的LRU更新

// Check for a cache hit
for (int i = 0; i < cache->lines; i++) {
    int index = set_index * cache->lines + i;

    if (cache->cache[index].valid && cache->cache[index].tag == tag) {
        // Cache hit
        (*hits)++;
        // M操作额外统计一次hit
        if (operation == 'M') {
            (*hits)++;
        }
        // 更新LRU:重置当前行,其他有效行递增
        cache->cache[index].lru = 0;
        for (int j = 0; j < cache->lines; j++) {
            int j_index = set_index * cache->lines + j;
            if (j_index != index && cache->cache[j_index].valid) {
                cache->cache[j_index].lru++;
            }
        }
        return;
    }
}

修正每组行数计算

// 原错误代码
// int lines_per_set = 1 << E;
// 修正后
int lines_per_set = E;

修正Miss后的LRU更新逻辑

// Update LRU counters
for (int i = 0; i < cache->lines; i++) {
    int index = set_index * cache->lines + i;
    if (index != lru_index && cache->cache[index].valid) {
        cache->cache[index].lru++;
    }
}

内容的提问来源于stack exchange,提问作者Alabama92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 17:30:01