bytes가 아니었다

1메가바이트 전개에 484가 들었다. 명백한 용의자는 byte 표현 —— int의 vector는 1바이트당 8바이트를 쓰고, 이 아크 전체가 「언어에 진짜 bytes 타입이 필요한가」를 맴돌고 있었다. 측정은 아니라고 했다. 100만 번의 날 push는 17메가바이트, 4인자 커리화 inner 재귀 함수를 100만 번 부르면 769, 같은 계산을 단일 tuple 인자로 하면 1.4. 세금은 bytes가 아니었다. 커리화 inner 함수가 top-level처럼 uncurry되지 않았던 것 —— 부분 적용마다 region이 결코 해방하지 않는 closure를 alloc하던 것이다.

mereperformanceclosuresmemorylanguage-design

해동기는, 컴파일이 통과하자 올바른 바이트를 인쇄했다 —— 그리고 하나의 잘못된 숫자를. 1메가바이트 파일의 전개에 484메가바이트의 메모리를 썼다. bytes 아크 전체가 어떤 설계의 물음으로 쌓여 있었다: 언어에 진짜 bytes 타입이 필요한가, 바이너리 데이터를 1 저장 바이트당 8바이트의 int vector로 표현하고 있었으니. 484는 그 답이 청구서로 도착한 것처럼 보였다. 아니었다. 이 이야기는, 숫자를 그럴듯한 원인이 아니라 실제 원인까지 좇아, 그럴듯한 쪽이 완전히 무죄임을 찾는 이야기다.

세 개의 측정

효과가 있었던 규율은, 용의자를 확인하기 전에 고치기를 거부한 것이다. 측정 1: 100만 개의 정수를 vector에 push하기만 하는 루프, 그 외 아무것도 —— 17메가바이트. 이것이 8바이트/int 표현에 배가 버퍼의 나머지를 더한 것이고, 이 스케일에서 byte 표현의 전체 비용이다. 484메가바이트를 먹던 것이 무엇이든, int의 vector는 아니었다. 측정 2: 해동기의 핫패스 형태를 한 최소 프로그램 —— 4인자 커리화 inner 재귀 함수를 100만 번 부르기 —— 769메가바이트. 거기 있었다. 측정 3, 확인: 동일한 계산을, inner 함수가 4개의 커리화 인자가 아니라 단일 tuple 인자를 받는 형태로 —— 1.4 메가바이트. 500배 차이, 그리고 유일한 변수는 커리화였다. bytes는 무죄, 범인은 함수의 형태였다.

언어의 절반에만 닿아 있던 uncurrying

기구는, 이름이 붙으면 명쾌했다. 커리화 함수 fn a -> fn b -> fn c -> fn d -> ...를 4인자 전부에 적용하는 것은, 아래에서는 4개의 적용이다: 각각이 1인자를 공급하고, 그것을 가둔 새 함수를 반환한다. top-level 함수에 대해서는 컴파일러가, 이전 이야기 이래, uncurry된 쌍둥이 —— 전 인자를 한 번에 받는 단일 함수, 호출 지점이 전부 한 번에 공급할 때 써서 중간 closure를 건너뛰는 —— 를 emit해 왔다. inner(중첩) 함수는 그 처리를 한 번도 받지 못했다. 그래서 해동기의 Huffman 디코더, 심볼마다 한 번 불리고 각 호출 안에서 몇 번 재귀하는 4인자 inner 재귀 함수는, 부분 적용마다·재귀 스텝마다 region에서 새 closure 환경을 alloc하고 —— 그리고 region은, bump 얼로케이터로, 결코 해방하지 않는다. 1메가바이트의 출력은 수백만 심볼. closure가 수백 메가바이트로 쌓였다.

수정은 uncurrying을 inner 함수로 확장한다: 구체적 인자 타입을 가진 커리화 inner 함수는 이제 같은 direct 쌍둥이를 얻고, 포화 호출 —— 결정적으로, 함수 자신의 body 안 재귀 자기 호출을 포함해 —— 가 그것을 쓰고, closure alloc 없음. 단일 인자 closure 형은 드문 부분 적용을 위해 남으므로, 변경은 additive다. 측정: 100만 반복 microbench는 769메가바이트에서 1.46으로 —— tuple 재작성의 숫자를, 아무것도 재작성하지 않고 달성 —— 그리고 해동기는 484메가바이트에서 34로, 체크섬 검증 붙은 byte 단위 동일 출력을 낸 채. bytes 타입을 저울에 달러 나선 아크는, 메모리가 bytes와 아무 관계 없음을 찾고 닫혔다. 그것이 정말 묻던 설계의 물음은, 두 기능 전에, top-level 함수에만, 이미 답해져 있었다. dogfood의 기여는, 언어의 다른 절반이 같은 답을 요함을 보인 것이다. 다음 Part가 무엇을 잴지는, 언제나처럼 미정 —— 진짜 프로그램이 가리킬 것이다.

← Back to Mere: 언어를 만들다