[furiosa-opt v0.9.0] INT8 contraction 처리량이 slice당 peak의 약 50%에서 멈춥니다 (packet 32와 64가 같은 시간)

# INT8 contraction 처리량이 slice당 peak의 약 50%에서 멈춥니다 (packet 32와 64가 같은 시간)

커스텀 커널에서 INT8 contraction을 많이 쓰는데, 처리량이 계산한 peak의 절반 근처에서 더 오르지 않아 여쭙습니다. 최소 재현 크레이트 `rngd-contraction-throughput-repro`를 첨부했습니다(관찰 2를 host launch wall로 재현). Furiosa Opt 0.9.0, RNGD 1칩에서 실측했습니다.

## 측정 방법

입력을 TRF에 한 번 올리고, 같은 contraction을 1번 하는 커널과 9번 하는 커널의 device time(trace) 차이를 8로 나눠 contraction 한 번의 시간을 구했습니다. 반복끼리 겹치지 않게 결과는 버퍼의 서로 다른 칸에 썼고, warmup 5회 뒤 30회 median입니다. K는 합산 길이, N은 slice당 출력 원소 수입니다.

```rust

ctx.main.begin(rhs.view())

.fetch::<Outer, Packet>().fetch_cast::<i8>().collect::<Outer2, Packet2>()

.contract_outer::<Outer, Packet, \_, \_, \_>(&trf)      // Packet: 한 번에 들어가는 K 조각(32 또는 64)

.contract_packet::<m!\[1\]>()

.contract_time::<Time>()

.contract_lane::<Time, m!\[X % 8 # 8\]>(LaneMode::Interleaved)

.vector_init().vector_intra_slice_tag(TagMode::Zero).vector_final()

.commit_trim::<..>().commit_view(..);

```

## 관찰 1: 시간이 K·N에 비례하고 처리량이 slice당 약 255 MAC/ns

| K | N | slice당 MAC | 시간 |

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

| 128 | 8,192 | 1.05 M | 4.70 µs |

| 256 | 4,096 | 1.05 M | 4.73 µs |

| 512 | 2,048 | 1.05 M | 4.73 µs |

| 256 | 8,192 | 2.10 M | 8.82 µs |

| 512 | 8,192 | 4.19 M | 17.04 µs |

| 1,024 | 8,192 | 8.39 M | 33.46 µs |

N = 8,192에서 t ≈ 0.62 µs + 32.1 ns·K로 맞습니다(K 64~1,024에서 오차 1% 안쪽). 기울기로 보면 slice당 약 255 MAC/ns입니다. INT8 512 TOPS를 slice 512개(PE 8개 × 64)로 나누면 slice당 약 500 MAC/ns라서, K를 길게 해도 peak의 약 50%가 상한입니다.

## 관찰 2: packet 폭을 32와 64로 바꿔도 시간이 같음

같은 K, N = 8,192에서 `contract_outer`의 packet 축만 32와 64로 바꿨습니다(나머지 K는 바깥 축으로 시간에 따라 누적).

| K | packet 32 | packet 64 |

| —: | —: | —: |

| 64 | 2.68 µs | 2.67 µs |

| 128 | 4.74 µs | 4.77 µs |

| 256 | 8.80 µs | 8.84 µs |

64개를 한 packet에 넣어도 원소당 처리량이 32개씩 넣을 때와 같았습니다. PE 클럭을 1 GHz로 보면 slice당 처리량은 lane 8개 × 사이클당 32 MAC와 맞습니다.

## 관찰 3: 고정 비용

contraction 하나에 K와 N에 상관없이 약 0.6 µs가 붙습니다. 또 K = 32(N = 8,192)는 2.33 µs로, 위 식(1.65 µs)보다 약 0.6 µs 더 걸립니다.

## 질문

1. INT8 contraction의 실제 처리량이 lane 하나당 사이클 32 MAC(slice당 256 MAC/cycle)인지 궁금합니다. 그렇다면 512 TOPS는 어떤 조건(데이터형, 클럭, 매핑)을 기준으로 한 값인지 알고 싶습니다.

2. 처리량이 아니라 저희 매핑의 문제라면, peak에 가깝게 쓰려면 packet·time·lane 축이나 LaneMode, TRF 배치를 어떻게 잡아야 하는지 알려 주시면 감사하겠습니다. 피연산자 공급(DM fetch, TRF 읽기) 쪽이 병목일 가능성도 있는지 궁금합니다.

3. packet 64가 packet 32 두 번과 같은 시간이 드는 것이 정상 동작인지 궁금합니다.

4. contraction 하나에 붙는 약 0.6 µs 고정 비용과, K = 32에서 더 붙는 약 0.6 µs가 무엇으로 이뤄지는지(명령 시작, 출력 drain 등) 알 수 있을까요?