Skip to content

perf(osar): 콜렉티브 위상별 사이클 계측 — 32ms 를 잔차가 아니라 실측으로 가른다 - #99

Merged
choiceoh merged 1 commit into
mainfrom
perf/osar-phase-timers
Aug 31, 2026
Merged

perf(osar): 콜렉티브 위상별 사이클 계측 — 32ms 를 잔차가 아니라 실측으로 가른다#99
choiceoh merged 1 commit into
mainfrom
perf/osar-phase-timers

Conversation

@choiceoh

@choiceoh choiceoh commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

스텝 고정비 42.0 ms 중 dense 가중치 9.9 ms 를 뺀 32.1 ms 가 아직 미귀속이다. 지금까지 이 값에 붙은 설명은 전부 잔차 귀속이었고 세 번 뒤집혔다:

가설 근거 결말
커널 개수 (15.4 µs/커널) 32.1 ms 를 전부 커널 수로 나눈 상한 #89 실측 5.4 µs/커널 — 1/3
호스트 병목 워커 CPU 114% GPU 사용률 93% 로 기각
콜렉티브 대기 사용률 93% + 전력 15 W 평평 간접 증거뿐

직접 재는 편이 빠르다.

Changes

  • Ctrlpad[8] → 위상 누적기 5 개 + pad[3]. 총 64 B 그대로tx/rx 오프셋이 안 변하고 피어의 rx_base/rxf_base 가 유효하다. 단조 증가·리셋 없음 — done_ctr 과 같은 이유로 cudagraph 재생이 리셋을 못 본다
  • k_oneshotclock64() 를 5 지점에서 읽어 guard / copy / wait / reduce 로 가른다. 기록은 블록 0 스레드 0 만 — perf(osar): 페이로드를 안 가진 블록은 두 스핀을 건너뛴다 — 디코드에서 피어 폴러 256→16 #93 이후 블록 0 은 항상 owns 라 모든 위상을 걷는 유일하게 대표성 있는 표본이다. 콜렉티브당 한 스레드의 store 5 회
  • 호스트 리더는 프록시 스레드에 둔다. 시임에 두면 안 된다 — 풀 디코드 cudagraph 아래서 파이썬 진입점은 캡처 때 한 번 돌고 다시는 안 돈다. 프록시는 부팅 내내 콜렉티브마다 도는 유일한 호스트 코드다. 랭크 0 만, 10 초마다, 직전 보고 이후의 델타로 (느린 워밍이 정상 상태를 뭉개지 않게)
[osar] phase us/collective (n=NNNN): guard=.. copy=.. wait=.. reduce=.. | total=..  @1592 MHz assumed

무엇을 가르나

104 콜렉티브/step 이므로 total × 104 가 곧 AR 이 스텝에서 차지하는 몫이다.

  • wait 이 지배적 → 32 ms 는 콜렉티브 지연. 수리는 커널 개수가 아니라 대기 방식(스핀 백오프·폴링 축소·프록시 게시 경로)
  • 넷 다 작음 → AR 은 무죄. 32 ms 는 드래프터·elementwise·MoE 글루 쪽이고 이 축은 닫힌다
  • guard 가 큼 → 링(RING=4) 이 좁아 생산자가 막힌다. RING 을 키우는 게 답

Verification

  • tests/test_logic.py 669 검사 통과
  • Ctrl 크기 불변 확인 (5×8 + 3×8 = 옛 pad[8] 64 B)
  • 절대 µs 는 SM 클럭 1592 MHz 가정만큼만 정확하다(nvidia-smi clocks.sm 이 부하 전 구간 평평). 다만 네 위상의 비는 그 가정에 전혀 의존하지 않고, 알고 싶은 것이 그 비다
  • ⚠️ 컴파일 미검증 — 지금 플릿이 fix(glm53): fp8 dense 사본을 적재가 끝난 뒤에 뜬다 — 이른 사본이 그대로 서빙됐다 #98 재시험 중이라 nvcc 를 돌리면 벤치가 오염된다. 컴파일 검사 통과 후에만 부팅할 것 (실패한 부팅이 13 분)

성능 변경이 아니라 계측기다.

🤖 Generated with Claude Code


Note

Low Risk
Measurement-only changes with preserved Ctrl size and offsets; minor extra work on one GPU thread and periodic stderr logging on rank 0.

Overview
Adds instrumentation to split each one-shot AllReduce collective into guard / copy / wait / reduce time using clock64(), aimed at attributing unexplained per-step overhead instead of inferring it from residuals.

The Ctrl layout reuses the old pad[8] bytes as five monotonic accumulators plus pad[3], so tx/rx offsets and peer rx_base/rxf_base stay unchanged. k_oneshot records phase deltas only from block 0, thread 0 (the block that always runs every phase).

The proxy thread on rank 0 prints delta-based µs/collective every 10s to stderr (not the Python shim, so metrics still update under full CUDA graph replay). Absolute µs assume 1592 MHz SM clock; phase ratios do not depend on that assumption.

Reviewed by Cursor Bugbot for commit 1b19ca8. Bugbot is set up for automated code reviews on this repo. Configure here.

스텝 고정비 42.0ms 중 dense 가중치 9.9ms 를 뺀 32.1ms 가 아직 미귀속이다.
지금까지 이 값에 붙은 설명은 전부 잔차 귀속이었고 세 번 뒤집혔다: 커널 개수
가설(상한 15.4us/커널)은 실측 5.4us/커널 로 1/3 이 됐고, 호스트 병목 가설은
GPU 사용률 93% 로 기각됐다. 지금 서 있는 가설(콜렉티브 대기)도 사용률 93% +
전력 15W 평평이라는 간접 증거뿐이다.

직접 재는 편이 빠르다.

- Ctrl 의 pad[8] 을 위상 누적기 5 개 + pad[3] 로 바꾼다. 총 64B 그대로라
  tx/rx 오프셋이 안 변하고 피어의 rx_base/rxf_base 가 유효하다. 단조 증가이며
  리셋하지 않는다 -- done_ctr 과 같은 이유로 cudagraph 재생이 리셋을 못 본다.
- k_oneshot 이 clock64() 를 5 지점에서 읽어 guard / copy / wait / reduce 로
  가른다. 기록은 블록 0 스레드 0 만 한다. #93 이후 블록 0 은 항상 owns 라 모든
  위상을 걷는 유일하게 대표성 있는 표본이다. 콜렉티브당 한 스레드의 store 5 회.
- 호스트 리더는 프록시 스레드에 둔다. 시임에 두면 안 된다 -- 풀 디코드
  cudagraph 아래서 파이썬 진입점은 캡처 때 한 번 돌고 다시는 안 돌지만 커널은
  재생마다 누적한다. 프록시는 부팅 내내 콜렉티브마다 도는 유일한 호스트 코드다.
  랭크 0 만, 10 초마다, 직전 보고 이후의 델타로 찍는다(느린 워밍이 정상 상태를
  뭉개지 않게).

절대 us 는 SM 클럭 1592MHz 가정만큼만 정확하다(nvidia-smi clocks.sm 이 부하
전 구간에서 평평했다). 다만 네 위상의 비는 그 가정에 전혀 의존하지 않고,
우리가 알고 싶은 것이 바로 그 비다.

성능 변경이 아니라 계측기다. 컴파일 검사 없이 부팅하지 말 것 -- 실패한 부팅이
13 분이다.
Copilot AI lite review requested due to automatic review settings August 31, 2026 06:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cursor

cursor Bot commented Aug 31, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d3dddf40-922c-4bcb-9d11-4d001237ecef)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T06:16:29.015857Z 1b19ca8 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@choiceoh
choiceoh merged commit cfcf6a2 into main Aug 31, 2026
3 checks passed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a non-blocking comment; no approval and no reviewers assigned. Cursor Bugbot was present but finished as skipped (usage/spend limit), so this run did not approve. Human review is needed before treating the change as signed off.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

choiceoh added a commit that referenced this pull request Aug 31, 2026
가정: 미귀속 32ms 는 콜렉티브 지연이다. #99 의 위상 계측이 이 가정을 곧 확정
하거나 기각하지만, 가정이 맞다면 손댈 곳은 커널 개수가 아니라 대기 방식이다.

두 스핀 루프가 읽는 플래그는 전부 피어가 쓴다 -- ack_seq 는 프록시가, rxf 는
피어 NIC 가 RDMA 로. 그 플래그를 최고 속도로 읽으면 그 쓰기가 가시화되는 바로
그 경로에 트래픽을 계속 얹는 셈이다. 기다리는 대상과 경쟁한다.

- 백오프: SPIN_HOT(8) 회는 그대로 뜨겁게 돌고(짧은 대기는 대가를 안 낸다),
  이후 __nanosleep 을 128ns 에서 두 배씩 4096ns 상한까지. 상한이 이 변경이
  더할 수 있는 지연을 묶는다 -- 콜렉티브당 최악 4us 이고, 의심하는 대기는
  그보다 두 자릿수 크다.
- 링 가드에서 tx_seq 를 루프 밖으로. 이 런치는 아직 게시 전이고 같은 스트림의
  직전 런치는 이미 회수됐으므로 스핀 중 움직일 수 없다. 재독할 이유가 있는
  것은 프록시가 올리는 ack_seq 뿐이라 루프 트래픽이 절반이 된다.
- 피어 대기에서 이미 도착한 플래그를 다시 읽지 않는다. 옛 형태는 매 패스마다
  세 개를 전부 재평가했다 -- 답이 바뀔 수 없는 것까지. 도착 여부를 기억하고
  아직 없는 첫 번째만 읽는다. 단축평가 모양은 그대로다.

정확성은 안 건드린다. 대기 조건도, 펜스 위치도, 카운터 불변식도 그대로이고
바뀐 것은 "얼마나 자주, 무엇을 읽는가" 뿐이다.

측정 순서 주의: #99(계측만) 부팅으로 위상 분해를 먼저 받고 그 다음 이걸 얹어야
한다. 둘을 한 부팅에 섞으면 wait 이 줄어든 것이 이 변경 덕인지 원래 그랬는지
못 가른다.

tests/test_logic.py 669 검사 통과. 컴파일 미검증 -- 부팅 전 컴파일 검사 필수.

Co-authored-by: choiceoh <astra7471@gmail.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b19ca82bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Peer wait: rxf is only ever written by the peers' NICs, never by a block
// of this kernel -- same independence argument as the guard above. Fence
// stays where it always was: after the wait, before reading peer data.
long long t2 = timer ? clock64() : 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Start peer-wait timing after grid-wide publication

With the fixed 256-block launch, whenever block 0 performs its atomic before the last block, this timestamp is recorded before tx_seq is published. Peers cannot update rxf until the proxy observes that publication, so t3 - t2, labeled as peer wait, necessarily includes the time for the remaining local blocks to run and for the last block to publish. A slow local grid or completion counter will therefore be misreported as collective/RDMA latency, invalidating the phase diagnosis; establish the wait boundary at actual publication or account for local completion separately.

Useful? React with 👍 / 👎.

// is the only host code that runs per collective for the life of the boot.
// Rank 0 only, every REPORT_SEC seconds, deltas since the last report so a
// slow warmup does not smear the steady state.
if (g_rank == 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Measure every rank before attributing collective latency

Restricting reports to rank 0 observes only the three inbound paths terminating at rank 0, while the distributed step is gated by the slowest rank and path. For example, if rank 0's proxy or outbound links are the bottleneck, the other ranks wait for rank 0 but rank 0 can report a small wait, leading this diagnostic to clear the collective even though it determines step latency. Emit per-rank measurements or aggregate the maximum across ranks before using these numbers for attribution.

Useful? React with 👍 / 👎.

// Rank 0 only, every REPORT_SEC seconds, deltas since the last report so a
// slow warmup does not smear the steady state.
if (g_rank == 0) {
time_t now = time(nullptr);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep wall-clock checks out of the proxy polling loop

On rank 0, time(nullptr) executes after every iteration of the latency-critical busy-poll loop, including iterations with no new collective or completion. This proxy is the only code publishing rank 0's RDMA writes, so reducing its polling rate can delay every peer and inflate the very wait interval being measured. Check the reporting deadline only after a bounded number of iterations or move reporting to a separate thread so the instrumentation does not perturb the collective.

Useful? React with 👍 / 👎.

time_t now = time(nullptr);
if (last_report == 0) last_report = now;
if (now - last_report >= REPORT_SEC) {
uint64_t calls = g_ctrl->t_calls, dn = calls - last_calls;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Snapshot phase counters at one call boundary

The GPU updates the phase accumulators concurrently with this unsynchronized host snapshot, but t_calls is loaded before the phase fields and the saved baselines are loaded again afterward. If a collective completes between those reads, its cycles can enter the current numerator without entering dn, while the following report counts the call after its cycles were already absorbed into the baseline; this can substantially distort intervals with a small dn. Read t_calls around a single snapshot of all counters and retry when it changes, with the required system-memory ordering.

Useful? React with 👍 / 👎.

choiceoh added a commit that referenced this pull request Sep 4, 2026
#301 머지 후 대조 확인 — 145커밋 사이에 생긴 변화가 세 축의 전제를 갈랐다:

- EXP-18(osar): #99 시대 copy+reduce 11.8µs 전제는 낡음(현 45.5µs/콜,
  고정비 ~22.7µs 지배) → R1 천장 ~0.2-0.3ms 로 하향. R2 의 판정 근거는
  L2 위생×EXP-13 프리페치 시너지(osar churn 26MB/스텝이 프리페치 예산
  12MB 와 직접 경쟁하는 것을 stwt/ldcs 로 제거)로 승격.
- EXP-17(MHC 패스설정): 무장 디코드가 mk_mhc 로 TileLang mhc 179발을
  대체(185→14) — 노브의 주 전장은 프리필 big_fuse + 잔여 14발로 축소.
- EXP-19(hc bf16): ONEPASS 미채택 + mk_mhc 초월 — 폴백 참조 측정으로
  강등, 프로덉션 경로의 같은 질문은 mk_mhc 쪽 별도 축임을 명시.
choiceoh added a commit that referenced this pull request Sep 4, 2026
#301 머지 후 대조 확인 — 145커밋 사이에 생긴 변화가 세 축의 전제를 갈랐다:

- EXP-18(osar): #99 시대 copy+reduce 11.8µs 전제는 낡음(현 45.5µs/콜,
  고정비 ~22.7µs 지배) → R1 천장 ~0.2-0.3ms 로 하향. R2 의 판정 근거는
  L2 위생×EXP-13 프리페치 시너지(osar churn 26MB/스텝이 프리페치 예산
  12MB 와 직접 경쟁하는 것을 stwt/ldcs 로 제거)로 승격.
- EXP-17(MHC 패스설정): 무장 디코드가 mk_mhc 로 TileLang mhc 179발을
  대체(185→14) — 노브의 주 전장은 프리필 big_fuse + 잔여 14발로 축소.
- EXP-19(hc bf16): ONEPASS 미채택 + mk_mhc 초월 — 폴백 참조 측정으로
  강등, 프로덉션 경로의 같은 질문은 mk_mhc 쪽 별도 축임을 명시.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants