How to run flint NVFP4 on 2× RTX 5060 Ti 16 GB

The 262,144-token window is real to about 60 % and fiction beyond: 18/18 needle hits through 157,286 tokens, 0/12 from 183,501 up — a hard cliff at the edge of the sliding-window cache, and past it the model fabricates. three clean stops, zero runnable pipelines. Plan usable context at ~160 k tokens.

Evaluated by grok (xAI) · Written by Ornith-1.5-35B-A3B

Results

LoadWallPPTGPredFinishQuality
25.7 s 371.5 s 815 84.9 31539 stop 2/5

Medians of three runs (wall 376.2 / 331.8 / 371.5 s); per-run quality 2 / 2 / 3. PP is the run-1 value — runs 2 and 3 prefix-cached the identical prompt (5 prompt tokens) and are excluded. Thinking tax ≈ 94 % of emitted characters, the heaviest of this batch.

Needle retrieval

Window
262144
Depth
26,214 → 262,080 (every 10 % of the slot)
Result
cliff
pp/tg
18/18 hits to 157,286 · 0/12 from 183,501 up
Anchor
tesseract-1c7d3f-vault

Quality of result

Adversarial review before code 4/5 Three precise resolutions with reasoning — integer vs :infinity TTL semantics, cancelling the old timer on reschedule, the start_link contract; reads the trap correctly.
Public API present (put/get/delete/stats) 5/5 Full set, typed with @spec lines throughout.
TTL + periodic eviction + named instances + hit/miss stats 4/5 Design is sound — timer refs stored, reschedule on write, per-put override — and the module compiles; behaviour is unverifiable because the suite never runs.
Idiomatic OTP / would it compile? 4/5 Module compiles clean (warnings only) on Elixir 1.20.4 / OTP 29; the companion test file does not.
Tests shipped, not promised 1/5 The suite calls start_supervised!/3, an arity that does not exist, and misses the ExUnit.Case import — nothing runs.
Finish reason + thinking tax 4/5 Clean stop in all three runs; ≈ 94 % tax, the heaviest measured in this batch.

Scale: 5 strong · 4 capable · 3 mixed · 2 weak · 1 broken.

Prompt

This run used v5 (The benchmarking prompt) — It answers a single Elixir-specific question, so YMMV.

Elixir code smells 4

  • test_3.exs:11-16 — run 3 — invented test helper: start_supervised! called with a 3-arity; the real ExUnit API takes the 1-tuple form.
  • kv_2.ex:10:41 — run 2 — atom invoked as a function: :infinity() inside a @type.
  • kv_1.ex:19 — run 1 — bare unbound word passed as the init arg: GenServer.start_link(__MODULE__, init, [opts], …); the same pattern recurs in run 3’s module.
  • kv_3.ex:— — run 3 — deprecated Map.size/1.

Defects 3

  • kv_1.ex — undefined variable init — the start arg is a never-bound bareword (module compile FAIL, Elixir 1.20.4 / OTP 29 — run 1)
  • kv_2.ex — SyntaxError — :infinity() parses as a function call inside a @type (module compile FAIL — run 2)
  • test_3.exs — undefined function start_supervised!/3 — the 3-arity helper does not exist (module PASS, tests FAIL — run 3)

Host

QuantNVFP4
Hardware2x RTX 5060 Ti 16 GB (PCIe 5.0 x8/x8), Ryzen 9700X, 16 GB DDR5-6000
Splitlayer-split per card — the architecture cannot tensor-split
Runtimellama-server 0.7.0-dev (d05706b4f), CUDA
HarnessGrok Build 1.0.50 (c58f321264ba)
Tested2026-10-31
Weightshuggingface.co/RecursiveSI/flint-nvfp4-gguf

Reproduce

Two 16 GB cards. This architecture cannot run tensor-split, so the run is served --split-mode layer. No drafter (speculative decoding off), no --mmproj (text-only). --host/--port omitted. The actual spawn line carried the sampler and the split mode twice; llama.cpp keeps the last value, so the line above is the effective configuration. It matches the vendor’s recommended sampling exactly (temp 1.0, top_p 0.95, top_k 20, min-p 0). The vendor card recommends at least 48 GB for this file; this 32 GiB pool serves it with q8_0 K/V. The answer cap was 261,000 tokens — prompt and output share the context window. Needle answers were capped at 512 tokens; the first attempt at a 48-token cap starved the content channel behind the reasoning stream, and those rows are archived, not measured.
llama-server \
  -m flint-nvfp4.gguf \
  --ctx-size 262144 \
  --parallel 1 \
  -b 2048 \
  -ub 512 \
  -t 6 \
  -tb 6 \
  -ngl 999 \
  -ctk q8_0 \
  -ctv q8_0 \
  --kv-offload \
  --cache-ram 0 \
  --no-cache-idle-slots \
  --load-mode mmap \
  --no-context-shift \
  -fa on \
  --jinja \
  --reasoning on \
  --reasoning-preserve \
  --cont-batching \
  --split-mode layer \
  --temp 1.0 \
  --top_p 0.95 \
  --top_k 20 \
  --min-p 0.0 \
  --presence_penalty 0.0

Warning: this is a temporary briefing, written by an LLM and not yet evaluated or rewritten by a human to get rid of the signature AI-slop writing style. It’s published as a draft despite its shortcomings, so that you can find out how to run it and what to expect in terms of pp and tg on this hardware, as well as what to expect in terms of coding performance on this benchmark, grosso modo.

I ran the v5 prompt three times on flint-nvfp4 — a 39.8B mixture-of-experts model with 2.5B active parameters, served at its full 262,144-token native window, layer-split across two RTX 5060 Ti 16 GB cards. This architecture cannot run tensor-split, so the box’s default split did not apply. The vendor card asks for at least 48 GB of memory for this file; this 32 GiB pool serves it with quantized K/V, so part of what you will see below is the 32 GiB budget showing through.

The needle ladder is where the badge breaks. The same unique anchor, planted at every tenth of the window, three repeats per depth, each request opening with a fresh nonce so no step inherited a warm cache:

depth hit pp (median) tg (median)
10 % (26,214) 3/3 1,741 40.2
20 % (52,429) 3/3 2,146 60.3
30 % (78,643) 3/3 1,975 49.8
40 % (104,858) 3/3 1,817 42.1
50 % (131,072) 3/3 1,059 22.5
60 % (157,286) 3/3 1,568 32.7
70 % (183,501) 0/3 1,060 22.5
80 % (209,715) 0/3 1,062 22.5
90 % (235,930) 0/3 1,064 22.6
100 % (262,080) 0/3 1,226 22.5

Eighteen of eighteen through 157,286 tokens, then zero of twelve from 183,501 up. Prefill dipped once at 50 % — KV pressure at this budget — and held flat to the edge; decode never moved. The speed was fine; the memory was the problem. The cliff sits exactly where the vendor’s card says memory should end: flint uses sliding-window attention, where only the recent part of the window is kept at full resolution, and the card instructs leaving --swa-full unset. For reference, a 131k full-attention model on this identical box (LFM2.5-24B) hit 30/30 to the edge of its window.

But past the cliff, it gets worse than a miss. The model does not report “not found”:

  • 70 %: claims the marker appears “exactly once as shown” while showing nothing, or calls the document “garbled or corrupted”.
  • 80 %: its planning monologue leaks into the answer (“We need answer user. They gave long reference doc…”) and it runs to the cap without producing the marker.
  • 90 %: emits the marker’s skeleton (tesseract-XXXX-vault (8)) instead of its value, then “The final answer is:” and nothing.
  • 100 %: degenerates into “Okay, I’ve got the unique marker” repeated until the cap.

An agent that trusts those answers writes bad code confidently. A model that misses can be debugged; a model that fabricates at 70 % of a 262k window cannot.

The runs, for the record: three clean stops, full deliveries, 0/3 pipelines through the compile-and-test gate. Two modules do not compile; the third compiles while its test suite calls a helper that does not exist. Thinking tax ran about 94 % of emitted characters — the heaviest measured in this batch. The review pass was the strongest part of the delivery: three precise resolutions, each with reasoning.

Representative output

Run 1’s TtlKeyValue — trimmed to the public API and eviction handler (a sliding-window cache, so this is the row that read past 60 % and kept going):

defmodule TtlKeyValue do
  @default_ttl 60_000
  @eviction_interval 60_000

  use GenServer

  @spec start_link(keyword()) :: GenServer.on_start()
  def start_link(opts) when is_list(opts) do
    ttl = Keyword.get(opts, :ttl, @default_ttl)

    if valid_ttl?(ttl) do
      GenServer.start_link(__MODULE__, init, [opts], name: __MODULE__)
    else
      raise ArgumentError, "ttl must be a positive integer or nil"
    end
  end

  @spec put(binary(), binary(), ttl()) :: :ok
  def put(key, value, ttl) do
    validate_key!(key)
    validate_value!(value)
    validate_ttl!(ttl)
    GenServer.call(__MODULE__, {:put, key, value, ttl})
  end

  @spec get(binary(), server()) :: {:ok, binary()} | :error
  def get(key, server) do
    validate_key!(key)
    GenServer.call(server, {:get, key})
  end

  def delete(key, server) do
    validate_key!(key)
    GenServer.call(server, {:delete, key})
  end

  def stats(server) do
    GenServer.call(server, {:stats, :ok})
  end

  def init(opts) do
    ttl = Keyword.get(opts, :ttl, @default_ttl)
    Process.send_after(self(), :evict, @eviction_interval)
    {:ok, %{ttl: ttl, entries: %{}, hits: 0, misses: 0, memory: 0}}
  end

  def handle_call({:get, key}, _from, state) do
    case Map.fetch(state.entries, key) do
      {:ok, {value, expires_at}} ->
        if expires_at != nil and expires_at <= System.monotonic_time(:millisecond) do
          {:reply, :error, %{state | misses: state.misses + 1}}
        else
          {:reply, {:ok, value}, %{state | hits: state.hits + 1}}
        end

      :error ->
        {:reply, :error, %{state | misses: state.misses + 1}}
    end
  end

  def handle_info(:evict, state) do
    now = System.monotonic_time(:millisecond)
    entries = Map.filter(state.entries, fn {_key, {_value, expires_at}} -> expires_at == nil or expires_at > now end)
    Process.send_after(self(), :evict, @eviction_interval)
    {:noreply, %{state | entries: entries}}
  end
end

Plan usable context at ~160 k tokens. Anything above that gets an external retrieval layer. Whether --swa-full or a 48 GB repack moves the cliff is a measured question — it earns a run before the 262 k number gets cited again.

Have something worth building?

A paragraph about what you are working on is enough to start.

Say hello