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

如何优化基于FIFO的二级包含式缓存模拟器read_fifo函数?

两级FIFO缓存模拟器优化实现

原实现的核心问题

  1. 未正确利用timeStamp字段实现FIFO逻辑,替换规则错误(简单替换第一个块或通过tag判断替换对象,不符合先进先出)
  2. 违反包含性缓存原则:将L1的块替换到L2,导致L1数据不再是L2的子集,违背设计要求
  3. 缺少有效位(Valid Bit):初始状态下缓存块tag可能为0,会出现误命中
  4. 代码冗余:L1/L2查找逻辑重复,存在不必要的内存拷贝操作

优化后的完整实现代码

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

// 全局参数定义
#define DRAM_SIZE 1048576
#define BLOCK_SIZE 16                  // 单缓存块大小:16字节
#define BLOCK_OFFSET_BITS 4            // 块偏移位宽:2^4=16

// L1缓存参数:64字节,2路组相连
#define L1_TOTAL_SIZE 64
#define L1_WAYS 2
#define L1_SETS (L1_TOTAL_SIZE / (L1_WAYS * BLOCK_SIZE))  // 组数:64/(2*16)=2
#define L1_SET_BITS 1                  // 组索引位宽:2^1=2组
#define L1_TAG_BITS (32 - BLOCK_OFFSET_BITS - L1_SET_BITS)

// L2缓存参数:256字节,4路组相连
#define L2_TOTAL_SIZE 256
#define L2_WAYS 4
#define L2_SETS (L2_TOTAL_SIZE / (L2_WAYS * BLOCK_SIZE))  // 组数:256/(4*16)=4
#define L2_SET_BITS 2                  // 组索引位宽:2^2=4组
#define L2_TAG_BITS (32 - BLOCK_OFFSET_BITS - L2_SET_BITS)

// 缓存块结构体:添加有效位,完善FIFO所需的时间戳
typedef struct cb_struct {
    unsigned char data[BLOCK_SIZE];
    uint32_t tag;
    uint32_t timeStamp;  // 记录块加载的周期数,用于FIFO替换
    uint8_t valid;       // 有效位:1=块有效,0=块无效
} cacheBlock;

// 缓存访问请求结构体
typedef struct access {
    int readWrite;  // 0=读操作,1=写操作
    uint32_t address;
    uint32_t data;  // 读操作时此值为0
} cacheAccess;

// 全局资源
unsigned char *DRAM;
cacheBlock L1_cache[L1_SETS][L1_WAYS];
cacheBlock L2_cache[L2_SETS][L2_WAYS];
FILE *trace;
long cycles;  // 全局周期计数器,每次访问递增

// 地址解析宏:避免硬编码,提升可读性
#define GET_BLOCK_OFFSET(addr) ((addr) & ((1 << BLOCK_OFFSET_BITS) - 1))
#define GET_L1_SET(addr) (((addr) >> BLOCK_OFFSET_BITS) & ((1 << L1_SET_BITS) - 1))
#define GET_L1_TAG(addr) ((addr) >> (BLOCK_OFFSET_BITS + L1_SET_BITS))
#define GET_L2_SET(addr) (((addr) >> BLOCK_OFFSET_BITS) & ((1 << L2_SET_BITS) - 1))
#define GET_L2_TAG(addr) ((addr) >> (BLOCK_OFFSET_BITS + L2_SET_BITS))

// 在指定缓存组中查找tag对应的块,找到返回索引,未找到返回-1
int cache_lookup(cacheBlock set[], int ways, uint32_t tag) {
    for (int i = 0; i < ways; i++) {
        if (set[i].valid && set[i].tag == tag) {
            return i;
        }
    }
    return -1;
}

// 找到组中需要替换的FIFO块(时间戳最小的最老块)
int fifo_victim(cacheBlock set[], int ways) {
    int victim_idx = 0;
    uint32_t oldest_time = set[0].timeStamp;
    for (int i = 1; i < ways; i++) {
        if (set[i].timeStamp < oldest_time) {
            oldest_time = set[i].timeStamp;
            victim_idx = i;
        }
    }
    return victim_idx;
}

// 初始化DRAM:分配内存并填充测试数据
void init_DRAM() {
    DRAM = (unsigned char*)malloc(DRAM_SIZE);
    if (!DRAM) {
        perror("Failed to allocate DRAM");
        exit(EXIT_FAILURE);
    }
    // 填充递增的测试数据,方便验证
    for (int i = 0; i < DRAM_SIZE; i++) {
        DRAM[i] = i & 0xFF;
    }
}

// 初始化L1/L2缓存:重置有效位、tag、时间戳
void init_caches() {
    // 初始化L1
    for (int s = 0; s < L1_SETS; s++) {
        for (int w = 0; w < L1_WAYS; w++) {
            L1_cache[s][w].valid = 0;
            L1_cache[s][w].tag = 0;
            L1_cache[s][w].timeStamp = 0;
            memset(L1_cache[s][w].data, 0, BLOCK_SIZE);
        }
    }
    // 初始化L2
    for (int s = 0; s < L2_SETS; s++) {
        for (int w = 0; w < L2_WAYS; w++) {
            L2_cache[s][w].valid = 0;
            L2_cache[s][w].tag = 0;
            L2_cache[s][w].timeStamp = 0;
            memset(L2_cache[s][w].data, 0, BLOCK_SIZE);
        }
    }
    cycles = 0;
}

// 核心FIFO读操作函数,严格遵守包含性缓存规则
uint32_t read_fifo(uint32_t address) {
    cycles++;  // 每次访问递增周期计数器
    uint8_t offset = GET_BLOCK_OFFSET(address);
    uint32_t l1_set = GET_L1_SET(address);
    uint32_t l1_tag = GET_L1_TAG(address);
    uint32_t l2_set = GET_L2_SET(address);
    uint32_t l2_tag = GET_L2_TAG(address);

    // 第一步:查找L1缓存
    int l1_idx = cache_lookup(L1_cache[l1_set], L1_WAYS, l1_tag);
    if (l1_idx != -1) {
        // L1命中,直接返回对应字节
        return L1_cache[l1_set][l1_idx].data[offset];
    }

    // 第二步:查找L2缓存
    int l2_idx = cache_lookup(L2_cache[l2_set], L2_WAYS, l2_tag);
    if (l2_idx != -1) {
        // L2命中,将块加载到L1(L2块保留,遵守包含性)
        int l1_victim = fifo_victim(L1_cache[l1_set], L1_WAYS);
        // 更新L1受害者块
        memcpy(L1_cache[l1_set][l1_victim].data, L2_cache[l2_set][l2_idx].data, BLOCK_SIZE);
        L1_cache[l1_set][l1_victim].tag = l1_tag;
        L1_cache[l1_set][l1_victim].valid = 1;
        L1_cache[l1_set][l1_victim].timeStamp = cycles;
        return L1_cache[l1_set][l1_victim].data[offset];
    }

    // 第三步:DRAM命中,先加载到L2,再加载到L1
    unsigned char *dram_block = DRAM + (address & ~((1 << BLOCK_OFFSET_BITS) - 1));
    // 更新L2受害者块
    int l2_victim = fifo_victim(L2_cache[l2_set], L2_WAYS);
    memcpy(L2_cache[l2_set][l2_victim].data, dram_block, BLOCK_SIZE);
    L2_cache[l2_set][l2_victim].tag = l2_tag;
    L2_cache[l2_set][l2_victim].valid = 1;
    L2_cache[l2_set][l2_victim].timeStamp = cycles;
    // 更新L1受害者块
    int l1_victim = fifo_victim(L1_cache[l1_set], L1_WAYS);
    memcpy(L1_cache[l1_set][l1_victim].data, L2_cache[l2_set][l2_victim].data, BLOCK_SIZE);
    L1_cache[l1_set][l1_victim].tag = l1_tag;
    L1_cache[l1_set][l1_victim].valid = 1;
    L1_cache[l1_set][l1_victim].timeStamp = cycles;

    return L1_cache[l1_set][l1_victim].data[offset];
}

// 打印L1缓存内容(符合要求的格式)
void print_L1_cache() {
    printf("L1 Cache Contents:\n");
    for (int s = 0; s < L1_SETS; s++) {
        printf("Set %d   : ", s);
        for (int w = 0; w < L1_WAYS; w++) {
            if (L1_cache[s][w].valid) {
                printf("Tag=0x%08X | ", L1_cache[s][w].tag);
            } else {
                printf("Invalid | ");
            }
        }
        printf("\n");
    }
}

// 打印L2缓存内容(符合要求的格式)
void print_L2_cache() {
    printf("L2 Cache Contents:\n");
    for (int s = 0; s < L2_SETS; s++) {
        printf("Set %d   : ", s);
        for (int w = 0; w < L2_WAYS; w++) {
            if (L2_cache[s][w].valid) {
                printf("Tag=0x%08X | ", L2_cache[s][w].tag);
            } else {
                printf("Invalid | ");
            }
        }
        printf("\n");
    }
}

优化点详细解释

1. 参数结构化定义

用宏定义缓存的所有核心参数(大小、路数、组数、地址位宽),避免硬编码。修改缓存配置时只需修改对应宏,其他计算自动更新,大幅提升可维护性。

2. 完善缓存块结构体

添加valid有效位:解决初始状态下tag为0导致的误命中问题,只有有效位为1且tag匹配的块才被视为命中。

3. 正确实现FIFO替换

利用全局cycles计数器记录每个块的加载时间,替换时选择时间戳最小的最老块,严格遵循先进先出规则,替换逻辑清晰可验证。

4. 严格遵守包含性缓存规则

包含性要求L1中的所有数据必须同时存在于L2中:

  • L1命中时,L2必然包含该块(加载L1时已确保L2存在对应块)
  • L2命中加载到L1时,L2块保持不变,仅更新L1
  • 从DRAM加载时,先更新L2再更新L1,确保L2包含所有L1数据

5. 代码复用与抽象

将缓存查找、FIFO受害者选择逻辑抽象为通用函数,避免重复代码,减少出错概率,同时提升代码可读性。

6. 修复冗余操作

移除原代码中重复的内存拷贝操作,仅在必要时执行一次拷贝,提升模拟效率。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:24:58