메뉴
HN
Hacker News • 3일 전

AI 에이전트에게 '더 빠르게' 요청해 러스트 코드 최적화하기

IMP
7/10
핵심 요약

저자는 Claude Opus 4.5 같은 에이전트형 LLM에 적절한 가드레일과 제약 조건을 주고 반복적으로 최적화를 요청하면, 기존 최고 수준 구현보다 2~20배 빠른 러스트(Rust) 코드를 작성할 수 있음을 검증했습니다. PyO3로 파이썬과 연동하고 criterion 벤치마크 도구를 활용해 UMAP 차원 축소 알고리즘을 최소 의존성으로 처음부터 구현하는 실험을 진행했으며, unsafe 코드 금지 원칙을 지켰습니다.

번역된 본문

2025년 1월, 나는 재미있는 가설을 가지고 블로그 글을 썼다: LLM에게 계속 "더 나은 코드를 작성하라"고 요청하면 더 나은 코드를 쓸 수 있을까? 그것은 견고한 에이전트형 코딩이 등장하기 전이었지만, Claude Sonnet 3.5는 여전히 알고리즘적인 파이썬 코드를 반복적으로 개선할 수 있었다. "더 나은"이라는 지시는 정의가 모호했는데, Sonnet은 그 모호성을 악용해 쓸모없는 기능을 잔뜩 추가했지만 코드는 실제로 더 빨라졌다. 합격/불합격 방식의 일반 코딩 문제 해결에 특화되어 RLHF된 에이전트형 LLM이 등장했더라도, 최적화는 대체로 그 기능 suite에 포함되어 있지 않다. 그 블로그 글의 마지막에서, 나는 LLM이 러스트 코드를 작성하고 PyO3로 두 언어를 연결하여 파이썬의 편의성과 러스트의 속도를 모두 얻어, 초고속 파이썬 코드를 작성할 수 있는 가상의 미래에 대해 의견을 밝혔다. 그 글의 초안에서는 같은 "코드를 더 낫게 작성하라"는 지시를 러스트 베이스 코드에 적용하면 속도가 크게 향상되어 파이썬 코드에도 전파될 것이라 주장했지만, 당시에는 러스트에 대해 충분히 알지 못했고 증거 없이 그런 주장을 하기엔 무리가 있었다. Opus 4.5의 출시로 에이전트형 코딩이 더 실용화된 이후 수개월간의 테스트와 실험 끝에, 나는 현대의 에이전트형 LLM이 적절한 가드레일과 제약 조건이 주어지면 현재 최고 수준의 접근법보다 상당히 빠른 러스트 코드를 작성할 수 있음을 확신을 갖고 확인할 수 있다. 또한 Opus 4.5 이후 각 프론티어 모델 출시마다 LLM의 코딩 능력이 크게 향상되면서 최적화는 더욱 좋아졌고, 누적으로는 도메인에 따라 2배에서 20배의 속도 향상을 달성했다. 더 중요하게, 이 블로그 글은 막연한 글이 아니며 내가 사용한 프롬프트와 벤치마크 결과를 모두 포함한다. 각오하시라.

반복적 "벤치맥싱(Benchmaxxing)" 처음에 소프트웨어를 더 빠르게 만드는 것은 새로운 에이전트형 모델을 테스트하고 비교하는 좋은 정량적 방법이었다. 나는 주로 파이썬 통합과 속도 때문에 러스트를 대상 언어로 사용했지만, 빠른 구현이 실제로 발견되었을 때 유용한 러스트 언어의 다른 특성들도 있다. 예를 들어 메모리 안전성, 그리고 WebAssembly(WASM)로 컴파일하여 큰 노력 없이 웹 브라우저에서 실행할 수 있는 능력 등이다. 다만, 기술적으로 가장 빠른 코드를 만들지 못할 수 있음에도 불구하고 내가 따를 중요한 제약 하나는 가능한 한 unsafe 코드를 금지하는 것이다.

나의 첫 테스트 케이스는 머신러닝 알고리즘을 러스트로 재구현하는 것이었는데, 더 빠르고 확장 가능한 도구가 있다면 데이터 과학자로서 나에게 의미 있는 생산성 향상이 될 것이다. 당시로서는 10년 넘게 반복 개선되어 왔고 이미 C로 작성된 검증된 알고리즘을 이길 수 있다고 가정하는 것은 오만한 일이었으며, 러스트의 저수준 이점이 그리 두드러지지 않는다. 내가 가장 최적화하고 싶었던 알고리즘은 UMAP이었다. UMAP은 내 업무에서 사용하는 가치 있는 차원 축소 알고리즘이지만 빅데이터에는 확장성이 좋지 않고 매우 느리며, cuML 같은 대안은 설정에 시간이 많이 걸린다. umap-rs 같은 UMAP 러스트 크레이트가 이미 존재해서 포크한 뒤 Claude Opus 4.5에게 파이썬/PyO3 지원을 추가하라고 지시할 수도 있었지만, 실험과 학습 경험으로서 가능한 한 낮은 수준에서 최적화를 하기 위해 최소한의 러스트 의존성만으로 에이전트가 알고리즘을 처음부터 작성하게 하고 싶었다. 러스트에는 모든 에이전트가 활용법을 아는 criterion 크레이트라는 포괄적인 벤치마킹 도구가 있다. criterion은 벤치마크를 실행하고, 반복 과정에서 결과를 추적하여 성능이 향상되었는지 퇴보했는지 확인하며, 이 변화가 통계적으로 유의미한지 아니면 단순한 노이즈인지 계산할 수 있다. 먼저, UMAP용 러스트 크레이트를 생성하는 초기 프롬프트에서 나는 Opus 4.5에게 서로 다른 입력 데이터 크기로 벤치마크를 생성하라고 요청했다. 작은 데이터셋에 대한 최적화는 상이할 수 있기 때문이다.

원문 보기
원문 보기 (영어)
In January 2025, I had a fun hypothesis for a blog post : can LLMs write better code if you keep asking them to “write better code”? That was prior to the advent of robust agentic coding, but Claude Sonnet 3.5 was still able to iteratively improve on algorithmic Python code. The “better” instruction turned out to be underspecified: Sonnet abused that ambiguity to instead add a ton of useless features but the code was indeed faster. Even with the rise of agentic LLMs specifically RLHF ed to handle solving common pass/fail coding problems, optimization is generally not a part of that suite. At the end of that blog post, I opined on a hypothetical future where LLMs could be able to write superfast Python code by instead writing Rust code and using PyO3 to bridge the languages to get Python’s ergonomics with Rust’s speed. An earlier draft of that post asserted that the same “write code better” instruction could instead be applied to the base Rust code and drastically improve its speed which would then propagate down to the Python code: however, back then I did not know enough about Rust and making such a claim would be too spicy without evidence. After months of testing and experimenting since the release of Opus 4.5 made agentic coding more viable, I can confidently confirm that modern agentic LLMs can indeed write Rust code that is significantly faster than current state-of-the-art approaches if given appropriate guardrails and constraints . Additionally, as LLMs have made drastic improvements in coding in each successive frontier model release since Opus 4.5, the optimizations have become even better, cumulatively resulting in anywhere from 2x-20x speedup depending on the domain. More importantly, this blog post is not a vaguepost and I am including both the prompts I used and the benchmark results. You’ve been warned. Iterative “Benchmaxxing” # At first, making software faster was a good quantitative way for me to test and compare these new agentic models. I used Rust as the target language primarily due to the Python integration and speed, but there are other aspects of the Rust language that make it particularly useful if a fast implementation is indeed discovered, such as memory safety and the ability to compile it to WebAssembly /WASM so it can run in a web browser without much effort. However, one important constraint I will follow that technically may not result in the fastest code is to forbid unsafe code whenever possible. My first test case was reimplementing machine learning algorithms in Rust, which would lead to a meaningful productivity increase for me as a data scientist if I had faster and scalable tooling. At the time, it was arrogant to assume that I could beat battle-tested algorithms that have been iterated on for over a decade and are already written in C so Rust’s low-level benefits are not as pronounced. The algorithm I wanted to optimize the most was UMAP , which is a valuable algorithm for dimensionality reduction I used in my work, but scales poorly to big data and is very slow, with alternatives such as cuML being time-consuming to set up. UMAP Rust crates such as umap-rs already exist where I could just fork them and prompt Claude Opus 4.5 to add Python/PyO3 support, but as an experiment and learning experience I wanted to have the agent write the algorithm from scratch with minimal Rust dependencies in order to make optimizations at as low of a level as possible. Rust has a comprehensive benchmarking tool with the criterion crate which all agents know how to leverage. criterion will run the benchmarks, track results across iterations to see if performance improved or regressed, and can calculate if this change is statistically significant or just noise. First, in the initial prompt for creating a Rust crate for UMAP, I asked Opus 4.5 to create benchmarks with different input data sizes since optimizations for small datasets may not work for large datasets and vice versa. Afterwards, create and run a benchmark suite which stores the results as a Markdown file. The benchmark suite MUST include inputs up to 100000x768 and be tested in both CPU and GPU modes. This approach created benchmarks using criterion and I manually reran the benchmark suite after prompting performance feature improvements such as using faer for faster linear algebra and using simsimd for faster SIMD operations . This quickly became cumbersome as I had to manually rerun each benchmark after each change to verify there are no speed regressions. A sidenote on prompting style The way I prompt agentic LLMs is unusual: I typically provide the agents very long prompts prewritten in a Markdown document with the additional use of ALL CAPS and **bolding** for emphasis. This is to ensure I capture all nuances through the use of prompt engineering , along with several other tricks as detailed in this blog post. Although some may argue prompt engineering is dead as the latest models have become smart enough to correctly handle ambiguity, I strongly disagree as LLMs have also become much better at following said nuances. After having enough confidence that the agent will not accidentally rm -rf the repo, I experimented with letting the agent be autonomous, giving them permission to iterate until they achieve a speed increase, hopefully. **YOU MUST KEEP ITERATING OPTIMIZATIONS AND SOLVING ISSUES UNTIL THE BENCHMARK RESULTS STOP IMPROVING AND THE CRATE IS AS FAST AS IT CAN BE** . You have permission to keep iterating until you run out of ideas. It turned out “fast as it can be” is too ambiguous and Opus 4.5 was lazy so it tweaked a few hyperparameters without much of an actual speed increase and called it a day. What I needed was a clear target goal that can be pass/failed, so I refined the prompt: First, **without making any futher changes** , run the CPU Rust benchmarks to establish a True Performance Baseline. Then, optimize the crate code to make it such that ALL CPU benchmarks run **atleast 1.2x faster** than the True Performance Baseline; ideally as fast as possible. NEVER hack the benchmarks to accomplish this runtime reduction, only iterate on the library code. You may use ANY techniques to do so (e.g. import new crates) other than adding `unsafe` code. **REPEAT THIS PROCESS UNTIL BENCHMARK PERFORMANCE CONVERGES AND YOU ARE OUT OF OPTIMIZATION IDEAS.** You have permission to keep iterating. After each benchmark iteration, report the relative results to the True Performance Baseline to console. Prioritize making quick/high-impact wins iteratively and making changes accordingly. Do not overthink the necessary changes. This worked very well and not only did I get a 1.2x speed up on the benchmarks, but the agent continued after hitting the metric constraint and only stopped if a metric constraint was infeasible; in this instance, the agent hit 1.5x-2.0x speedups. The low-level Rust optimizations centered around a number of techniques including but not limited to: leveraging SIMD operations more aggressively, fusing functions, unrolling loops, creating intermediate caches, using Arc instead of borrowing wherever possible, and creating performance profiles based on input data (e.g. if the data is small, don’t use rayon data parallelism as the overhead erases gains). I chose “1.2x faster” as a sanity test: if the goal is too high, the agent may cheat to achieve it through risky/verbose rewrites. Smaller changes are better since the agent can more easily isolate the cause of a speedup/regression, hence the note about iteration. After new frontier LLMs released such as GPT-5.3 Codex and Opus 4.6, I repeated this prompt unchanged for every new LLM and each were able to achieve a cumulative 1.5x-2.0x speedup over the previous pass. Going all the way to GPT-6 Astra over many months, that’s around 7.5x-32x faster than the initial implementation baseline. This approa