# \[furiosa-opt 0.4\] TDMA layout broadcast의 cycle 증가와 일부 mapping의 correctness 문제

**URL:** <https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435>\
**Category:** 한국어 (Korean)\
**Tags:** sdk, rngd\
**Created:** [July 24, 2026, 4:37am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435 "2026-07-24T04:37:15Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![rpdlaehfdl21](https://avatars.discourse-cdn.com/v4/letter/r/ba8739/32.png) [@rpdlaehfdl21](https://forums.furiosa.ai/u/rpdlaehfdl21)\
**Post date:** [July 24, 2026, 4:37am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435/1 "2026-07-24T04:37:15Z")

</div>

안녕하세요. GEMM 커널을 구현하다 보니 추가적인 궁금증이 생겨서 질문드립니다.

RNGD에서 일반 row-major GEMM 입력을 여러 slice로 배치하는 과정에서 TDMA 비용이 크게 늘어나는 현상을 확인했습니다. 단순한 연속 HBM→DM 복사는 문서에 설명된 startup latency 범위로 보이지만, compact source를 여러 slice로 broadcast하는 mapping에서는 source가 작아질수록 cycle이 오히려 증가하거나 결과가 틀리는 경우가 있었습니다.

- DMA Engine: [DMA Engine - Programming Tensor Contraction Processors](https://developer.furiosa.ai/furiosa-opt/book/moving-tensors/dma-engine.html)

- Memory Performance: [Memory Performance - Programming Tensor Contraction Processors](https://developer.furiosa.ai/furiosa-opt/book/moving-tensors/memory-performance.html)

**## 환경**

```text

Host: Ubuntu, Linux 6.8.0-124-generic, x86\_64

Device: RNGD npu5

Device firmware reported by furiosa-smi: 2026.2.0 (a5db283)

Installed furiosa-driver-rngd: 2026.3.0

Installed furiosa-firmware-image-rngd: 2026.3.0

rustc: 1.97.0-nightly (2026-04-30), selected by nightly-2026-05-01

furiosa-opt-std: 0.4.0

```

**## 최소 재현**

모든 kernel은 HBM 입력을 같은 32 KiB DM tensor로 옮긴 뒤, correctness 확인을 위해 같은 packed HBM layout으로 다시 저장합니다.

```rust

axes![ProbePe = 8, ProbeLocalSlice = 64, ProbeValue = 32];

type ProbeCluster = m![ProbePe / 4];

type ProbeSlice = m![ProbePe % 4, ProbeLocalSlice];

type ProbeElement = m![ProbeValue];

type FullLayout = m![ProbePe, ProbeLocalSlice, ProbeValue];

type CompactF2Layout = m![ProbePe, ProbeLocalSlice / 2, ProbeValue];

type CompactF8Layout = m![ProbePe, ProbeLocalSlice / 8, ProbeValue];

type CompactF64Layout = m![ProbePe, ProbeLocalSlice / 64, ProbeValue];

let loaded: DmTensor\<bf16, m![1], ProbeCluster, ProbeSlice, ProbeElement\> =

```
input.to_dm_at(&mut ctx.tdma, 0);

```

loaded.to\_hbm(&mut ctx.tdma)

```

`CompactF2`, `CompactF8`, `CompactF64`의 기대 결과는 각 source group을 각각 2개, 8개, 64개 local slice에 반복한 값입니다.

컴파일 명령:

```bash

FURIOSA\_OPT\_OUT\_DIR=/tmp/tdma-f64 \

RUSTFLAGS=‘–cfg=feature=“tdma-read-probes”’ \

cargo furiosa-opt compile rngd\_tdma\_compact\_f64 \

–exact -p rngd-bf16-matmul \

–dump-visa /tmp/tdma-f64.visa \

–dump-schedule /tmp/tdma-f64.schedule.json \

–dump-summary /tmp/tdma-f64-summary

```

다른 variant도 kernel 이름만 바꿔 같은 방식으로 컴파일했습니다.

**## 결과**

| HBM→DM mapping | HBM source | DM destination | DmaLoad | DmaStore | 결과 |

|—|—:|—:|—:|—:|—|

| contiguous | 32 KiB | 32 KiB | 707 cycles | 8,457 cycles | 정확 |

| compact ×2 | 16 KiB | 32 KiB | 702 cycles | 8,457 cycles | 8,191개 불일치 |

| compact ×8 | 4 KiB | 32 KiB | 733 cycles | 8,457 cycles | 14,329개 불일치 |

| compact ×64 | 512 B | 32 KiB | 5,020 cycles | 8,457 cycles | 정확 |

| prepacked | 32 KiB | 32 KiB | 707 cycles | 8,457 cycles | 정확 |

Compiler cycle은 현재 0.4 toolchain에서 다시 생성했습니다. Correctness와 host wall은 같은 환경의 물리 `npu5`에서 측정했습니다. Host wall median은 129.002–152.733 µs였지만, 이 값에는 kernel launch와 runtime synchronization이 포함되어 있으므로 TDMA engine 시간으로 해석하지 않았습니다.

32 KiB contiguous load가 707 cycles인 것은 Book에 설명된 command당 약 500-cycle startup latency를 고려하면 특별히 이상하지 않아 보입니다. 확인이 필요한 부분은 세 가지입니다.

1. 512 B source를 32 KiB DM으로 배치하는 compact ×64 load가 contiguous보다 7.10배 긴 5,020 cycles입니다.

2. compact ×2와 ×8은 compiler가 오류 없이 EDF/vISA를 생성하지만 물리 NPU 결과가 틀립니다. 반면 같은 방식의 compact ×64는 정확합니다.

3. 모든 variant에서 32 KiB DM→HBM store가 8,457 cycles로 동일하며, contiguous HBM→DM load보다 약 12배 깁니다.

compact ×64의 LIR에는 `DmaLoad` 한 개가 생성됩니다. Source 쪽 반복 축의 stride는 0이고 destination은 64개 slice로 전개됩니다. Schedule만으로는 5,020 cycles가 하나의 command 내부 직렬화 때문인지, 내부 command 분할이나 HBM/DMN interleaving 문제 때문인지 확인하기 어렵습니다.

실제 4K GEMM에서도 비슷한 결과가 나왔습니다. Compact 입력 variant의 compiler estimated I/O는 203,085 cycles, prepacked variant는 58,262 cycles였습니다. Prepacked variant가 compiler 보고 기준 DRAM data를 16.25배 더 사용했는데도 I/O estimate는 compact의 28.7%였습니다. 출력도 일반 row-major store는 145,022 cycles였지만 tiled packed store는 2,167 cycles, reducer packed store는 1,304 cycles였습니다.

**## 확인 부탁드리는 내용**

1. `ProbeLocalSlice / N` 형태의 HBM mapping을 전체 `ProbeLocalSlice` DM mapping으로 옮기는 것이 지원되는 broadcast 표현인지 확인 부탁드립니다.

2. 지원되는 표현이라면 compact ×2와 ×8이 compile-time 진단 없이 잘못된 값을 만드는 이유를 확인해 주실 수 있을까요?

3. compact ×64의 5,020-cycle DmaLoad는 기대되는 비용인가요? 한 개의 LIR `DmaCommand`가 내부적으로 어떻게 분할되고 직렬화되는지 확인하는 방법도 알려주시면 감사하겠습니다.

4. 32 KiB DmaStore의 8,457 cycles는 64-byte packet에 대한 HBM partial-write/RMW 비용 때문인가요? Compiler가 연속 packet을 256-byte 단위로 합치도록 mapping이나 stream을 지정하는 권장 방법이 있다면 안내 부탁드립니다.

5. Compiler artifact에서 실제 packet size, DMA command 수, homogeneous/heterogeneous aggregate 여부, HBM channel 및 stack 사용, row conflict, RMW 횟수를 확인하는 방법이 있을까요?

6. 일반 row-major GEMM 입력과 출력을 별도 prepacking 없이 효율적으로 HBM↔DM에 배치하는 권장 mapping 예제가 있다면 공유 부탁드립니다.

필요하시면 최소 재현 프로젝트를 비롯해 EDF, vISA, LIR, schedule JSON, 물리 NPU 측정 결과 JSON을 제공하겠습니다.

---

<div class="post-metadata">

**Author:** ![jeongmin.park](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jeongmin.park](https://forums.furiosa.ai/u/jeongmin.park)\
**Post date:** [July 24, 2026, 5:05am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435/2 "2026-07-24T05:05:19Z")

</div>

안녕하세요, 최소 재현을 위한 vISA 코드를 제공해주실 수 있을까요?

---

<div class="post-metadata">

**Author:** ![rpdlaehfdl21](https://avatars.discourse-cdn.com/v4/letter/r/ba8739/32.png) [@rpdlaehfdl21](https://forums.furiosa.ai/u/rpdlaehfdl21)\
**Post date:** [July 24, 2026, 5:55am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435/3 "2026-07-24T05:55:18Z")

</div>

> **[Download tdma-layout-broadcast-repro.zip | LimeWire](https://limewire.com/d/Jdb3W?lmwr-ftv=1)**
>
> Download tdma-layout-broadcast-repro.zip on LimeWire

안녕하세요. 말씀하신 최소 재현 자료를 정리해 첨부합니다.  
아래 경로는 `tdma-layout-broadcast-repro/\` 폴더를 기준으로 한 상대경로입니다.

```text

tdma-layout-broadcast-repro/

├── Cargo.toml

├── Cargo.lock

├── rust-toolchain.toml

├── README.md

├── src/

│ └── lib.rs

├── generated/

│ ├── contiguous.visa

│ ├── contiguous.schedule.json

│ ├── compact\_f2.visa

│ ├── compact\_f2.schedule.json

│ ├── compact\_f8.visa

│ ├── compact\_f8.schedule.json

│ ├── compact\_f64.visa

│ └── compact\_f64.schedule.json

└── evidence/

```
├── physical-npu-result-2026-07-23.json

└── physical-npu-result-2026-07-24.json

```

```

재현 코드 전체는 `src/lib.rs`에 있습니다. compiler가 생성한 vISA는 `generated/*.visa`, 각 명령의 예상 cycle은 `generated/*.schedule.json`에서 확인할 수 있습니다. 물리 NPU에서 값이 맞았는지 확인한 원본 결과는 `evidence/physical-npu-result-2026-07-24.json`입니다. `2026-07-23` 파일은 같은 오답이 반복되는지 비교하려고 함께 넣었습니다.

여기서 `compact`는 host에서 데이터를 미리 복제해 둔 방식이 아닙니다. HBM에는 여러 slice가 함께 사용할 값 한 벌만 저장하고, TDMA가 그 값을 2개, 8개 또는 64개 local slice로 복제하도록 mapping했습니다. 이 때문에 HBM 입력은 각각 16 KiB, 4 KiB, 512 B로 줄지만 DM에는 모두 32 KiB가 배치됩니다. 반대로 `prepacked`는 host가 slice별 값을 미리 복제해 HBM에 32 KiB 전체를 저장하며, TDMA는 이를 1:1로 옮깁니다.

재현 코드는 복제 없는 1:1 대조군 `contiguous`와 `compact_f2`, `compact_f8`, `compact_f64`로 구성했습니다. 네 커널 모두 HBM에서 DM으로 값을 옮긴 뒤 같은 32 KiB 결과를 HBM에 다시 저장합니다. 연산은 넣지 않아 TDMA layout 차이만 남습니다.

같은 `furiosa-opt 0.4` 환경에서 첨부 소스를 다시 컴파일했으며, 결과는 아래와 같습니다.

| kernel | HBM 입력 | DM 결과 | DmaLoad | DmaStore |

|—|—:|—:|—:|—😑

| `contiguous` | 32 KiB | 32 KiB | 707 cycles | 8,457 cycles |

| `compact_f2` | 16 KiB | 32 KiB | 702 cycles | 8,457 cycles |

| `compact_f8` | 4 KiB | 32 KiB | 733 cycles | 8,457 cycles |

| `compact_f64` | 512 B | 32 KiB | 5,020 cycles | 8,457 cycles |

생성된 vISA에서 compact 입력의 반복 간격은 각각 2, 8, 64로 나타납니다. 목적지는 64개 local slice 전체입니다. compiler는 각 커널을 `DmaLoad` 하나와 `DmaStore` 하나로 lowering했습니다. schedule의 cycle도 표와 같았습니다.

별도 JSON에는 2026-07-24에 `npu5`에서 다시 측정한 원본 결과를 담았습니다. warmup 2회 후 5회 측정했으며, `contiguous`와 `compact_f64`의 결과는 정확했습니다. `compact_f2`에서는 8,191개, `compact_f8`에서는 14,329개 값이 기대값과 달랐습니다. 두 경우 모두 첫 불일치는 flat index 33이었고, 실제값은 0.0, 기대값은 1.0이었습니다. 2026-07-23 측정과 오답 위치 및 개수가 같습니다.

해당 mapping은 compiler에서 오류 없이 처리됐으며 vISA와 schedule도 생성됐습니다. schedule에 기록된 `compact_f64`의 DmaLoad는 5,020 cycles입니다. `compact_f2`와 `compact_f8`의 오답은 2026-07-24 물리 NPU 실행 결과에서 확인했습니다. 표의 DmaLoad와 DmaStore cycle은 compiler 추정치이고, JSON의 host wall time에는 kernel launch와 runtime 비용이 포함됩니다.

아래 네 가지를 확인해 주시면 감사하겠습니다.

1. `ProbeLocalSlice / N` 입력을 전체 `ProbeLocalSlice` DM layout으로 옮기는 표현이 공식적으로 지원되는 broadcast mapping인지 확인 부탁드립니다.

2. 지원되는 mapping이라면 `compact_f2`와 `compact_f8`이 compile-time 진단 없이 잘못된 결과를 내는 원인을 확인해 주실 수 있을까요?

3. 512 B를 32 KiB DM layout으로 복제하는 `compact_f64`의 DmaLoad가 5,020 cycles로 산정되는 것이 예상된 동작인지 알고 싶습니다. 이는 32 KiB를 그대로 읽는 `contiguous`의 707 cycles보다 7.10배 큽니다.

4. 모든 경우에 32 KiB DmaStore가 8,457 cycles로 동일하게 산정되는 이유도 함께 설명해 주시면 감사하겠습니다.

필요하시면 당시 사용한 전체 host 실행 harness와 EDF/LIR도 추가로 전달드리겠습니다.

---

<div class="post-metadata">

**Author:** ![jeongmin.park](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jeongmin.park](https://forums.furiosa.ai/u/jeongmin.park)\
**Post date:** [July 29, 2026, 9:31am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435/4 "2026-07-29T09:31:25Z")

</div>

안녕하세요. 확인이 늦어 죄송합니다.

첨부해 주신 LimeWire 링크가 아직 만료된 것으로 표시되지는 않지만, 현재 다운로드가 되지 않아 재현 자료를 확인하지 못하고 있습니다. 번거로우시겠지만 기존 `tdma-layout-broadcast-repro.zip` 전체를 다시 업로드해 주실 수 있을까요?

가능하시다면 이전에 말씀해 주신 host 실행 harness와 EDF/LIR도 함께 포함해 주시면 원인 분석에 도움이 될 것 같습니다. 다만 우선은 기존 zip만 다시 올려 주셔도 괜찮습니다.

---

<div class="post-metadata">

**Author:** ![rpdlaehfdl21](https://avatars.discourse-cdn.com/v4/letter/r/ba8739/32.png) [@rpdlaehfdl21](https://forums.furiosa.ai/u/rpdlaehfdl21)\
**Post date:** [July 30, 2026, 6:51am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435/5 "2026-07-30T06:51:10Z")

</div>

안녕하세요. 기존 zip 파일 재업로드합니다. opt 0.4.0 버전 기준 발생했던 문제점이라 해당 버전의 코드인 점 양해 부탁드려요.

> **[tdma-layout-broadcast-repro.zip](https://drive.google.com/file/d/1RcC9cU-7aAl1HMXwHzL-h5nyNGyGF3yv/view)**
>
> Google Drive file.

---

<div class="post-metadata">

**Author:** ![jeongmin.park](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jeongmin.park](https://forums.furiosa.ai/u/jeongmin.park)\
**Post date:** [August 3, 2026, 7:19am UTC](https://forums.furiosa.ai/t/furiosa-opt-0-4-tdma-layout-broadcast-cycle-mapping-correctness/435/6 "2026-08-03T07:19:59Z")

</div>

> 1. `ProbeLocalSlice / N` 입력을 전체 `ProbeLocalSlice` DM layout으로 옮기는 표현이 공식적으로 지원되는 broadcast mapping인지 확인 부탁드립니다.  
> 2. 지원되는 mapping이라면 `compact_f2`와 `compact_f8`이 compile-time 진단 없이 잘못된 결과를 내는 원인을 확인해 주실 수 있을까요?

말씀대로 input: m![A / 2] → output: m![A]와 같은 경우에 A % 2가 broadcast로 처리되지 않고있는 문제가 있었습니다. 일단은 우회책으로, input: m![A / 2], output: m![A / 2, 2] 로 설정하고, reshape으로 output을 m![A] 로 만드는 방식으로 원하는 기능을 이룰 수는 있을 듯 합니다. switch engine 및 dma에서, 이러한 경우를 broadcast로 취급하는 fix를 진행하는 중입니다.

> 3. 512 B를 32 KiB DM layout으로 복제하는 `compact_f64`의 DmaLoad가 5,020 cycles로 산정되는 것이 예상된 동작인지 알고 싶습니다. 이는 32 KiB를 그대로 읽는 `contiguous`의 707 cycles보다 7.10배 큽니다.

HBM의 memory geometry는 256B 단위의 channel이 총 32개 존재하고, channel당 48GB/s의 bandwidth를 가져 총 1.5TB/s의 bandwidth를 가집니다. 데이터 크기가 너무 작아서, 전체 channel을 다 활용하지 못하면서 발생한 문제로 보입니다. 관련해서, 더 자세하게 cycle estimation을 할 수 있는 cycle 모델을 문서에 업데이트할 예정입니다.

> 4. 모든 경우에 32 KiB DmaStore가 8,457 cycles로 동일하게 산정되는 이유도 함께 설명해 주시면 감사하겠습니다.

8개 DMA Engine이 32 KiB를 나누므로 engine당 4 KiB이고, 한 번에 쓰는 양은 Element의 64 B입니다. DM slice는 각자 주소 공간이라 packet이 slice 경계를 넘지 못하므로, HBM에서 이웃 slice의 행이 한 256 B 안에 붙어 있어도 write는 slice마다 따로 나갑니다. 결과가 engine당 64회입니다.

write 하나의 비용은 두 층으로 나뉩니다.

정렬된 write 하나가 2.4 cycle입니다. HBM 1.5 TB/s를 8개 engine이 나눠 쓰므로 engine당 몫이 약 190 B/cycle이고(실장비 5.2 Gbps 기준으로는 170 B/cycle), 256 B transaction 하나가 1.4~1.5 cycle입니다. 여기에 주소 패턴 penalty가 곱해져 실측 2.4가 됩니다. 32 KiB짜리 전송은 32개 channel을 다 채우지 못해 그 penalty를 받습니다. 여기에, 256B에 정렬되지 않은 64B write이므로, 약 50배의 RMW 페널티가 곱해집니다.

자세한 estimation 수식은 이후에 문서에 업데이트 예정입니다.
