Skip to content

csapp/cache_sim.c ​

配套源码,运行方法见同目录 README。返回实验总览。

c
/* A small LRU simulator. Cross-block requests are split, unlike Cache Lab's
 * simplifying input assumption. A modify runs the whole load, then the store. */
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { bool valid; uint64_t tag, stamp; } Line;
typedef struct {
    unsigned s, b;
    size_t ways, sets;
    Line *lines;
    uint64_t clock, hits, misses, evictions;
} Cache;

static bool number(const char *text, uint64_t *value) {
    if (!*text || *text == '-') return false;
    char *end;
    errno = 0;
    unsigned long long n = strtoull(text, &end, 10);
    if (errno || *end) return false;
    *value = (uint64_t)n;
    return true;
}

static void access_block(Cache *c, uint64_t address) {
    uint64_t set = (address >> c->b) & (c->sets - 1);
    uint64_t tag = address >> (c->s + c->b);
    Line *lines = c->lines + set * c->ways;
    size_t victim = 0;
    bool found_empty = false;
    ++c->clock;
    for (size_t i = 0; i < c->ways; ++i) {
        if (lines[i].valid && lines[i].tag == tag) {
            ++c->hits;
            lines[i].stamp = c->clock;
            return;
        }
        if (!lines[i].valid) {
            if (!found_empty) victim = i;
            found_empty = true;
        } else if (!found_empty && lines[i].stamp < lines[victim].stamp) {
            victim = i;
        }
    }
    ++c->misses;
    if (lines[victim].valid) ++c->evictions;
    lines[victim] = (Line){true, tag, c->clock};
}

static void access_range(Cache *c, uint64_t address, uint64_t size) {
    uint64_t block = address >> c->b;
    uint64_t last = (address + size - 1) >> c->b;
    for (;;) {
        access_block(c, block << c->b);
        if (block == last) break;
        ++block;
    }
}

int main(int argc, char **argv) {
    uint64_t s, ways, b;
    if (argc != 5 || !number(argv[1], &s) || !number(argv[2], &ways) ||
        !number(argv[3], &b) || s > 20 || b > 63 || s + b > 63 ||
        ways == 0 || ways > (UINT64_C(1) << 22) / (UINT64_C(1) << s)) {
        fprintf(stderr, "usage: %s s E b trace (s<=20, s+b<=63, 1<=E, <=2^22 lines)\n", argv[0]);
        return 2;
    }
    Cache c = {(unsigned)s, (unsigned)b, (size_t)ways, (size_t)1 << s, NULL, 0, 0, 0, 0};
    c.lines = calloc(c.sets * c.ways, sizeof *c.lines);
    if (!c.lines) { perror("calloc"); return 1; }
    FILE *input = fopen(argv[4], "r");
    if (!input) { perror(argv[4]); free(c.lines); return 1; }
    char *line = NULL;
    size_t capacity = 0, lineno = 0;
    int result = 0;
    while (getline(&line, &capacity, input) >= 0) {
        ++lineno;
        char *start = line;
        while (isspace((unsigned char)*start)) ++start;
        if (!*start || *start == '#') continue;
        char op;
        uint64_t address, size;
        int end = 0;
        if (sscanf(start, " %c %" SCNx64 ",%" SCNu64 " %n", &op, &address, &size, &end) != 3 ||
            !end || start[end] || !strchr("ILSM", op) || !size || size > (UINT64_C(1) << 20) ||
            address > UINT64_MAX - (size - 1)) {
            fprintf(stderr, "invalid trace at line %zu\n", lineno);
            result = 2;
            break;
        }
        if (op == 'I') continue;
        access_range(&c, address, size);
        if (op == 'M') access_range(&c, address, size);
    }
    if (ferror(input)) { perror("trace read"); result = 1; }
    if (!result)
        printf("hits:%" PRIu64 " misses:%" PRIu64 " evictions:%" PRIu64 "\n", c.hits, c.misses, c.evictions);
    free(line);
    fclose(input);
    free(c.lines);
    return result;
}