Skip to content

fix(qwen36 tier): keep the per-device block inside its buffer, and wake every waiter on shutdown - #1348

Closed
JustVugg wants to merge 1 commit into
devfrom
fix/tier-overrun-and-shutdown
Closed

JustVugg wants to merge 1 commit into
devfrom
fix/tier-overrun-and-shutdown

Conversation

@JustVugg

@JustVugg JustVugg commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Fixes #1339 and #1340, both reported by @crichalchemist while working on #1338. Neither comes from #1334 β€” I checked my diff: zero lines of mine in qt_issue, cv_take or is_x. They are pre-existing, surfaced by the attention that PR drew.

#1339 β€” the per-device block ran past its buffer

G.is_x was allocated 32*D in total β€” one device's worth β€” while the addressing assumed a block each at stride 8*D. With two or more GPUs, device 1 wrote from 8*D to 8*D + 32*D. Three hand-written numbers in three places (32, 8, 32), two of which disagreed.

qt_is_offset() and qt_is_floats() are now the single source: stride and capacity cannot drift apart because they are the same constant, and the buffer is sized by ndev.

#1340 β€” shutdown woke only half the waiters

th_stop is watched by two conditions and qt_shutdown signalled only cv. Anyone parked on cv_take β€” a full queue in enqueue_locked, or a group in flight in the uploader β€” never woke, and the pthread_join inside qt_shutdown hung. Now both are broadcast: stopping must not depend on which of the two waits happened to be in progress.

Negative controls, both bite

restored defect result
single signal on shutdown the test hangs (timeout) β€” which is the real failure, so the hang is the diagnosis
8*D stride two assertions fail, naming the defect

My first test for #1339 did not bite. It recomputed the arithmetic from the constants instead of calling the code, so it stayed green with the wrong stride restored β€” it verified itself. That is why the addressing was extracted into functions: so the test calls what qt_issue calls. The negative control is the only reason I found out.

…ke every waiter on shutdown

Due difetti preesistenti trovati da crichalchemist mentre lavorava a
#1338. Nessuno dei due viene da #1334: sono nel codice che quella PR ha
solo portato all'attenzione.

#1339 -- G.is_x era allocato 32*D in tutto, cioe' la capienza di UN
device, mentre l'indirizzamento assumeva un blocco per ciascuno con
passo 8*D. Con due o piu' GPU il device 1 scriveva da 8*D fino a
8*D+32*D, oltre la fine. Tre numeri scritti a mano in tre punti (32, 8,
32) e due non erano d'accordo.

Ora esistono qt_is_offset e qt_is_floats e sono l'unica fonte: il passo
e la capienza non possono piu' divergere perche' sono la stessa
costante, e il buffer si dimensiona su ndev.

#1340 -- th_stop lo guardano DUE condizioni, e qt_shutdown segnalava
solo cv. Chi era fermo su cv_take -- coda piena in enqueue_locked, o un
gruppo in volo nell'uploader -- non si svegliava, e il pthread_join
dentro qt_shutdown restava appeso. Ora broadcast su entrambe: fermarsi
non deve dipendere da quale delle due attese e' capitata.

Controlli negativi, entrambi mordono:
 - rimesso il signal singolo, il test SI APPENDE (timeout) -- che e'
   esattamente il guasto reale, quindi il blocco e' la diagnosi
 - rimesso il passo 8*D, due asserzioni falliscono nominando il difetto

La prima versione del test su #1339 NON mordeva: ricalcolava
l'aritmetica con le costanti invece di chiamare il codice, quindi
restava verde col passo sbagliato rimesso -- verificava se stessa. E'
il motivo per cui l'indirizzamento e' stato estratto in funzione: cosi'
il test chiama cio' che chiama qt_issue.
@JustVugg

JustVugg commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Closing in favour of #1344, which was opened 16 hours before this and covers all three issues.

I did not check the PR list before starting β€” I read the issue, replied "send your fix", and then wrote my own anyway. That is duplicated effort I caused, and the apology is owed to @crichalchemist rather than to the queue.

#1344 is also better where it counts most. I wrote in #1347 that the caller-side violation could not be caught without a GPU, and #1344's test_qwen36_tier_int8_engine.c catches it: it includes qwen36.c, builds an in-memory int8 model with no container behind it, and drives tier_warmstart() against the shared fake backend. The thing I declared out of reach was in fact reachable, and the fake-backend header factored out of my test file is what made it reachable.

One idea from here is worth carrying over, and I have left it as a review comment on #1344 rather than a competing PR.

@JustVugg JustVugg closed this Sep 5, 2026
@JustVugg
JustVugg deleted the fix/tier-overrun-and-shutdown branch September 24, 2026 22:28
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.

1 participant