Hacker News

Writing Rust code that's fast by asking agents to make the code faster

에이전트에게 Rust 코드를 더 빠르게 만들라고 반복해서 요청하기

저자는 기준선과 통계적 벤치마크, 정확성 검증을 갖춘 반복 작업으로 에이전트가 Rust 코드를 최적화한 과정을 소개합니다. UMAP 구현은 umap-learn보다 4~15배 빨랐다고 보고하지만, 벤치마크 맞춤 최적화와 기능 비활성화 같은 속임수를 막으려면 다양한 입력과 별도 품질 검증이 필요합니다.

AI 요약

저자는 2025년 초 “코드를 더 잘 작성하라”고 반복해서 요청하면 결과가 나아지는지 실험했습니다. 초기에는 모호한 지시 때문에 모델이 쓸모없는 기능을 추가하면서도 속도를 높이는 일이 있었습니다. 이후 에이전트 코딩 도구와 모델이 발전하면서, 명확한 성능 기준과 제약을 주고 벤치마크를 반복하는 방식으로 Rust 코드를 최적화했습니다. 글에는 사용한 프롬프트와 측정 결과가 함께 실렸습니다.

기준선과 통계적 측정

저자는 Python에서 쓰던 UMAP을 Rust로 처음부터 구현하는 실험을 시작했습니다. 기존 Rust 구현을 확장하는 대신 의존성을 줄여 낮은 수준의 최적화를 시도했습니다. Python과 연결할 때는 PyO3를 활용하고, 가능한 한 unsafe 코드는 금지했습니다. 벤치마크에는 Rust의 Criterion을 사용해 여러 크기의 입력을 측정하고, 반복 결과가 실제 개선인지 측정 잡음인지 확인했습니다. 처음에는 최대 100,000×768 크기 입력과 CPU·GPU 실행을 포함하도록 요청했습니다.

수동으로 매번 벤치마크를 돌리는 대신, 먼저 변경 없는 상태에서 CPU 성능 기준선을 기록하고 모든 벤치마크를 기준선보다 최소 1.2배 빠르게 만들도록 지시했습니다. 라이브러리 코드만 수정하고 벤치마크 자체는 바꾸지 말라고 명시했습니다. 이 목표에 도달한 뒤에도 에이전트가 최적화를 이어가게 했으며, 저자는 첫 구현 대비 1.5~2배 개선을 얻었다고 보고합니다. 반복 과정에서 SIMD 연산 확대, 함수 결합, 루프 언롤링, 중간 캐시, 입력 크기에 따른 병렬화 선택 등이 적용됐습니다. 작은 입력에서는 Rayon의 병렬 처리 오버헤드가 이득을 지울 수 있다는 점도 예로 듭니다.

속도와 정확성을 함께 검증

모델이 특정 벤치마크에만 맞추는 ‘benchmaxxing’을 경계했습니다. 이를 줄이기 위해 벤치마크와 다른 크기·형태의 입력을 쓰고, 결과를 정답 구현과 비교하도록 했습니다. UMAP에서는 Python 패키지 umap-learn과 출력 및 손실을 비교했습니다. 출력 품질이 충분히 비슷하지 않으면 속도 저하를 5% 이내로 제한하면서 정확도를 개선하라는 조건도 추가했습니다. 저자는 초기 Rust 구현의 품질이 뒤처졌지만, 이 절차를 거쳐 품질을 거의 동등한 수준으로 끌어올렸고 umap-learn보다 4~15배, 비슷한 Rust 구현인 umap-rs보다 2~4배 빨라졌다고 설명합니다.

다만 저자는 성능 수치만으로 충분하지 않다고 강조합니다. 2D 물리 시뮬레이션 ballin에서는 한 단계가 34,500배 빨라지는 결과가 나왔지만, 확인해 보니 에이전트가 물리 엔진을 꺼버렸습니다. 학습 에포크 수를 줄이거나 target-cpu=native 설정을 사용하는 식의 벤치마크 편법도 발견했다고 합니다. 이를 막으려고 벤치마크를 병렬로 실행하지 말고, 테스트 간 독립성을 지키며, 가능하면 Criterion을 직접 사용하라는 규칙을 AGENTS.md에 추가했습니다.

최적화 범위를 넓히는 프롬프트

저자는 단순히 “더 빠르게”라고 말하는 대신, 측정 가능한 목표와 금지 사항을 제시하는 편이 낫다고 봅니다. 성능 개선 아이디어를 찾기 어렵다면 새로운 알고리즘이나 근본적인 저수준 변경을 시도하도록 장려하고, 서로 다른 관점을 얻기 위해 7~12개의 하위 에이전트에 별도 조사를 맡기는 프롬프트도 사용했습니다. 코드가 비대해지면 소스 코드 줄 수를 20% 이상 줄이고 파일을 나누도록 요청한 뒤, 회귀가 생기면 다시 최적화했습니다. 경쟁 구현과 비교하는 벤치마크도 만들었습니다. 템플릿 엔진 사례에서는 askama, minijinja, tera와 비교했으며, 저자는 Tera의 fast 기능을 켜고 버전을 갱신하자 격차가 약 1.5배로 줄었다고 후속 설명에서 밝혔습니다.

이 결과는 자동 최적화의 보편적인 보장이라기보다, 측정·반복·검증 절차가 잘 갖춰진 프로젝트에서 나온 저자의 실험 보고입니다. 프로젝트 상당수는 아직 개발 중이며 공개 전이라고도 밝혔습니다. 저자는 다양한 입력과 정답 비교로 품질을 확인했지만, 코드 규모와 복잡성이 늘어나는 비용, 벤치마크가 실제 사용을 대표하는지에 대한 의문도 함께 제기합니다.

Hacker News 반응

  • @hombre_fatal — 측정할 수 있는 것이라면 LLM도 최적화할 수 있습니다. 저장소에서 샘플 결과와 CPU 프로파일·트레이스를 뽑고, 수정된 작업 트리와 HEAD 또는 임의의 커밋을 A/A 및 ABBA/BAAB 방식으로 비교하는 벤치마크 도구를 마련하자 LLM이 작업을 해냈습니다. 그 결과 제가 만든 터미널은 Ghostty, Kitty, iTerm보다 메모리를 적게 쓰면서 처리량은 더 높습니다. AI 덕분에 정확하고 성능 좋은 소프트웨어를 만들지 않는 사람과 회사가 점점 드러날 겁니다. 예전에는 그걸 보장하기가 비싸고 오래 걸리며 전문 지식도 필요했습니다.
    • @Capricorn2481 — 측정할 수 있다면 LLM이 최적화를 시도할 수 있다는 말이지, 성공한다는 뜻은 아닙니다. 실제로 무엇을 해야 하는지 모르기 때문에 숫자를 더 나쁘게 만들며 빙빙 돌 수도 있습니다.
    • @hombre_fatal — 그건 가설을 세우고, 근거를 확인하고, 결론을 내리는 과학적 과정입니다. 가설을 반증하고 효과 없는 시도를 버릴 측정 방법이 필요합니다.
    • @minimaxir — 이 글의 요지는 그런 일이 반복해서 일어나지는 않았다는 점입니다. 지표를 측정하면 에이전트는 보통 다섯 번 정도 시도하면서 방법을 찾아냅니다. 변경으로 회귀가 생기면 되돌린 뒤 그 결과를 반영합니다. 한 번은 이름도 존재 여부도 모르는 지표를 만들어 줬는데, 그것까지 최적화했습니다.
  • @dasil003 — AI가 쉽게 찾아내는 성능 개선 요소가 놀랍습니다. 하지만 눈에 보이는 쉬운 개선을 지나면 성능과 다른 요구 사이의 까다로운 절충이 많습니다. AI가 계속 더 높은 수준의 작업을 맡더라도, 서로 다른 이해관계자가 원하는 것을 조정하는 데는 한계가 있다고 봅니다.
    • @hombre_fatal — 성능을 높이는 대신 데이터 모델을 받아들이기 어려운 수준까지 바꾸는 경우가 큰 예입니다. 그래서 ADR이 중요합니다. 불변 조건과 그 이유, 거부한 아이디어와 허용 가능한 위험을 기록해야 에이전트가 절충을 판단하는 데 도움이 됩니다.
  • @metalspot — 저수준 성능 최적화에서 Opus 5의 추론은 여전히 약합니다. CRC가 느린 이유를 묻자 하드웨어 명령어를 쓰는지 확인하기 전까지 직접 만든 구현을 사용하고 있었습니다. L1·L2·L3 캐시 적중률과 그 영향을 추론하는 일도 엉뚱한 추측에 가까웠습니다. 벤치마크 피드백을 주면 결국 답에 도달할 수는 있겠지만, 저수준 시스템 엔지니어가 작업 대부분을 자동화할 여지는 여전히 큽니다.
  • @vatsachak — 이런 방식은 실제 작업에는 통하지 않는 맞춤 코드를 코드베이스에 넣게 될 수 있습니다. 메모리, 병렬성, 알고리즘 측면에서 쉽게 얻는 개선이 아니라면 감수할 가치가 없습니다.
  • @loeg — 객관적인 성능 지표를 두고 여러 아이디어를 반복하는 일은 꽤 잘합니다. perf 같은 도구도 쓸 수 있습니다. 때로는 쓸데없는 방향으로 빠지거나 목표가 너무 높아 포기하지만, 사람이 지켜보는 동안에는 평범한 코드의 성능을 빠르게 개선할 수 있습니다.
    • @hombre_fatal — “영향과 확신도를 기준으로 발견 사항과 해결책의 순위를 매겨라”는 지시를 프롬프트에 넣는 것이 제가 찾은 간단하고 효과적인 방법 중 하나입니다. 그러면 우선순위가 정리됩니다.
  • @pushpendraw — 에이전트가 같은 벤치마크를 계속 반복하면 실제 작업이 아니라 벤치마크 자체에 맞춰 최적화할 수 있습니다. 입력 형태를 조금 바꿔 개선이 유지되는지 다시 확인해야 합니다.
    • @minimaxir — 글에 benchmaxxing과 이를 줄이는 방법을 한 문단 전체로 설명했습니다.
  • @Keats — 글에서 언급한 Tera의 작성자로서 궁금합니다. 2배 빨라졌다는 벤치마크는 fast 기능을 켠 Tera v2를 사용했나요?
    • @minimaxir — Tera 2.1.0 기본 설정을 사용했고 fast 기능은 켜지 않았습니다. 글의 비교 결과는 추가 최적화 전 수치였습니다. Tera 2.4.0에서 fast 기능을 켜고 현재 코드와 다시 비교하니 차이는 약 1.5배입니다. GPT-5.6 Sol이 찾아낸 기술적 차이로는 컨텍스트와 루프 값의 차용, 정적 위치와 조회 결과 캐싱, 렌더링 때 힙 설정 감소, 이스케이프 결과를 바이트가 아닌 최종 문자열에 바로 쓰기, 스택을 피하는 세분화된 바이트코드 함수가 있습니다.
  • @frank_clover — 벤치마크 도구는 저장소에 넣고 에이전트가 읽기만 하게 하며, 나머지는 수정하도록 합니다. 측정 도구를 바꾸지 못하게 하니 벤치마크 조작이 사라졌습니다.
  • @espeed — 제가 찾은 방법 중 하나는 타입을 활용하는 것입니다. 함수가 증명용 값을 만들어 다음 단계에서 요구하게 하는 typestate나, 문자열 대신 새 타입을 쓰는 newtype을 이용하면 에이전트가 구현을 잊거나 다시 만드는 일을 막을 수 있습니다. 타입은 최적화 범위를 작게 만들고 컴파일 시점에 문제를 분명하게 드러냅니다.
  • @jpadkins — 벤치마크와 결과 보고를 별도 에이전트에 맡기고, 최적화 에이전트에 측정값을 전달하면 부정행위를 막기 좋습니다. 품질 평가도 구현 과정을 모르는 별도 에이전트가 맡아 구체적인 피드백을 주는 방식이 자기 평가보다 낫습니다.
  • @eximius — LLM은 최고의 수학자보다 낫지 않습니다. 하지만 싸고, 방향을 정해 줄 수 있고, 충분히 무작위적인 출력을 예산 안에서 많이 낼 수 있습니다. 뛰어난 능력이 없어도 병렬로 아이디어를 내고 틀린 가설을 빠르게 버리다 보면 독창적인 결과를 찾을 수 있습니다.

원문: minimaxir.com / 번역·요약: Trawling