TECH 으로 돌아가기
TECH HACKER NEWS 오늘 7분 읽기 24 READS

힙을 없앤 실험적 언어 Goose: C++·Rust보다 빠르다는 주장의 실체

힙을 없앤 실험적 언어 Goose: C++·Rust보다 빠르다는 주장의 실체
SOURCE IMAGE · HACKER NEWS

메모리 안전성과 성능은 오랫동안 상충 관계로 여겨져 왔다. Rust는 소유권과 수명 표기로 안전을 확보하는 대신 학습 곡선과 표기 부담을 치렀고, C++는 속도를 얻는 대신 안전을 개발자 책임으로 넘겼다. 새로 공개된 실험적 시스템 언어 Goose는 이 구도를 다른 각도에서 공략한다. 벤치마크 기준으로 C++보다 1.16배, 안전한 Rust보다 1.12배 빠르면서도 더 적은 메모리를 쓰고, 할당자나 가비지 컬렉터도 수명 표기도 없다고 주장한다. 문법은 C나 Rust처럼 익숙하지만, 언어 전체가 단 하나의 발상 위에 서 있다. 바로 '힙이 없다'는 것이다.

힙을 없앤다는 설계

Goose 프로그램은 네이티브 호출 스택, 정적 데이터, 그리고 컴파일러가 개수를 스스로 산출하는 여러 개의 '데이터 스택'만 가진다. 데이터 스택은 넓은 주소 공간을 예약해 둔 영역에 범프 포인터 하나를 얹은 구조로, 값의 증가는 포인터를 밀어 올리는 것이고 해제는 스코프를 벗어날 때 한 번뿐이다. 크기가 변하는 값은 스택당 최대 하나만, 그것도 항상 맨 위에만 살아 있게 강제된다. 덕분에 값이 커져도 무언가를 옮기거나 용량을 점검할 일이 없다. 이 모든 규칙은 컴파일 시점에 증명되며, 런타임에는 범프 포인터 외에 아무것도 남지 않는다.

여기서 성능 이득의 상당 부분이 나온다. push는 방금 만든 원소에 대한 참조를 돌려주는데, 재할당이 일어나는 vector나 Vec은 이 보장을 할 수 없다. 참조와 슬라이스는 저마다 대상의 수명을 한정하는 변수, 즉 '루트'를 정적으로 지니고, 루트는 추론되며 함수는 루트별로 특수화된다. 그 결과 수명을 위한 별도 문법도, 앨리어싱이나 배타성 규칙도 존재하지 않는다. 검사기가 문제를 발견하면 참조와 그 대상의 양쪽을 모두 지목한다.

데이터를 통째로 인라인에

Goose에는 단일한 배열 타입이 없다. 크기 처리 방식만 다른 배열 계열이 있고, 모든 원소는 [메타데이터][원소들] 형태로 포인터 없이 촘촘히 인라인된다. 문자열도 u8 배열의 한 종류일 뿐이며, 구조체는 가변 크기 부분을 선언 순서대로 몸체 안에 그대로 품는다. 원문이 든 예에서 어떤 주문 레코드는 29바이트에 할당 0회로 표현되는데, 같은 것을 C++의 std::string과 std::vector로 만들면 160바이트에 할당 1회, Rust의 String과 Vec으로는 153바이트에 할당 4회가 된다. 주문서 전체가 캐시를 타고 흐르는 하나의 배열이 되는 셈이다. 대가도 분명하다. 가변 크기 원소로 이뤄진 배열은 순차적이어서, 순회는 되지만 인덱싱은 되지 않는다.

동적 다형성은 대수적 데이터 타입(ADT)으로만 제공되며 상속도 vtable도 없다. 값을 저장할 때 고정 모드와 가변 모드를 골라 쓸 수 있고, 분기는 태그 기반 점프 테이블로 이뤄지며 누락 없는지 검사된다. 참조는 8바이트짜리 기계 주소이지만 결코 매달린 포인터가 되지 않으며, 같은 배열 안을 좁은 오프셋으로 가리키는 상대 참조를 쓰면 구조체가 위치 독립적이 된다. 자기 상대적 링크로 짜인 자료구조는 바이트를 그대로 쓰는 것이 저장이고 되읽는 것이 로딩이라, 직렬화기도 스키마도 포인터 보정도 필요 없다. from_bytes가 프레이밍과 모든 태그·길이·링크를 검증한 뒤에야 값이 되므로, 손상되거나 악의적인 파일은 와일드 참조가 아니라 실패로 귀결된다.

동시성과 C 생태계, 그리고 한계

공유 가변 메모리는 없다. 스레드 함수와 그것이 부르는 모든 것은 자체 데이터 스택과 전역 복사본을 가진 독립 프로그램으로 컴파일되고, 값은 타입별 큐를 통해 오간다. 넘기는 값은 어떤 깊이에도 참조가 없는 평면 값이어야 하며, 평면 값은 연속적이므로 전달은 memcpy 한 번으로 끝난다. 외부 연동은 Goose 시그니처를 C 심벌에 묶는 extern fn이 전부이고, 컴파일러는 프로그램 전체를 하나의 C 파일로 내보내 아무 C 컴파일러로나 빌드하거나 번들된 TinyCC로 프로세스 안에서 곧바로 실행한다. 모든 인덱스는 경계 검사되지만 컴파일러가 대부분을 증명해 없애며, 살아남은 검사는 --bce-lines로 확인할 수 있다.

실무자가 냉정하게 봐야 할 지점은 성숙도다. Goose는 약 3만 줄 규모의 C++ 전체 프로그램 컴파일러와 명세, 표준 라이브러리, 26개 샘플, CI 테스트 스위트를 갖췄지만 여전히 새로운 언어다. 가변 값의 이동, 구조체당 둘 이상의 가변 부분, 레이블 break, 네임스페이스 접근 제어, 폭넓은 OS·라이브러리 접근은 현재 의도적으로 범위 밖이다. 게다가 이 저장소는 인간이 설계하되 코드 구현 대부분을 AI가 맡아, 설계자 Wouter van Oortmerssen(Lobster 언어 제작자)이 자신의 기존 컴파일러 스타일을 모방하도록 만든 실험이기도 하다. 벤치마크 수치가 인상적이더라도, 제한된 샘플과 초기 도구 생태계라는 맥락 위에서 읽어야 한다.

그럼에도 Goose가 던지는 질문은 유효하다. 대부분의 프로그램이 실제로 힙을 얼마나 필요로 하는가, 그리고 데이터를 인라인으로 촘촘히 배치할 때 캐시가 대신 벌어 주는 성능은 얼마나 되는가. 당장 프로덕션에 도입할 물건은 아니지만, 자료구조 배치와 메모리 지역성이 곧 성능이라는 사실을 다시 일깨우는 참고 사례로서의 값어치는 충분하다. 관심 있는 이라면 튜토리얼과 벤치마크의 결과표(패배한 항목 포함)를 직접 뜯어보는 편이 주장만 읽는 것보다 얻는 것이 많을 것이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://github.com/aardappel/goose/tree/master
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...