This article was written with the assumption that the reader has some understanding of the transformer architecture of LLMs with basic arithmetic skills. I also encourage the reader to have some understanding of model parameter counting. Read [2] as Chen, C. advised in her transformer arithmetic article [1].
Model inferencing for thousands of users and let alone millions or billions requires a high level of craftsmanship to get right.
Doing this efficiently requires optimizing both the runtime and infrastructure layers of your inferencing stack. Runtime optimization cares about writing efficient kernels for fast attention computation, KV reuse, kernel fusing to minimise GPU bubbles etc. The infrastructure layer borders around efficiently scheduling inferencing workloads across a cluster of GPUs, dealing with distributed systems tradeoffs like fault tolerance, correctness analysis with formal verification etc.
In this article, we want to understand how communication and compute boundedness impact inferencing speed.
LLM inferencing comprises of two math intensive stages - prefill and decode that require as much compute, memory/communication bandwidth as possible to minimize latency and increase throughput. Majority of the time consumed during inferencing happens during the decode stage due to the nature of how matmuls are calculated. Prefill matmul computation is embarrassingly parallel because there isn’t a need for tokens to be related to each other in such a way that makes semantic sense to the user. In decode, matmuls are computed in a sequential manner i.e on a token by token basis because each output token has to be a consequence of previous tokens in other to maintain attention. The most important problem in inferencing then becomes “how do we speed up the decode stage?”
There are bunch of variables that affect inferencing speed and we would touch on them:
A fine-balance configuration is needed between these variables in other to achieve maximum token per watt for your GPUs.
In a standard transformer, size of a token in bytes after one forward pass is equivalent to:
$$ 2~.~2~.~n_{layers}~.~n_{heads}~.~d_{head} \tag 1 $$
One of the “2” represents two bytes that’s required for an FP16 value while the second “2” represents the fact that k and v tokens are stored.
k and v projections:
$$ W_k, W_v \in \mathbb{R}^{d_{model} \times d_{model}} \tag 2 $$
token embedding is a 1-d vector from the embedding matrix:
$$ t \in \mathbb{R}^{1 \times d_{model}} \tag 3 $$