AI・量子・未来技術編 / 第98話

第98話:イトは、64個の答えを一列で計算させた|GPUはなぜAI計算に向く?

ピコルート第98話。64個の行列出力を一列へ積み、今度はmemory読込で渋滞した。GPUの並列計算、matrix tile、shared memory、VRAMを追います。

GPU行列演算並列計算VRAM11分更新

第97話のGPU routeへ、掛け算の板が届いた。

input matrix Aは8×8。

weight matrix Bも8×8。

作るoutput matrix Cは8×8。

イトはCの左上から、答えを一つずつ一列のlaneへ並べた。

output cells                 64
active compute lane           1
multiply-accumulate steps   512
estimated finish tick        64
deadline tick                20
commit                        0

壁一面のlaneは空いている。

一列目だけが、64個の答えを抱えて赤くなった。

「同じ形に分けたのに、また一列へ積んでた」

一つのoutput cellは、rowとcolumnのdot product

matrix multiplication C = A × Bをsmall fixtureで追う。

Cの一つのcellを作るには、Aの一rowとBの一columnを同じ位置どうしで掛け、足し合わせる。

8×8なら、一cellにつき8回のmultiply-accumulateを進める。

C[row, col]
= A[row, 0] * B[0, col]
+ A[row, 1] * B[1, col]
+ ...
+ A[row, 7] * B[7, col]

input A / Bが計算中に変わらないなら、Cの別cellは別threadへ割り当てられる。

64 cellを64 threadへ分ける形を作れる。

neural networkのdense layerにも、inputとweightのdot productへbiasやactivationを組み合わせる処理がある。

ただしAI modelの仕事がmatrix multiplicationだけとは限らない。

normalization、activation、memory movement、control、前処理や後処理もある。

「掛け算があるから、どんなAIでもGPUなら速い」とは決めない。

threadを増やしたら、今度はdataが届かなかった

イトは64 laneを開くpreviewへ切り替えた。

各threadがoutput一cellを受け持つ。

compute boardは八tickで終わる表示になった。

でもfar memory shelfから、同じAのrowとBのcolumnをthreadごとに読み直している。

mapping                    one thread / output cell
output cells                                  64
global element reads                         1024
compute ready tick                              8
data ready tick                                34
observed finish tick                           34

laneは増えた。

材料待ちは増えた。

「計算する手が空いても、同じ数字を遠くから何度も運んでたら待つんだ」

matrixをtileへ分け、近くでreuseする

大きなmatrix全体を、すべて一度に近いmemoryへ置けるとは限らない。

そこでoutput Cをsmall tileへ分ける。

今回のclosed fixtureでは4×4 tileを使う。

Cには四つのoutput tileができる。

各thread blockが一つのoutput tileを受け持つ。

必要なA tileとB tileをglobal memoryから近いshared trayへ協力してloadする。

loadがそろう前に計算を始めないよう、block内でsynchronizeする。

tile内のthreadは、近いtrayの同じvalueをreuseする。

K方向の次tileをloadし、partial sumへ足す。

最後に完成したC cellをglobal memoryへwriteする。

output tiles                    4
tile size                     4x4
K phases per output tile        2
global element reads          256
output writes                  64

これは仕組みを見えるようにしたlab countだ。

real kernelのload数、cache hit、instruction、timeはdevice、library、data type、shape、implementationで変わる。

よくある誤解:VRAMが多いほど、同じ計算は必ず速い

VRAM capacityが大きければ、larger model、input、intermediate stateをdevice側へ置ける余地が増える。

capacity不足でworkloadが載らない、分割や移動が増える、という問題を避けられる場合がある。

でもcapacityは、dataが一秒にどれだけ動くかというbandwidthと同じではない。

access patternも別だ。

近いaddressをgroupでまとめて読むcoalesced accessなら、memory transactionを有効に使いやすい。

ばらばらのaddressを読むと、運んだ帯域の一部を使わずに捨てることがある。

shared memoryを使っても、bank conflictや不要なsynchronizationを増やせば別の待ちが生まれる。

discrete GPUではhost memoryとdevice memory間のtransferも調べる。

integrated GPUなどmemory構成が違うdeviceへ、同じ棚の比喩を固定しない。

見るのはcapacityだけではない。

fits in memory
host-device transfer
requested / effective bandwidth
access pattern
reuse
synchronization
end-to-end time

training preview終了まで残り17秒

output Cをreference CPU resultとexact compareする。

残り十七秒。

input A / Bはread-only。

deadlineはtick 20。

global element read budgetは320以下。

output 64 cellのmissing / duplicate / mismatchは0にする。

tile loadがそろわなければcomputeへ進まない。

イトはmatrix wallの前へ、三つのroute cardを置いた。

イト

A。output 64 cellを、一つのcompute laneで順番に作る

イト

B。64 threadへ分けるけど、AとBを各threadがglobal memoryから毎回読み直す

イト

C。4×4 output tileへ分け、blockでA / B tileをloadして近いtrayからreuseする

Aは正しいがdeadlineを越える。

Bはcompute laneを使えても、data readyが遅れる。

Cにはtile boundaryとsynchronizationの管理が増える。

それでもglobal readをbudget内へ収め、outputごとの対応を保てる。

イトがsingle-cell queueを閉じ、四つのoutput tileを開いた

イトは左手で、64 cellを一列へ流すred queue gateを閉じた。

naive reread leverにもdark lockを掛ける。

右手で、matrix wallを四つに分けるtile guardを開いた。

cyan A tileとamber B tileを、各blockのnear shared trayへそろえて置いた。

tile-ready lampを確認してから、compute gateを開く。

イトが行列の答えを一列へ送る赤いゲートと再読込レバーを閉じ、入力タイルを近い共有トレイへ置いて四つの出力タイルを開く
output cellを並列へ分けるだけでなく、A / B tileを近くへ一度運び、block内でreuseする。

Picoはglobal shelf、shared tray、output tileの境界だけを照らす。

gate、lever、input、tray、thread、outputには触れない。

64 cellがそろい、memory待ちが消えた

イトはfirst output tileだけをsingle-stepする。

output tile              top-left 4x4
threads active                       16
A tile loaded                        16
B tile loaded                        16
tile-ready                            1
partial output cells                 16

K方向のsecond phaseをloadする。

second A tile loaded                 16
second B tile loaded                 16
block synchronized                    1
completed output cells               16

残り三blockも同じmappingで進める。

completed output cells          64 / 64
global element reads                256
output writes                         64
observed finish tick                  14
reference mismatch                     0
input A / B changed                    0

read budget 320以下。

deadline tick 20より六tick早い。

イトは赤いqueue lampが消えるのを見届けた。

「laneを増やしただけじゃない。近くへ一度運んで、同じ数字を使い回した」

tileにもcostと境界がある

tileが大きすぎれば、shared memoryやregisterのresourceを使い切る。

小さすぎれば、loadとsynchronizationの回数が増える。

matrix sizeがtileの倍数でなければ、edgeのbounds checkやpaddingが要る。

別thread block間で自由に同じshared trayを使えるわけでもない。

highly tuned libraryやcompilerが適切なkernelを選ぶ場合もある。

だから「4×4が正解」と保存しない。

matrix shape and data type
tile / block mapping
memory layout and transfer
resource usage
correctness
measured end-to-end time

target deviceとreal workloadで測り直す。

同じ音声cardに、二つの行き先が開いた

matrix wallがWAIT_EVENTへ戻る。

ユイの端末から、短いvoice memoが届いた。

同じ音声を文字へ変えるinference requestだ。

raw voice location      device
small local model       available
larger cloud model      available
network                 unstable
privacy permission      undecided

イトは、端末内のsmall compute roomと、networkの先にあるcloud hallを同時に開きかけた。

でもraw voiceを送るかどうかと、どちらのmodelを使うかは、matrixの速さだけでは決められない。

片方はofflineでも動く。

片方はnetworkとremote serviceへ依存する。

イトは二つのsend gateを閉じたまま、voice memoの条件を先に並べた。

次回、ピコルート第99話。

イトは、同じ声を端末と雲へ同時送信しかけた|クラウドAIとオンデバイスAIは何が違う?

この物語を、解説で深める

第98話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

GPUはなぜAIの計算に向いているの?を読む