From b09bda62059e11ee43ae6d1fd62bf31e2e4fbde6 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Tue, 29 Sep 2026 18:15:11 +0100 Subject: Say what adopting the link took, and how it fares against ngtcp2 (102) 102: the spec (012) and a receiver written from it alone; the four holes writing it found (REFUSED, the record's size, a peer's run places, the memo's places beside) and the credit M6 showed fixed at 1,024 records; M6 with ngtcp2 as a third, two rounds. Under reordering Pure carries the most (45 Mbit/s; ngtcp2 29-47, quinn 1-2), under random loss the three are the same, on a clean bottleneck Pure carries 3-5 % less (its framing, some 112 B a packet to QUIC's 41), and where loss and reordering meet ngtcp2 carries 1.3-3.6 times as much: undoing a loss it took for reordering, it restores the window a real loss had cut too (its log: 22 of 34 undos past the window before the last cut; none without reordering). Shares beside TCP stay within 25 %. 011 and the status table say so; 012 also says that a receiver answers within 25 ms (a sender's PTO counts on it), that a shape and a record's bytes are the app's, and why a packet of the past does no harm. --- docs/design/011-pure-transport.md | 27 +++++++++++++++++---------- docs/design/012-link-protocol.md | 24 ++++++++++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) (limited to 'docs/design') diff --git a/docs/design/011-pure-transport.md b/docs/design/011-pure-transport.md index 5513476..392210c 100644 --- a/docs/design/011-pure-transport.md +++ b/docs/design/011-pure-transport.md @@ -56,18 +56,25 @@ M3(저장소 옮기기, H3)는 [100](../research/100-moved-receiver.md)에 있 - 잃음·겹침 검사, 논스 검사, 보안 검사를 모두 통과한다. - 전달당 CPU는 1 KiB 이상에서 두 QUIC보다 25 % 넘게 가볍다. 61 B에서는 ngtcp2의 1.04배로 10 % 안이다. - TCP와의 몫은 ±25 % 안이다. -- 전송 코드는 C 3,097줄이다. +- 전송 코드는 C 3,146줄이다. 그래서 연결 없는 링크를 노드 사이 전달의 기본으로 삼는다(`--send OUTPUT=pure://host:port/INPUT`). HTTP 전달은 제3자와 -브라우저를 잇는 경계로 남고, 중계를 지나는 봉인한 전달([088](../research/088-sealed-sessions.md))도 그대로다. M6를 ngtcp2와 -재지는 않았다. 가드레일은 quinn과 TCP로 쟀다. - -명세 [012](012-link-protocol.md)를 쓰면서 구현의 구멍 셋을 찾아 고쳤다. -- 받는 쪽이 받지 않는 스트림을 보내는 쪽은 아무 말도 듣지 못해 매초 다시 보냈다. 이제 REFUSED로 이유를 듣고 드물게 다시 시도한다. -- 받는 쪽이 쥘 수 없는 16–64 MiB 기록이 영원히 다시 갔다. -- 한 노드의 출력 셋 이상이 같은 노드로 가면 run들이 받는 쪽의 두 자리를 서로 밀어냈다. - -명세만 보고 파이썬으로 따로 쓴 받는 쪽이 `pure-serve` 노드와 에폭 바꿈까지 맞붙는다. +브라우저를 잇는 경계로 남고, 중계를 지나는 봉인한 전달([088](../research/088-sealed-sessions.md))도 그대로다. + +채택과 함께 한 일은 [102](../research/102-adoption-and-spec.md)에 있다. +- 명세 [012](012-link-protocol.md)를 쓰면서 구현의 구멍 넷을 찾아 고쳤다. + - 받는 쪽이 받지 않는 스트림을 보내는 쪽은 아무 말도 듣지 못해, 90초에 패킷 2만 2천여 개를 다시 보냈다. 이제 REFUSED로 + 이유를 듣고 드물게 다시 시도한다(같은 90초에 9개). + - 받는 쪽이 쥘 수 없는 16–64 MiB 기록이 영원히 다시 갔다. + - 한 노드의 출력 셋 이상이 같은 노드로 가면 run들이 받는 쪽의 두 자리를 서로 밀어냈다. + - 메모가 피어마다 run 하나의 에폭만 들어, 다시 뜬 받는 쪽이 나머지 run에 LOST를 보냈다. +- 명세만 보고 파이썬으로 따로 쓴 받는 쪽이 `pure-serve` 노드와 에폭 바꿈까지 맞붙는다. +- 허가가 기록 1,024건으로 고정되어, 100 ms 경로에서 61 B 기록은 초당 1만 건이 끝이었다. 이제 받는 쪽 메모리에 맞추어 5만 + 6천 건이다. +- M6를 ngtcp2와도 쟀다. + - 재정렬만 있을 때 Pure가 가장 많이 나르고, 손실만 있을 때 셋이 같다. + - 깨끗한 병목에서는 틀의 바이트로 3–5 % 뒤진다. + - 손실과 재정렬이 겹칠 때 ngtcp2가 1.3–3.6배 나르는 것은, 잘못 본 손실을 되돌리며 진짜 손실의 감소까지 지우기 때문이다. 2026-09-29에 링크를 `link/`로 뗐다([009](009-layout.md)). 프로토콜은 이제 소켓, 스레드, 시계가 없는 상태이고 (`link/link-protocol.h`), UDP와 스레드는 그 위의 층이다. 그래서 한 스레드가 링크 여럿을 들 수 있다. M2를 이 보내는 쪽으로 diff --git a/docs/design/012-link-protocol.md b/docs/design/012-link-protocol.md index 1ad3739..558ff7f 100644 --- a/docs/design/012-link-protocol.md +++ b/docs/design/012-link-protocol.md @@ -18,8 +18,10 @@ - **보내는 쪽**: 한 노드의 출력 하나가 저장한 보내기를 다른 노드의 입력 하나로 보낸다. 시작할 때마다 **run**을 새로 뽑는다. run은 0이 아닌 무작위 u64다. - **받는 쪽**: 받아들일 보내는 쪽을 (source 이름, 공개 키) 목록으로 가진다. -- **스트림**: source 이름과 input 이름의 쌍이다. 각 이름은 1–64 B다. 메시지의 shape(u32)가 함께 간다. -- **기록**: 스트림 안에서 1부터 연속된 번호(u64)를 가진 바이트열이다. 0–16 MiB다. +- **스트림**: source 이름과 input 이름의 쌍이다. 각 이름은 1–64 B다. 메시지의 shape(u32)가 함께 간다. 받는 쪽 입력의 + shape와 같아야 한다. 링크에게 shape는 비교만 하는 값이고, 뜻은 모듈 형식([008](008-module-format.md))이 정한다. +- **기록**: 스트림 안에서 1부터 연속된 번호(u64)를 가진 바이트열이다. 0–16 MiB다. 바이트는 보내는 앱이 저장한 그대로이고, + 링크는 읽지 않는다. - **들은 번호**: 받는 쪽 앱이 적용하고 저장한 마지막 기록 번호다. - **답**: 받는 쪽이 보내는 쪽의 run에 보내는 패킷이다. @@ -197,8 +199,12 @@ STORED의 들은 번호 이하인 기록은 덜어 낸다. 보내는 앱은 그 쌓인다(지역 선택). 그래서 꾸며 낸 패킷 폭주에 드는 HKDF에 상한이 있고, 열리는 보내는 쪽 천 개가 한꺼번에 깨어나도 토큰을 쓰지 않는다. - run의 자리는 패킷이 열린 뒤에만 만든다. 위조한 패킷은 아무 자리도 밀어내지 못한다. -- 이미 받은 패킷 번호(구간 32개까지 기억)의 패킷은 ACK만 다시 하고 프레임은 처리하지 않는다. 그보다 오래된 번호는 기록의 - 들은 번호와 STORED의 "가장 큰 값" 규칙이 막는다. +- 이미 받은 패킷 번호(구간 32개까지 기억)의 패킷은 ACK만 다시 하고 프레임은 처리하지 않는다. 그보다 오래된 번호의 패킷은 + 다시 처리해도 해가 없다. + - 기록은 들은 번호가 걸러 한 번만 적용된다. + - 지난 OFFER는 지금 에폭 다음이 아니라 받아들여지지 않는다. + - 지난 RESPONSE는 지금의 CHALLENGE와 맞지 않는다. + - 주소는 가장 큰 번호의 패킷만 옮긴다. **주소.** @@ -209,7 +215,7 @@ STORED의 들은 번호 이하인 기록은 덜어 낸다. 보내는 앱은 그 **답.** -- 확인을 부르는 패킷을 받은 뒤의 다음 차례에 답 패킷 하나에 다음을 넣는다. +- 확인을 부르는 패킷을 받으면 25 ms 안에 답 패킷 하나에 다음을 넣어 보낸다. 보내는 쪽의 PTO가 이 상한을 기대한다. - CHALLENGE(확인 전) - ACCEPT(다음 에폭이 쓰이기 전) - ACK: 가장 큰 번호를 받은 뒤 지난 시간과 구간들 @@ -284,7 +290,8 @@ LOST는 그 패킷의 번호를 담아 에폭 0의 키로 봉인한다. 답 run | 거절 뒤 기다림 | 1초부터 두 배, 64초까지 | 지역 선택 | | 에폭을 잃었다고 볼 때 | 2초와 PTO 4번 | 지역 선택 | | 받은 뒤 저장을 기다리는 시간 | 1초 | 지역 선택 | -| RTT 첫값, ACK 지연 상한, PTO 상한 | 100 ms, 25 ms, 1초 | 지역 선택 | +| ACK 지연 상한(받는 쪽이 지킨다) | 25 ms | 명세 | +| RTT 첫값, PTO 상한 | 100 ms, 1초 | 지역 선택 | | 혼잡 제어 | CUBIC(RFC 9438, C 0.4, β 0.7), 첫 창 10 데이터그램, 페이싱, 잘못 본 손실은 되돌림 | 지역 선택(표준 알고리즘만) | | 재정렬 인내 | 패킷 3–256개, 시간 RTT/8부터 최소 RTT의 1/4씩 RTT까지 | 지역 선택 | | 키 만들기 / 에폭 수락 / LOST | 초당 256 / 256(한꺼번에 8) / 4,096 | 지역 선택 | @@ -326,8 +333,9 @@ LOST는 그 패킷의 번호를 담아 에폭 0의 키로 봉인한다. 답 run ## 이 명세와 구현 - 구현은 `link/link-protocol.c`(상태, 소켓 없음)와 `link/link.c`(UDP, GSO/GRO, 스레드)다. -- 전송 코드는 C 3,097줄이다(프로토콜 2,510, UDP 587). 011의 한도는 5,000줄이다. -- 이 명세를 쓰면서 구현의 구멍 셋을 찾아 고쳤다. 셋 다 `link/test-link.c`에 검사가 있다. +- 전송 코드는 C 3,146줄이다(프로토콜 2,559, UDP 587). 011의 한도는 5,000줄이다. +- 이 명세를 쓰면서 구현의 구멍 셋을 찾아 고쳤다. 셋 다 `link/test-link.c`에 검사가 있다. 메모의 곁 자리와 허가의 크기까지 + [102](../research/102-adoption-and-spec.md)에 있다. - 받는 쪽이 스트림을 받지 않을 때 보내는 쪽이 아무 말도 듣지 못해 매초 기록을 다시 보냈다. REFUSED를 더했다. - 보내는 쪽은 64 MiB까지의 기록을 받아 두었지만 받는 쪽은 16 MiB까지만 쥘 수 있었다. 그 사이 크기의 기록은 영원히 다시 갔다. 기록의 상한을 16 MiB로 맞췄다. -- cgit v1.2.3