Martin Fuller, Xbox Advanced Technology Group
Published May 13th 2016
The Xbox One GPU is comprised of two shader engines, each with six compute units (CUs).
Because the fastest instruction takes a SIMD four cycles to complete, and there are four SIMDs per CU, each CU can issue a vector ALU operation per cycle. Waves per SIMD is known as occupancy. Typically, engineers should aim for an occupancy of at least 3, preferably 4 or greater.
When a shader uses LDS, all waves in a threadgroup must execute on the same CU. Although each CU has 64 KB of LDS, we prohibit allocating more than 32 KB per threadgroup.
In the event that there is just one wave per threadgroup (<= 64 threads issued) and that group requires 32 KB of LDS, the CU is only able to run two waves concurrently, meaning that half the SIMDs are unused. This is a catastrophic performance issue.
| Max occupancy per SIMD | Max instructions per clock | Max SGPRs (align 8) |
Max VGPRs (align 4) |
|---|---|---|---|
| 1 | 1 | 104 | 256 |
| 2 | 2 | 104 | 128 |
| 3 | 3 | 104 | 84 |
| 4 | 4 | 104 | 64 |
| 5 | 5 | 96 | 48 |
| 6 | 5 | 80 | 40 |
| 7 | 5 | 72 | 36 |
| 8 | 5 | 64 | 32 |
| 9 | 5 | 56 | 28 |
| 10 | 5 | 48 | 24 |
LDS per threadgroup (KB, aligned to 256 bytes)
| Max occupancy per SIMD | Threadgroup = 1 wave |
Threadgroup = 2 waves |
Threadgroup = 4 waves |
|---|---|---|---|
| 1 | 16 | 32 | - |
| 2 | 8 | 16 | 32 |
| 3 | 5.25 | 10.5 | 21.25 |
| 4 | 4 | 8 | 16 |
| 5 | 3 | 6.25 | 12.75 |
| 6 | 2.5 | 5.25 | 10.5 |
| 7 | 2.25 | 4.5 | 9 |
| 8 | 2 | 4 | 8 |
| 9 | 1.75 | 3.5 | 7 |
| 10 | 1.5 | 3 | 6.25 |
The maximum LDS allocation in bytes for each occupancy tipping point is given by:
floor(((65536 / (maxWavesPerSIMD * 4)) * ThreadsPerGroup) / 256) * 256
LDS is arranged into 32 banks of 2 KB each. Each bank has a 32-bit wide bus, providing a maximum bandwidth of 128 bytes per cycle per CU. An LDS instruction is split into two operations, one for the low 32 threads and one for the high 32. For conflicts, we only need to consider one of these 32-bit wide accesses.
Of the 16-bit address, the most significant 9 bits select the address within the bank, the next 5 bits select the bank, and the least significant 2 bits select the byte within the 32-bit wide address. (However, currently we only expose 32-bit access.)
A bank conflict is generated when:
In the case of a bank conflict, the memory operation will be N times slower, where N is the highest number of threads accessing the same bank. For example:
The advice is therefore to avoid writing to LDS with strides that are a power of 2 .
There is no penalty if threads in the high 32 threads access the same banks as threads in the low 32 threads.
- Greater than zero of course
This paper provides a quick reference that you can use when optimizing for occupancy. While register allocation is relatively straight forward, effective LDS optimization is dependent not only on occupancy, but also on ensuring that waves do not generate bank conflicts.