Jam 프로그래밍 언어
요약
Jam 프로그래밍 언어의 설계와 Rust, Zig와의 차이점을 분석하며, 특히 컴파일러 최적화와 메모리 안전성, ABI 안정성에 대해 논합니다. LLM이 생성한 정성적인 기술 글의 위험성을 경고하며 정량적 데이터 기반 분석의 중요성을 강조합니다.
핵심 포인트
- Jam 언어의 성능 최적화를 위한 low-level 작업 필요성
- LLM 생성 기술 글의 정성적 서술 및 데이터 부재 위험성 경고
- Rust, Zig와 Jam의 메모리 안전성 및 ABI 설계 차이 분석
- 컴파일러 프로파일링 시 정량적 데이터의 중요성
Rust와 Zig는 standard library의 target-specific intrinsic, auto-vectorization hint, allocator-aware container, hot path tuning, LLVM pass tuning 같은 작업을 오래 해옴
Jam도 마지막 10~30%를 좁히려면 같은 종류의 작업이 필요함
지금 측정한 workload에서는 gap이 “다른 class”가 아니라 small constant factor 안에 있다고 봄
특성 해결은 탐색 문제다. 빌림 검사는 함수 전체 영역 분석이다. 단형화는 가장 느린 단계인 LLVM이 보기도 전에 코드 양을 늘린다…
이런 LLM 생성 글은 엔지니어, 특히 젊은 엔지니어가 조심해야 할 일을 하고 있음: 정량 데이터 대신 정성적이고 그럴듯한 산문으로 대체하는 것
이야기로 설득하는 건 단단한 수치를 모으고 분석하는 것보다 작성자와 독자 모두에게 쉬움. 사람 뇌는 이야기를 좋아하고, 이야기는 단순하고 깔끔할 때 가장 잘 먹힘. 현실 데이터는 들여다보려는 만큼 뉘앙스가 많은 복잡한 세계를 반영하는 경우가 많음
rustc 기여자가 쓴 Rust 컴파일러 프로파일링 정량 블로그 글과 비교해보면 됨
이 프로젝트를 진지하게 받아들여야 하는지부터 의문이 듦
“정량 데이터 대신 정성적/환기적 산문으로 대체”라는 표현이 특히 좋았음
좋은 기술 글은 적절하다면 둘 다 담을 수 있고 담아야 하지만, 정말 전달해야 할 것을 놓치면 안 됨. 보증 업무 조직을 크게 운영해보니 기술 글쓰기가 얼마나 어려운지 알게 됐고, LLM 접근성이 쉬워지면서 이 문제가 얼마나 더 나빠질 수 있는지 조심해야 함
Zig와의 핵심 차이는 drop이 있고, 쉽게 오용되는 특정 구성요소인 undefined가 없다는 점인가?
undefined가 없고 모든 값은 초기화돼야 하지만, Maybe(T).empty()는 내용이 “아직 의미 없는” 값을 반환하고, 바로 뒤에 unsafeAssumeInit()을 호출하면 쓰레기 값을 돌려줄 듯함. 그러면 Rust처럼 컴파일러가 unsafe를 명시적 unsafe { .. }가 필요한 오염으로 다루는 의미의 안전은 아님
내가 잘못 본 게 아니라면 이건 안전하지 않은가? 수동 파일 디스크립터 할당은 제쳐두더라도 close(7)을 호출한 뒤 7을 반환함. 수명 추적이 없으니 파일 디스크립터의 수명이 useFile() 반환 전에 끝났다고 사용자가 표현할 방법이 없음
ABI 예제에서 export fn counterAdd(c: mut Counter, n: i64) i64 { .. }가 int64_t counterAdd(Counter *c, int64_t n);가 될 때, c가 NULL이어도 되는지 아닌지는 어떻게 표현하나? Rust에는 이 부분에 정의된 ABI가 있고, extern "C" fn counterAdd(c: &mut Counter, n: i64) -> i64도 가능하고 extern "C" fn counterAdd(c: Option<&mut Counter>, n: i64) -> i64도 가능함
Rust 버전도 unsafe가 필요하지 않음. 참조로 API를 정의할 수 있음. 아이러니하게도 unsafe가 필요할 수 있는 유일한 곳은 최신 Rust에서 #[unsafe(no_mangle)]인 #[no_mangle] 정도인데, 예제는 어째서인지 Rust 쪽에서 원시 포인터를 쓰도록 구성함
나도 그렇게 읽었음. 다만 Rust의 .as_raw_fd()와 다를 바 없고, 거기에도 같은 안전성 문제가 있음
Rust의 ABI를 불안정하게 만드는 것이 Jam에는 존재하지 않는다. 일급 참조도, 수명도, 지워야 할 niche-packed 레이아웃도 없다
이건 Rust 표준 라이브러리의 FFI 안정성을 오해한 것임. 공유 참조, 가변 참조, Box, 그리고 그것들의 Option은 모두 정의되고 안정적인 ABI를 가짐. 그래서 예제의 Box::into_raw/from_raw 절차 전체가 불필요함
수명은 바이너리 수준에는 아예 존재하지 않음. 열거형에 안정 ABI를 정의하겠다고 선택하면 niche 최적화는 비활성화됨
대부분의 타입이 안정 ABI를 정의하지 않는 이유는, 타입 내부를 바꿀 수 없게 되기 때문에 안정 ABI를 원하지 않는 경우가 많아서임
Jam은 아직 공개되지 않았다. 컴파일러는 존재하고 실행되지만, 매일 쓰기 좋게 만드는 것들—안정된 표면, 패키지 관리자, LSP, 포매터, 없을 때만 알아차리는 나머지 도구들—을 작업하는 동안 더 넓은 공개를 미루고 있다…
이 선택은 이해가 안 됨. 불완전한 것을 “출시”하는 것과 그냥 소스 공개를 하는 것 사이에는 큰 차이가 있음. 어차피 나중에 할 거라면 프로젝트를 만드는 동안 공개해서 해가 될 게 뭔가?
장점은 방향이 마음에 드는 사람들이 직접 써보고 어쩌면 기여도 할 수 있다는 것임. 물론 “AI의 시대”라서 그런 기여가 순이익일지는 분명하지 않음. 또 사람들이 무엇을 만들고 있는지 더 잘 이해하고, 왜 훌륭한지에 대한 주장을 평가할 수 있게 해줌. 그걸 못 하면 프로젝트는 훨씬 덜 흥미로워짐
게다가 이런 도구를 하나도 안 쓰는 사람들도 있음. 지금 내 팀도 자동 포매터 채택조차 합의하지 못하지만, 그 외에는 훌륭함. 그러니 그런 도구를 만드는 동안 공개를 미루는 건 별 차이를 만들지 않음
사람들은 계속 “성가신 수명 없는 Rust”를 만들려 하고 계속 실패함. 다른 댓글이 실패 양상 하나를 다뤘는데, drop된 값의 일부를 반환하는 문제는 참조를 반환할 수 없어서 생김. 다른 고전적인 문제는 이거임:
let mut arr = vec![1];
let x = &arr[0];
arr.push(2);
// x를 사용하면 어떻게 되나?
답은 셋임:
거부한다. 그러려면 어떤 형태의 빌림 개념이 필요함. 보통 공유 XOR 가변인데, 가변만 있으면 불편하고 공유만 있으면 안전하지 않음
허용한다. 다른 변수를 통한 참조가 존재하지 않고 모든 것이 GC 또는 참조 카운트 포인터이기 때문임
허용하고 런타임에 정의되지 않은 동작을 일으킨다
이 셋 중 어느 쪽을 택해도 나름의 좋은 이유는 있지만, Jam은 Rust처럼 1번이 되고 싶어 하면서 실제로는 값 의미론 때문에 2번인 듯함. 모든 것이 복사된다는 뜻이라면, 안전하면서도 효율적인 자료구조를 작성하는 걸 막을 가능성이 큼
Inko는 꽤 잘하고 있다고 봄. 물론 내 명백한 편향은 빼고 봐야 하지만, 그 자체의 절충도 분명 있음
특히 빌림 검사기를 버리면, 여러 단서를 도입하지 않고는 스택 할당 타입을 지원하기가 훨씬 어려워짐. 예를 들어 빌릴 때 복사하는 방식인데, Inko와 Swift가 둘 다 이렇게 함
Jam은 잘 모르겠지만, Hylo식 가변 값 의미론에는 subscripts라는 빌림 형태가 있음. 그래서 좀 더 중간 지대에 가까움
그 부분을 읽었을 때 가장 먼저 든 질문은 “참조도 수명 표기도 없으면 구조체 안에 참조를 어떻게 저장하지?”였음
언어 참조를 보니 참조는 없지만 mut와 const포인터는 있고, 그 안전성에 관한 내용은 찾지 못했음
Zig를 Zig답게 만드는 큰 요소는 RAII가 없다는 점이고, Rust는 빌림 검사임. 그런데 이 설계 선택들이 도달한 지점인 “참조 없는 RAII”는 실제로 누가 필요로 하는 건지 잘 모르겠음
그래도 이 틈새에서 실험할 공간은 있다고 보고, 그런 시도는 좋게 생각함. 다만 이 접근은 아닌 것 같음
요즘 계속 생각하는 방향은 Zig의 comptime, Pony 비슷한 참조 권한, 수명을 컴파일 타임 값으로 다루는 것, 그리고 수명을 할당자에 브랜딩하는 것의 조합임
기대하는 건 Zig의 할당자 전략에 참조 안전성을 더하고, 거의 표기할 필요 없는 수명을 얻는 것임
새 언어는 좋지만 모든 것이 LLVM의 프런트엔드가 되는 건 싫음. 백엔드가 어렵다는 건 알지만, 가끔은 다른 선택지도 좀 있었으면 함
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기