Skip to content

fix(qwen35): reclaim prefill scratch after allocation failure - #731

Merged
Kaden-Schutt merged 1 commit into
warpfront:betafrom
fivetide:replan/g4-error-ownership
Sep 9, 2026
Merged

fix(qwen35): reclaim prefill scratch after allocation failure#731
Kaden-Schutt merged 1 commit into
warpfront:betafrom
fivetide:replan/g4-error-ownership

Conversation

@fivetide

@fivetide fivetide commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Scope

First independent G4 error-path ownership fix for #666, based directly on upstream beta at febf298a22862b24ab6bb709546499f23f07676b. This PR does not close G4 or promote any device-mesh milestone. No historical #676 stack is replayed.

Reproduced defect

PrefillBatchScratch::new_opt recorded DeviceBuffer::alias() handles in its rollback ledger. Those are borrowed buffers; Gpu::free_tensor explicitly refuses to pool borrowed handles. When a later allocation failed, the partially constructed object dropped its actual owners without freeing them, and the alias-ledger cleanup could not reclaim them.

On gfx1151/HIP 7.2, the committed GPU regression warms 54 real allocations, injects failure before the final allocation, and immediately retries construction. Before the fix the fresh-allocation counter increased 54 → 107 (53 reusable buffers lost). After the fix it stays 54; retry reuses the complete warm pool.

Fix

  • Stage actual owners in Vec<Option<GpuTensor>>, not aliases.
  • Preserve all 54 possible allocation shapes, dtypes, ordering and optional predicates.
  • Reverse-release staged owners on failure; move each owner into the published scratch exactly once only after all allocations succeed.
  • Private statically-dispatched allocator callback supports the regression; no environment flag, runtime injection mode, public API change, kernel or arithmetic change.
  • Update the affected crate map and changelog. No loader policy, fallback, terminal/reset rewrite, workspace dependency or ratchet change.

Verification

Source head: 0c76c3470afe8da862b25d7f34b21669ce6dd9d2. CLI/daemon were rebuilt from the final Rust content before committing; the final commit also includes the generated map and changelog. This is narrow PR evidence, not a final-composition milestone receipt.

cargo build --release -p hipfire-cli -p hipfire-daemon --locked
cargo test --release -p hipfire-arch-qwen35 --lib -- --test-threads=1
# 191 passed; 0 failed; 1 ignored (explicit GPU regression below)
flock -w 1800 /tmp/hipfire-gpu.lock cargo test --release   -p hipfire-arch-qwen35 --lib prefill_scratch_failure_preserves_reusable_allocations   -- --ignored --nocapture --test-threads=1
# pre-fix: FAILED, left107/right54; post-fix: 1 passed, complete warm-pool reuse
cargo clippy -p hipfire-arch-qwen35 --lib --locked
# completes with existing warnings, no errors
rustfmt --edition 2021 --check --config skip_children=true   crates/hipfire-arch-qwen35/src/qwen35/batch.rs
python3 scripts/check-crate-maps.py --check  # 43 maps match
bash scripts/leanup-ratchets.sh             # 21 metrics, 0 violations
python3 scripts/check-env-docs.py           # covered/config-owned

Independent static review: no blocking findings; confirmed each slot's shape/dtype/order/optional predicate and exactly-once publication. That review did not run hardware tests; the runs below were executed separately.

Hardware serving evidence

Halo, gfx1151, HIP 7.2. GPU access serialized using flock. All service binds were 127.0.0.1:11466 with isolated temporary homes, not the user's running service.

Identity MD5
qwen3.6-27b.mq4 9a6acdc49bcaa6a7b52ac161444cb769
qwen36-27b-dflash-mq4.hfq 204c4c4ceab30cb9ebc118fa9d59a446
qwen3.6-35b-a3b.mq4 edde51ec1dac0f2bd42cff5ef1cb8944
final hipfire binary 12ab9cdabc8fe5b1be6973f2d2c76ab6
final daemon binary 510898a04b786c41f3a2fc0e678e9161
baseline hipfire binary fb423f33edf3ec8c97b899dd08103d61
baseline daemon binary 45ac198243de4700953f0b5af735550c
benchmarks/prompts/bare_factual.txt 1d32df5f12c414d3e34c7b35b6611e6c

Common harness settings: --thinking off --sampling greedy --kv q8 --kv-backend vmm --max-seq 4096 --max-tokens 512 --port 11466; native CLI/daemon paths explicitly supplied using HIPFIRE_CLI_BIN/HIPFIRE_DAEMON_BIN.

Executed python3 scripts/serve_harness.py routes:

  1. Dense --model ~/.hipfire/models/qwen3.6-27b.mq4 --tag qwen3.6:27b --draft ~/.hipfire/models/qwen36-27b-dflash-mq4.hfq --dflash on --mode battery --prompt-file benchmarks/prompts/bare_factual.txt.
  2. Same dense/draft settings with --mode chain, using the committed harness's built-in five-turn chain.
  3. A3B --model ~/.hipfire/models/qwen3.6-35b-a3b.mq4 --tag qwen3.6:35b-a3b --dflash off --mode battery --prompt-file benchmarks/prompts/bare_factual.txt.

All seven turns finished stop; zero empty, runaway, attractor or retrieval-miss flags. Full decoded text was read. Dense logs confirm the paired DFlash draft loaded and verify-graph B16 warmup; dense reports have dflash=true. A3B used AR.

Additionally, a byte-identical baseline/final dense DFlash request at --max-tokens128 produced byte-identical decoded output (request_md5=6722a9b3ac054aca62181d96ff073e73). Both reached that deliberately small cap and were flagged length/runaway; this is a matched output-preservation observation, not a completed-turn pass. The separate512-token final run completed normally at148 generated tokens.

Full decoded final serving responses and request identities (timing omitted; no perf claim)

g4-final-dense, turn 1

Prompt MD5 1d32df5f12c414d3e34c7b35b6611e6c; request MD5 5cbcaa74134d340a209b9169a0e1eba7; finish stop; generated 148; cached 0.

The capital of France is **Paris**.

*   **River:** The **Seine** River runs through the city.
*   **Famous Landmark:** The **Eiffel Tower** is one of the most iconic landmarks in Paris.

**Historical Importance:**
Paris is historically significant as a global center for art, fashion, gastronomy, and culture. It played a pivotal role in major historical events, such as the **French Revolution** (1789), which reshaped modern democracy and human rights. The city was also a hub for intellectual movements like the **Enlightenment** and the **Romantic** and **Impressionist** art movements, influencing global culture and politics for centuries.

g4-final-chain, turn 1

Prompt MD5 43ca0d15712d3dfb777b51ae76d8fd5f; request MD5 2c0d12dbd7bb19517b442aa096960a8f; finish stop; generated 168; cached 0.

```python
def merge_sorted(a, b):
    """
    Merge two sorted lists into a single sorted list.
    
    Args:
        a: First sorted list.
        b: Second sorted list.
    
    Returns:
        A new list containing all elements from a and b in sorted order.
    """
    result = []
    i, j = 0, 0
    
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            result.append(a[i])
            i += 1
        else:
            result.append(b[j])
            j += 1
    
    # Append remaining elements
    result.extend(a[i:])
    result.extend(b[j:])
    
    return result
```

g4-final-chain, turn 2

Prompt MD5 640e0fd4f55996cb175a422f0a12cef5; request MD5 3b927296b47ce16279c927d23c9e432b; finish stop; generated 206; cached 212.

To find the total distance traveled, we calculate the distance for each segment of the trip separately and then add them together.

**Step 1: Calculate the distance for the first segment**
*   Speed = 60 mph
*   Time = 2.5 hours
*   Distance = Speed × Time
*   Distance₁ = 60 × 2.5 = 150 miles

**Step 2: Calculate the distance for the second segment**
*   Speed = 40 mph
*   Time = 1.5 hours
*   Distance = Speed × Time
*   Distance₂ = 40 × 1.5 = 60 miles

**Step 3: Add the distances together**
*   Total Distance = Distance₁ + Distance₂
*   Total Distance = 150 + 60 = 210 miles

**Final Answer:**
The train traveled a total of **210 miles**.

g4-final-chain, turn 3

Prompt MD5 8f66b4c97988825bd8e7840aaf44357e; request MD5 b69a6e7dc9976befe1855af6ec920602; finish stop; generated 83; cached 474.

The seasons on Earth are caused by the tilt of the Earth's rotational axis relative to its orbital plane around the Sun. This axial tilt of approximately 23.5 degrees means that different hemispheres receive varying amounts of direct sunlight throughout the year as the Earth orbits. When a hemisphere is tilted toward the Sun, it experiences summer due to more direct and intense sunlight, while the opposite hemisphere experiences winter.

g4-final-chain, turn 4

Prompt MD5 8fe0ad36f61bcf4992cc9df81cdf3817; request MD5 71628f33d18c8e1079e9f0cc023a26bc; finish stop; generated 98; cached 583.

Elias had spent thirty years watching the gray waves crash against the jagged rocks, but today the tide brought something strange. Amidst the tangled kelp and driftwood lay a brass compass, its glass face cracked but its needle still spinning wildly. He picked it up, feeling a sudden, inexplicable warmth radiating from the cold metal in his calloused hand. As he turned it over, he realized the back was engraved with his own name and a date from next week.

g4-final-chain, turn 5

Prompt MD5 8bed8e2d056dc1d47dccae9d32dbecf4; request MD5 b56b913784cc2e00e8c079e4dd9fc0f3; finish stop; generated 77; cached 715.

1. Use descriptive and consistent naming conventions for variables, functions, and classes.
2. Keep functions small and focused on a single responsibility.
3. Write clear, concise comments and documentation for complex logic.
4. Avoid code duplication by extracting common patterns into reusable functions or modules.
5. Write and maintain unit tests to ensure code reliability and facilitate refactoring.

g4-final-a3b, turn 1

Prompt MD5 1d32df5f12c414d3e34c7b35b6611e6c; request MD5 c511ce1f15475b7f372a6ef247a87dce; finish stop; generated 124; cached 0.

The capital of France is **Paris**.

*   **River:** The **Seine** River runs through Paris.
*   **Famous Landmark:** The **Eiffel Tower** is one of the most iconic landmarks in the city.

**Historical Importance:**
Paris has been a major center for art, fashion, gastronomy, and culture for centuries. Historically, it was a pivotal hub during the Enlightenment and the French Revolution, which profoundly influenced the development of modern democracy, human rights, and secularism. Its influence on global politics, literature, and philosophy continues to resonate today.

Nonclaims / remaining G4 work

  • This proves this constructor's allocation-failure rollback/retry and sampled normal serving, not the complete G4 lifecycle matrix, numerical/state parity admission, full load-fault matrix, physical multi-GPU behavior or performance.
  • No parser/terminal rewrite: current beta already carries consuming finalization/staged terminal patterns.
  • Other constructors, including the separate Qwen35DecodeBatchState late-allocation ledger and broader cleanup-error handling, remain separate G4 obligations. This PR does not claim they are fixed.
  • No tracker acceptance fields or receipts are promoted; G5 and G6–G10 remain prerequisite-gated.

The rollback ledger stored DeviceBuffer::alias() views, which free_tensor
refuses to release. Stage real GpuTensor owners and publish them only after
every mandatory/optional allocation succeeds; unwind failures in reverse.

GPU fault regression on gfx1151/HIP7.2: warm54 allocations; fail the final
allocation and retry. Before:107 fresh allocations (53 buffers lost).
After:54, full warm-pool reuse. Default qwen35 lib tests191 passed/1 ignored;
the ignored GPU fault test was explicitly run and passed. Scoped clippy
completed with existing warnings; crate maps43/43, ratchets21/21 pass.

Rebuilt CLI/daemon, verified qwen3.6:27b DFlash factual+5-turn chain and
qwen3.6:35b-a3b AR factual:7 turns stop, no empty/runaway/attractor outputs.
Matched128-token DFlash request is byte-identical before/after (both hit
that explicit cap); separate512-token run completes at148 tokens.

Evidence scope: first independent G4 allocation-error fix, not G4 milestone
acceptance, numerical/state admission, physical multi-GPU, or performance.
@Kaden-Schutt
Kaden-Schutt merged commit af39905 into warpfront:beta Sep 9, 2026
6 of 7 checks passed
@Kaden-Schutt

Copy link
Copy Markdown
Collaborator

Landed in beta merge af39905 after independent review. Exact staged candidate24929bd6 on84fb395 passed the real gfx1201/HIP7.15 injected allocation54 failure+retry regression. Release serve_harness battery on SHA-pinned Qwen3.8 XT9f91556f + draftd0a74a23, q8/vmm,maxseq8192,DFlash on:5/5 stop,0 empty/runaway/attractor, decoded text inspected coherent. Daemon md5f512dc23f954b9f51353e1938f89e148. This is correctness evidence, not a performance claim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants