using what I paid for

finally trying to utilize my gaming rig

For years I have wanted to game. To be a cool nerd who is good at video games. To find joy in the escape of unreal worlds and interesting puzzles. So many of my friends are gaming, from elementary school (Mario, Sonic, ToeJam and Earl with my mom) to middle school (Tony Hawk, GoldenEye) through high school (Metal Gear Solid still GOAT, thanks Blake) across college (Halo) over grad school (Madden and watching Starcraft) through to today. I’m on multiple Discords whose main purpose is to assemble people to play games. I’ve built two PCs just for me to game, the second one I’m on right now with a RTX 4070.

Turns out, I don’t game.

Instead, I build. I have spent this weekend with Pi and Gemini 3.8 Flash just to make a trip layout. Just like how I cooked four portions of murgir jhol and made 2lb of koobideh all in one day. I get into that escape state when I am making, not when I am playing. That’s why I wrote that previous post on Pi and Gemini. That line where I said “Pi opens doors to local large language model usage, a future post.”?

This is that post.

That gaming rig I am using right now? Runs Windows 11 with WSL2 and thus Ubuntu 24.04.

  • GPU: NVIDIA GeForce RTX 4070 so 12 GB GDDR6X
  • Usable VRAM: ~10.6 GiB (the rest is Windows programs like Chrome, Discord, etc.)
  • CPU: AMD Ryzen 7 7700X. 8 cores, Zen 4, AVX-512 boost up to 5.4 GHz
  • RAM: 32 GB DDR5

There are lots of open models available. None are going to be as amazing as Fable or Astra or even GLM 5.3, but that’s OK. Like I said, if I wanted expert food I’d eat out. I’m eating in today.

click to see the analysis for my computer, otherwise let's move on

First, the one true equation for local LLM support: what context can you support?

Available VRAM − model weights − runtime overhead = context and KV cache
Family Model Parameters Footprint† Usable Context GPU-only? Quality Speed
Qwen Qwen 3.5 9B 9B ~6–7 GB 32K+ ⭐⭐⭐⭐½ Fast
Mistral Ministral 3 8B 8B ~5 GB 16–24K ⭐⭐⭐⭐½ Very fast
Google Gemma 4 12B 12B ~6.7 GB 8–16K ⚠️ ⭐⭐⭐⭐⭐ Fast
Qwen Qwen 3.8 27B 27B ~18 GB 4K* ⭐⭐⭐⭐⭐ Very slow
Meta Llama 3.1 8B 8B ~5 GB 32K ⭐⭐⭐½ Fast
Qwen Qwen 3 14B 14B ~9 GB 8K ⚠️ ⭐⭐⭐⭐⭐ Moderate
Mistral Ministral 3 14B 14B ~9.1 GB 8K ⚠️ ⭐⭐⭐⭐½ Moderate
Qwen Qwen 3 Coder 30B-A3B 3.3B / 30.5B ~17+ GB 8–16K* ⭐⭐⭐⭐⭐ Slow
Mistral Devstral Small 2 24B 24B ~14+ GB 8K* ⭐⭐⭐⭐⭐ Slow
Z.ai GLM-4 9B 9B ~5.5 GB 32K ⭐⭐⭐ Fast
Meta Llama 3.2 3B 3B ~2 GB 64K ⭐⭐½ Blazing

†Footprint assumes 4-bit quantization of weights.

We’re keeping a few models out b/c like I said we’re not rich.

  • Inkling-Small (Thinking Machines): 276B total / 12B active. Too big, just too big.
  • GLM-5.3-Flash (Z.ai): 320B total / 18B active. Compute might work but still need to load all the weights.
  • Llama 3.3 70B (Meta): 70B needs ~40 GB memory at 4-bit quantization. Would use a lot of RAM offloading all the time.

Also a lot of credit here to localmodel.run for making numbers easy to read.

From the analysis hidden in that expand, I’m looking at the following four candidates. Installation instructions are going to be in a follow-up blog post.

Like in 2026 February I’ll try it out on the same basic task.

You are a staff engineer at Google. Plan how to write a local markdown-based journal. It should have three parts:

A journal.py script that takes a string and saves it to a file named YYYY-MM-DD.md in an entries/ folder.

It should automatically append a timestamp to each entry.

If the entries/ folder doesn’t exist, it should create it.

Inspect the repository first. Then implement the task, run it, and verify that it works.

Do not ask me questions. Complete the task autonomously.

click to see benchmark setup

Set up Jujutsu for its easy separation of work that is on the same code base.

sudo apt update
sudo apt install build-essential libssl-dev openssl pkg-config
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# restart shell
cargo install --locked --bin jj jj-cli

Now the empty repository.

mkdir -p the_winning
cd the_winning
jj git init
jj new
jj config set --user user.name "Syed Ashrafulla"
jj config set --user user.email "syed+the_winning@ashraful.la"
printf "# The Winning: Benchmarking Local LLMs on My Gaming Rig\n" > README.md
jj describe -m "[docs] init with readme"

Then the prompt.

jj new -m "[feat] add prompt"
cat > PROMPT.md <<'EOF'
You are a staff engineer at Google. Plan and then implement a local markdown-based journal.

A journal.py script that takes a string and saves it to a file named YYYY-MM-DD.md in an entries/ folder.

It should automatically append a timestamp to each entry.

If the entries/ folder doesn't exist, it should create it.

Inspect the repository first. Then implement the task, run it, and verify that it works.

Do not ask me questions. Complete the task autonomously.
EOF

Then the test benches.

jj workspace add ../the_winning-qwen35
jj workspace add ../the_winning-ministral
jj workspace add ../the_winning-gemma
jj workspace add ../the_winning-qwen38

Then the benchmarking script. The important takeaway: use pi --mode rpc --no-session and feed JSON {"type":"prompt","message":"..."}. That allows for agentic loops. pi -p is just a single task, a single iteration of the loop. And yet again, AI-assisted tiny Python scripts are better than most anything else.

#!/usr/bin/env bash
set -uo pipefail

ROOT="$HOME/the_winning"
WINROOT="/mnt/c/Users/syeda"

cd "$ROOT"

while read -r model; do
  echo "===== $model ====="

  name="${model//[:\/]/-}"
  real="$WINROOT/the_winning-$name"
  link="$HOME/the_winning-$name"

  [ -d "$real" ] || (cd "$ROOT" && jj workspace add "$real")
  [ -e "$link" ] || ln -s "$real" "$link"

  cd "$link"
  /usr/bin/time -v \
    python3 - "$model" "$ROOT/PROMPT.md" <<'PY' \
    > "$ROOT/$name.jsonl" \
    2> "$ROOT/$name.time"
import json
import subprocess
import sys

model = sys.argv[1]
prompt = open(sys.argv[2]).read()

p = subprocess.Popen(
    ["pi", "--mode", "rpc", "--no-session", "--model", model],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True,
)

p.stdin.write(json.dumps({"type": "prompt", "message": prompt}) + "\n")
p.stdin.flush()

for line in p.stdout:
    print(line, end="")
    try:
        event = json.loads(line)
    except json.JSONDecodeError:
        continue
    if event.get("type") == "agent_settled":
        break
p.stdin.close()
p.wait()
PY
  jj diff > "$ROOT/$name.diff"
  cd "$ROOT"

done < <(curl -sf http://localhost:11434/v1/models | jq -r '.data[].id')

As the experiment runs you can watch how the models are used via curl -s http://localhost:11434/api/ps | jq.

Qwen 3.8 made my gaming rig act funny. As described above Qwen 3.8 on my rig requires RAM offloading. That offloading was so much that actions from SSH were throttled. In addition, my live hugo server paused loading. That’s territorial flex.

Model Time Output tokens Tool calls Status Tests written Evidence Human review
Gemma 4 0:48.01 1,557 5 PASS NO Code generation without actual thought. -
Ministral 3 0:36.19 1,591 21 PASS YES test*.py script but still little though. ⭐⭐⭐⭐
Qwen 3.5 0:57.02 3,152 18 PASS YES More fuzz testing than committed testing, interesting take. ⭐⭐⭐⭐⭐
Qwen 3.8 2:31.75 847 4 PASS YES More complicated fuzz testing but did not keep tests. ⭐⭐⭐⭐

It’s true, Gemma 4 refused to write unit tests. Ministral’s tool calls were writing test scripts instead of fuzzing like Qwen.

My qualitative read of the code below is that yes Qwen is closest to usable maybe? Gemma 4 and Ministral 3 are just code generators. But that’s not throwing those models away. Add an orchestration layer with a smarter model and you can mold AI slop. It’s more annealing than intelligence but 36 seconds is fast. This kind of experiment provides a feel for different paths of engagement with local LLMs.

Gemma 4's submission It's not production code, just answer-the-question code. I would call Gemma 4 a high schooler at best.
import argparse
import os
from datetime import datetime

def main():
    parser = argparse.ArgumentParser(description="A simple markdown-based journal.")
    parser.add_argument("entry", help="The content of the journal entry.")
    args = parser.parse_args()

    # Create entries directory if it doesn't exist
    if not os.path.exists("entries"):
        os.makedirs("entries")

    # Get current date and time
    now = datetime.now()
    date_str = now.strftime("%Y-%m-%d")
    timestamp = now.strftime("%Y-%m-%d %H:%M:%S")

    # File path
    file_path = os.path.join("entries", f"{date_str}.md")

    # Prepare the entry with timestamp
    entry_text = f"### {timestamp}\n{args.entry}\n\n"

    # Save to file
    with open(file_path, "a", encoding="utf-8") as f:
        f.write(entry_text)

    print(f"Entry saved to {file_path}")

if __name__ == "__main__":
    main()
Ministral 3's submission It's actually simpler than Gemma 4 to pass the unit tests, but is also clearly not production, just fastest to answer question.
#!/usr/bin/env python3
import os
import sys
from datetime import datetime

def create_journal_entry(content):
    timestamp = datetime.now().strftime("%Y-%m-%d")
    entry_path = (
        os.path.join(os.getcwd(), "entries", f"{timestamp}.md")
    )
    os.makedirs("entries", exist_ok=True)

    with open(entry_path, "w") as f:
        f.write(f"# Journal Entry: {timestamp}\n")
        f.write(f"### Timestamp: {datetime.now().strftime('%H:%M:%S\n')}")
        f.write(f"\n{content}\n")

if __name__ == "__main__":
    if len(sys.argv) <= 1:
        print("Usage: python journal.py \"your journal entry\"")
    else:
        entry_content = " ".join(sys.argv[1:])
        create_journal_entry(entry_content)
Qwen 3.5's submission Many more comments and a bit of production readiness in terms of time parsing. You see hints of complexity in code but not really.
#!/usr/bin/env python3
"""
A markdown-based local journal that saves entries to files.
Usage: python journal.py "your journal entry"
"""

import os
import sys
from datetime import datetime


def save_entry(entry_text: str, base_dir="."):
    """
    Save a journal entry to a file with today's date and a timestamp.

    Args:
        entry_text: The content of the journal entry.
        base_dir: The directory where entries are stored (default: current directory).
    """
    # Create entries directory if it doesn't exist
    entries_dir = os.path.join(base_dir, "entries")
    if not os.path.exists(entries_dir):
        os.makedirs(entries_dir)

    # Get current date for filename (YYYY-MM-DD format)
    date_suffix = datetime.now().strftime("%Y-%m-%d")

    # Construct filename with date suffix
    entry_file = os.path.join(entries_dir, f"{date_suffix}.md")

    # Append timestamp to entry content
    timestamp = datetime.now()
    timestamp_line = f"\n\n---\n*Entry created at: {timestamp.strftime('%Y-%m-%d %H:%M:%S')}\n*\n"

    # Write the entry to file
    with open(entry_file, "w", encoding="utf-8") as f:
        f.write(entry_text + timestamp_line)

    print(f"Entry saved to: {entry_file}")

    return entry_file


def main():
    """Main function - expects a journal entry as command line argument or from stdin."""
    if len(sys.argv) > 1 and sys.argv[1] != "-":
        # Read entry from command line argument (strip quotes if present)
        entry_text = " ".join(sys.argv[1:])
        print(f"Saving entry for {datetime.now().strftime('%Y-%m-%d')}...\n\n")
        save_entry(entry_text.replace("\"", "").replace("'", ""))
    else:
        # Read entry from stdin (echo -e "entry" | python journal.py)
        print("Saving entry from stdin...")
        entry_text = sys.stdin.read()
        entry_text = entry_text.strip()
        if entry_text:
            save_entry(entry_text)
        else:
            print("No entry provided.")


if __name__ == "__main__":
    main()
Qwen 3.8's submission So interestingly this is as simple as Gemma 4 and Ministral 3 but seems to be a bit more conceptual in terms of handling time. Not bad but again, at best we're talking kids learning to code. Stack Overflow probably has better answers.
#!/usr/bin/env python3
"""Local markdown-based journal.

Usage:
    python journal.py "My entry text"
"""

import sys
from datetime import datetime
from pathlib import Path

ENTRIES_DIR = Path(__file__).parent / "entries"


def save_entry(text: str) -> Path:
    ENTRIES_DIR.mkdir(parents=True, exist_ok=True)

    now = datetime.now()
    path = ENTRIES_DIR / f"{now:%Y-%m-%d}.md"
    timestamp = now.strftime("%Y-%m-%d %H:%M:%S")

    separator = "" if path.exists() else f"# Journal — {now:%Y-%m-%d}\n\n"
    with path.open("a", encoding="utf-8") as f:
        f.write(f"{separator}## {timestamp}\n\n{text}\n\n")

    return path


def main() -> int:
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} \"entry text\"", file=sys.stderr)
        return 1

    path = save_entry(sys.argv[1])
    print(f"Saved to {path}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Also now I’m actually using my gaming rig. Maybe my definition of gaming is different than others.

Published by using 1909 words.