{
  "id": "P074",
  "slug": "cache-simulator",
  "key": "P074-cache-simulator",
  "title": "Cache simulator",
  "summary": "One trace of requests through a small cache under LRU, FIFO, LFU and Belady's optimal rule: hits side by side, one rule step by step with every eviction, and hits as the cache grows - where FIFO can do worse with more room.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P074 - Cache simulator\n\nOne trace of requests through a small cache, under four rules for what to\nthrow out when it is full. The hits are shown side by side, one rule step\nby step with every eviction, and the hits as the cache grows. It starts\nwith the trace 1 2 3 4 1 2 5 1 2 3 4 5, where FIFO does worse with 4\nplaces than with 3.\n\n- `main.eml` - the menu, reading traces and sizes, and the tables\n- `caches.eml` - the four rules, the simulation, and hits by cache size\n\nHow each part works:\n\n- The four rules, as in the corpus case `cache-eviction-policy`:\n  - LRU throws out the key used longest ago;\n  - FIFO throws out the key that came in first;\n  - LFU throws out the key used least often since it came in, and of\n    those the one used longest ago;\n  - OPT throws out the key whose next request is furthest away, or that\n    is never asked for again (Belady, 1966). It needs to know the future,\n    so no real cache can follow it. But no rule can get more hits, which\n    makes it the fair yardstick.\n- As the cache grows, LRU and OPT never lose a hit: what a smaller cache\n  holds, a bigger one under the same rule holds too. FIFO has no such\n  guarantee, and on the starting trace it gets 3 hits with 3 places and\n  2 with 4 - Belady's anomaly.\n- None of the rules is best everywhere. On a loop slightly longer than\n  the cache, LRU throws out exactly the key it needs next and hits\n  nothing.\n\nWhat is checked: menu choices 1 to 5; a cache of 1 to 8 places; the rule\nas LRU, FIFO, LFU or OPT; a trace of 1 to 40 names, each 1 to 6 letters or\ndigits, separated by spaces. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the four rules with 3 places: OPT 5 hits, FIFO 3, LRU and LFU 2;\n- hits for sizes 1 to 6, with FIFO's anomaly at 4;\n- FIFO and OPT step by step with 3 places;\n- the trace A B C A B D A B C D E A with 2 places: no rule but OPT gets a\n  hit.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- cache sizes 0, 9 and abc, then a cancel;\n- the rules lru and MRU, then a cancelled size;\n- traces with an 11-letter name, with a comma, and with 41 requests, then a\n  cancel.\n\nBuilt on the verified corpus case `cache-eviction-policy` (LRU, LFU and\nFIFO on one trace, against the optimal count that looks at the future).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P074 cache simulator: a trace of requests through a small cache under\n# LRU, FIFO, LFU and Belady's optimal rule - hits side by side, one rule\n# step by step, and hits as the cache grows, where FIFO can do worse with\n# more room.\nimport caches\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef number(s):\n    if len(s) == 0 or len(s) > 2:\n        return -1\n    0 => n\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef pad(s, n):\n    while len(s) < n:\n        s + \" \" => s\n    return s\n\ndef lpad(s, n):\n    while len(s) < n:\n        \" \" + s => s\n    return s\n\ndef words(s):\n    [] => out\n    \"\" => w\n    for c in s + \" \":\n        if c == \" \":\n            if w != \"\":\n                out + [w] => out\n            \"\" => w\n        else:\n            w + c => w\n    return out\n\ndef good_key(w):\n    if len(w) == 0 or len(w) > 6:\n        return False\n    for c in w:\n        if not ((c >= \"a\" and c <= \"z\") or (c >= \"A\" and c <= \"Z\") or (c >= \"0\" and c <= \"9\")):\n            return False\n    return True\n\ndef joined(xs):\n    \"\" => s\n    for x in xs:\n        if s != \"\":\n            s + \" \" => s\n        s + x => s\n    return s\n\ndef percent(h, n):\n    # h / n as a percentage to one decimal, rounded half up.\n    (2000 * h + n) => a\n    2 * n => b\n    0 => q\n    while a >= b:\n        a - b => a\n        q + 1 => q\n    0 => whole\n    q => r\n    while r >= 10:\n        r - 10 => r\n        whole + 1 => whole\n    return str(whole) + \".\" + str(r) + \"%\"\n\ndef ask_size():\n    while True:\n        trim(input(\"cache size (1 to 8)> \")) => s\n        if s == \"\":\n            return -1\n        number(s) => n\n        if n >= 1 and n <= 8:\n            return n\n        \"Type a whole number from 1 to 8.\" ^0\n\ndef ask_policy():\n    while True:\n        trim(input(\"rule (LRU, FIFO, LFU or OPT)> \")) => s\n        if s == \"\":\n            return \"\"\n        if s == \"LRU\" or s == \"FIFO\" or s == \"LFU\" or s == \"OPT\":\n            return s\n        \"Type LRU, FIFO, LFU or OPT.\" ^0\n\ndef compare(trace):\n    ask_size() => size\n    if size == -1:\n        \"Cancelled.\" ^0\n        return\n    (str(len(trace)) + \" requests, cache of \" + str(size) + \":\") ^0\n    caches.run(trace, size, \"OPT\")[0] => best\n    for p in [\"LRU\", \"FIFO\", \"LFU\", \"OPT\"]:\n        caches.run(trace, size, p)[0] => h\n        \"\" => note\n        if p == \"OPT\":\n            \"  the most any rule can get\" => note\n        elif h == best:\n            \"  as good as OPT here\" => note\n        (\"  \" + pad(p, 5) + lpad(str(h), 3) + \" hits  \" + lpad(percent(h, len(trace)), 6) + note) ^0\n\ndef step_by_step(trace):\n    ask_policy() => p\n    if p == \"\":\n        \"Cancelled.\" ^0\n        return\n    ask_size() => size\n    if size == -1:\n        \"Cancelled.\" ^0\n        return\n    caches.run(trace, size, p) => r\n    \"  time  request  result  evicted  cache after\" ^0\n    for t in [0:len(trace) - 1]:\n        r[1][t] => st\n        \"miss\" => res\n        if st[1]:\n            \"hit\" => res\n        (\"  \" + lpad(str(t + 1), 4) + \"  \" + pad(st[0], 7) + \"  \" + pad(res, 6) + \"  \" + pad(st[2], 7) + \"  \" + joined(st[3])) ^0\n    (p + \": \" + str(r[0]) + \" hits out of \" + str(len(trace)) + \".\") ^0\n\ndef growing(trace):\n    \"hits for cache sizes 1 to 6:\" ^0\n    \"  size    1   2   3   4   5   6\" ^0\n    for p in [\"LRU\", \"FIFO\", \"LFU\", \"OPT\"]:\n        caches.hits_by_size(trace, p, 6) => hs\n        \"\" => line\n        for h in hs:\n            line + lpad(str(h), 4) => line\n        (\"  \" + pad(p, 5) + line) ^0\n        for k in [1:5]:\n            if hs[k] < hs[k - 1]:\n                (\"  \" + p + \" gets fewer hits with \" + str(k + 1) + \" places than with \" + str(k) + \" - Belady's anomaly.\") ^0\n\ndef new_trace(trace):\n    while True:\n        trim(input(\"requests, separated by spaces> \")) => s\n        if s == \"\":\n            \"Cancelled.\" ^0\n            return trace\n        words(s) => ws\n        True => ok\n        for w in ws:\n            if not good_key(w):\n                False => ok\n        if ok and len(ws) >= 1 and len(ws) <= 40:\n            (\"A trace of \" + str(len(ws)) + \" requests.\") ^0\n            return ws\n        \"Type 1 to 40 names of letters or digits, up to 6 each.\" ^0\n\n[\"1\", \"2\", \"3\", \"4\", \"1\", \"2\", \"5\", \"1\", \"2\", \"3\", \"4\", \"5\"] => trace\n\n\"== Cache simulator ==\" ^0\n\"One trace of requests through a small cache: LRU, FIFO, LFU and Belady's optimal OPT.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    (\"trace: \" + joined(trace)) ^0\n    \"1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        compare(trace)\n    elif choice == \"2\":\n        step_by_step(trace)\n    elif choice == \"3\":\n        growing(trace)\n    elif choice == \"4\":\n        new_trace(trace) => trace\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import caches\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef number(s):\n    if len(s) == 0 or len(s) > 2:\n        return -1\n    n = 0\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef pad(s, n):\n    while len(s) < n:\n        s = s + \" \"\n    return s\n\ndef lpad(s, n):\n    while len(s) < n:\n        s = \" \" + s\n    return s\n\ndef words(s):\n    out = []\n    w = \"\"\n    for c in s + \" \":\n        if c == \" \":\n            if w != \"\":\n                out = out + [w]\n            w = \"\"\n        else:\n            w = w + c\n    return out\n\ndef good_key(w):\n    if len(w) == 0 or len(w) > 6:\n        return False\n    for c in w:\n        if not (c >= \"a\" and c <= \"z\" or c >= \"A\" and c <= \"Z\" or c >= \"0\" and c <= \"9\"):\n            return False\n    return True\n\ndef joined(xs):\n    s = \"\"\n    for x in xs:\n        if s != \"\":\n            s = s + \" \"\n        s = s + x\n    return s\n\ndef percent(h, n):\n    a = 2000 * h + n\n    b = 2 * n\n    q = 0\n    while a >= b:\n        a = a - b\n        q = q + 1\n    whole = 0\n    r = q\n    while r >= 10:\n        r = r - 10\n        whole = whole + 1\n    return str(whole) + \".\" + str(r) + \"%\"\n\ndef ask_size():\n    while True:\n        s = trim(input(\"cache size (1 to 8)> \"))\n        if s == \"\":\n            return -1\n        n = number(s)\n        if n >= 1 and n <= 8:\n            return n\n        print(\"Type a whole number from 1 to 8.\")\n\ndef ask_policy():\n    while True:\n        s = trim(input(\"rule (LRU, FIFO, LFU or OPT)> \"))\n        if s == \"\":\n            return \"\"\n        if s == \"LRU\" or s == \"FIFO\" or s == \"LFU\" or s == \"OPT\":\n            return s\n        print(\"Type LRU, FIFO, LFU or OPT.\")\n\ndef compare(trace):\n    size = ask_size()\n    if size == -1:\n        print(\"Cancelled.\")\n        return\n    print(str(len(trace)) + \" requests, cache of \" + str(size) + \":\")\n    best = caches.run(trace, size, \"OPT\")[0]\n    for p in [\"LRU\", \"FIFO\", \"LFU\", \"OPT\"]:\n        h = caches.run(trace, size, p)[0]\n        note = \"\"\n        if p == \"OPT\":\n            note = \"  the most any rule can get\"\n        elif h == best:\n            note = \"  as good as OPT here\"\n        print(\"  \" + pad(p, 5) + lpad(str(h), 3) + \" hits  \" + lpad(percent(h, len(trace)), 6) + note)\n\ndef step_by_step(trace):\n    p = ask_policy()\n    if p == \"\":\n        print(\"Cancelled.\")\n        return\n    size = ask_size()\n    if size == -1:\n        print(\"Cancelled.\")\n        return\n    r = caches.run(trace, size, p)\n    print(\"  time  request  result  evicted  cache after\")\n    for t in range(0, len(trace)):\n        st = r[1][t]\n        res = \"miss\"\n        if st[1]:\n            res = \"hit\"\n        print(\"  \" + lpad(str(t + 1), 4) + \"  \" + pad(st[0], 7) + \"  \" + pad(res, 6) + \"  \" + pad(st[2], 7) + \"  \" + joined(st[3]))\n    print(p + \": \" + str(r[0]) + \" hits out of \" + str(len(trace)) + \".\")\n\ndef growing(trace):\n    print(\"hits for cache sizes 1 to 6:\")\n    print(\"  size    1   2   3   4   5   6\")\n    for p in [\"LRU\", \"FIFO\", \"LFU\", \"OPT\"]:\n        hs = caches.hits_by_size(trace, p, 6)\n        line = \"\"\n        for h in hs:\n            line = line + lpad(str(h), 4)\n        print(\"  \" + pad(p, 5) + line)\n        for k in range(1, 6):\n            if hs[k] < hs[k - 1]:\n                print(\"  \" + p + \" gets fewer hits with \" + str(k + 1) + \" places than with \" + str(k) + \" - Belady's anomaly.\")\n\ndef new_trace(trace):\n    while True:\n        s = trim(input(\"requests, separated by spaces> \"))\n        if s == \"\":\n            print(\"Cancelled.\")\n            return trace\n        ws = words(s)\n        ok = True\n        for w in ws:\n            if not good_key(w):\n                ok = False\n        if ok and len(ws) >= 1 and len(ws) <= 40:\n            print(\"A trace of \" + str(len(ws)) + \" requests.\")\n            return ws\n        print(\"Type 1 to 40 names of letters or digits, up to 6 each.\")\n\ntrace = [\"1\", \"2\", \"3\", \"4\", \"1\", \"2\", \"5\", \"1\", \"2\", \"3\", \"4\", \"5\"]\nprint(\"== Cache simulator ==\")\nprint(\"One trace of requests through a small cache: LRU, FIFO, LFU and Belady's optimal OPT.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"trace: \" + joined(trace))\n    print(\"1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        compare(trace)\n    elif choice == \"2\":\n        step_by_step(trace)\n    elif choice == \"3\":\n        growing(trace)\n    elif choice == \"4\":\n        trace = new_trace(trace)\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "caches.eml",
      "eml": "# P074 cache simulator - one trace of requests through a cache of a given\n# size under four rules for what to throw out when it is full, as in the\n# corpus case cache-eviction-policy:\n#   LRU   the key used longest ago\n#   FIFO  the key that came in first\n#   LFU   the key used least often since it came in (ties: longest ago)\n#   OPT   the key whose next use is furthest away, or never (Belady 1966) -\n#         it needs to know the future, so no real cache can do it, but\n#         nothing can do better, which makes it the fair yardstick.\n\ndef next_use(trace, key, t):\n    # The time key is next asked for after time t, or len(trace) if never.\n    t + 1 => k\n    while k < len(trace):\n        if trace[k] == key:\n            return k\n        k + 1 => k\n    return len(trace)\n\ndef victim(cache, policy, trace, t, last, came, uses):\n    # Which key in the cache to evict at time t.\n    cache[0] => best\n    for key in cache:\n        if policy == \"LRU\":\n            if last[key] < last[best]:\n                key => best\n        elif policy == \"FIFO\":\n            if came[key] < came[best]:\n                key => best\n        elif policy == \"LFU\":\n            if uses[key] < uses[best] or (uses[key] == uses[best] and last[key] < last[best]):\n                key => best\n        else:\n            if next_use(trace, key, t) > next_use(trace, best, t):\n                key => best\n    return best\n\ndef run(trace, size, policy):\n    # [hits, steps] where each step is [request, hit, evicted (\"\" if\n    # none), the cache after it, in the order keys came in].\n    [] => cache\n    {} => last\n    {} => came\n    {} => uses\n    0 => hits\n    [0] * len(trace) => steps\n    for t in [0:len(trace) - 1]:\n        trace[t] => key\n        \"\" => out\n        False => hit\n        for c in cache:\n            if c == key:\n                True => hit\n        if hit:\n            hits + 1 => hits\n            uses[key] + 1 => uses[key]\n        else:\n            if len(cache) == size:\n                victim(cache, policy, trace, t, last, came, uses) => out\n                [] => kept\n                for c in cache:\n                    if c != out:\n                        kept + [c] => kept\n                kept => cache\n            cache + [key] => cache\n            t => came[key]\n            1 => uses[key]\n        t => last[key]\n        [key, hit, out, cache] => steps[t]\n    return [hits, steps]\n\ndef hits_by_size(trace, policy, most):\n    # Hits for every cache size from 1 to most.\n    [0] * most => out\n    for s in [1:most]:\n        run(trace, s, policy)[0] => out[s - 1]\n    return out\n",
      "python": "def next_use(trace, key, t):\n    k = t + 1\n    while k < len(trace):\n        if trace[k] == key:\n            return k\n        k = k + 1\n    return len(trace)\n\ndef victim(cache, policy, trace, t, last, came, uses):\n    best = cache[0]\n    for key in cache:\n        if policy == \"LRU\":\n            if last[key] < last[best]:\n                best = key\n        elif policy == \"FIFO\":\n            if came[key] < came[best]:\n                best = key\n        elif policy == \"LFU\":\n            if uses[key] < uses[best] or uses[key] == uses[best] and last[key] < last[best]:\n                best = key\n        elif next_use(trace, key, t) > next_use(trace, best, t):\n            best = key\n    return best\n\ndef run(trace, size, policy):\n    cache = []\n    last = {}\n    came = {}\n    uses = {}\n    hits = 0\n    steps = [0] * len(trace)\n    for t in range(0, len(trace)):\n        key = trace[t]\n        out = \"\"\n        hit = False\n        for c in cache:\n            if c == key:\n                hit = True\n        if hit:\n            hits = hits + 1\n            uses[key] = uses[key] + 1\n        else:\n            if len(cache) == size:\n                out = victim(cache, policy, trace, t, last, came, uses)\n                kept = []\n                for c in cache:\n                    if c != out:\n                        kept = kept + [c]\n                cache = kept\n            cache = cache + [key]\n            came[key] = t\n            uses[key] = 1\n        last[key] = t\n        steps[t] = [key, hit, out, cache]\n    return [hits, steps]\n\ndef hits_by_size(trace, policy, most):\n    out = [0] * most\n    for s in range(1, most+1):\n        out[s - 1] = run(trace, s, policy)[0]\n    return out\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n0\n9\nabc\n\n2\nlru\nMRU\nLRU\n\n4\nA B toolongname\nA,B\nk k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k\n\n5\n",
      "screen": "== Cache simulator ==\nOne trace of requests through a small cache: LRU, FIFO, LFU and Belady's optimal OPT.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> x\nPick a number from 1 to 5.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 1\ncache size (1 to 8)> 0\nType a whole number from 1 to 8.\ncache size (1 to 8)> 9\nType a whole number from 1 to 8.\ncache size (1 to 8)> abc\nType a whole number from 1 to 8.\ncache size (1 to 8)> \nCancelled.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 2\nrule (LRU, FIFO, LFU or OPT)> lru\nType LRU, FIFO, LFU or OPT.\nrule (LRU, FIFO, LFU or OPT)> MRU\nType LRU, FIFO, LFU or OPT.\nrule (LRU, FIFO, LFU or OPT)> LRU\ncache size (1 to 8)> \nCancelled.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 4\nrequests, separated by spaces> A B toolongname\nType 1 to 40 names of letters or digits, up to 6 each.\nrequests, separated by spaces> A,B\nType 1 to 40 names of letters or digits, up to 6 each.\nrequests, separated by spaces> k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k\nType 1 to 40 names of letters or digits, up to 6 each.\nrequests, separated by spaces> \nCancelled.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n3\n3\n2\nFIFO\n3\n2\nOPT\n3\n4\nA B C A B D A B C D E A\n1\n2\n5\n",
      "screen": "== Cache simulator ==\nOne trace of requests through a small cache: LRU, FIFO, LFU and Belady's optimal OPT.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 1\ncache size (1 to 8)> 3\n12 requests, cache of 3:\n  LRU    2 hits   16.7%\n  FIFO   3 hits   25.0%\n  LFU    2 hits   16.7%\n  OPT    5 hits   41.7%  the most any rule can get\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 3\nhits for cache sizes 1 to 6:\n  size    1   2   3   4   5   6\n  LRU     0   0   2   4   7   7\n  FIFO    0   0   3   2   7   7\n  FIFO gets fewer hits with 4 places than with 3 - Belady's anomaly.\n  LFU     0   0   2   4   7   7\n  OPT     0   3   5   6   7   7\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 2\nrule (LRU, FIFO, LFU or OPT)> FIFO\ncache size (1 to 8)> 3\n  time  request  result  evicted  cache after\n     1  1        miss             1\n     2  2        miss             1 2\n     3  3        miss             1 2 3\n     4  4        miss    1        2 3 4\n     5  1        miss    2        3 4 1\n     6  2        miss    3        4 1 2\n     7  5        miss    4        1 2 5\n     8  1        hit              1 2 5\n     9  2        hit              1 2 5\n    10  3        miss    1        2 5 3\n    11  4        miss    2        5 3 4\n    12  5        hit              5 3 4\nFIFO: 3 hits out of 12.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 2\nrule (LRU, FIFO, LFU or OPT)> OPT\ncache size (1 to 8)> 3\n  time  request  result  evicted  cache after\n     1  1        miss             1\n     2  2        miss             1 2\n     3  3        miss             1 2 3\n     4  4        miss    3        1 2 4\n     5  1        hit              1 2 4\n     6  2        hit              1 2 4\n     7  5        miss    4        1 2 5\n     8  1        hit              1 2 5\n     9  2        hit              1 2 5\n    10  3        miss    1        2 5 3\n    11  4        miss    2        5 3 4\n    12  5        hit              5 3 4\nOPT: 5 hits out of 12.\n\ntrace: 1 2 3 4 1 2 5 1 2 3 4 5\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 4\nrequests, separated by spaces> A B C A B D A B C D E A\nA trace of 12 requests.\n\ntrace: A B C A B D A B C D E A\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 1\ncache size (1 to 8)> 2\n12 requests, cache of 2:\n  LRU    0 hits    0.0%\n  FIFO   0 hits    0.0%\n  LFU    0 hits    0.0%\n  OPT    3 hits   25.0%  the most any rule can get\n\ntrace: A B C A B D A B C D E A\n1) compare  2) step by step  3) as the cache grows  4) new trace  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "cache-eviction-policy",
      "caseId": "249-cache-eviction-policy",
      "title": "Cache eviction policy — the workload decides, not the policy"
    }
  ],
  "updated": "2026-10-14"
}
