이차 시간 복잡도 연산을 멋지게 풀어낸 설명임. 링크된 Zig 코드를 잠깐 살펴보니, C 문자열을 문자가 들어 있는 안전하지 않은 메모리 위치의 연속으로 다루는 대신 안전한 Zig 배열로 변환하다 보니 이렇게 된 듯함.
“Zig의 libzigc가 표준 라이브러리를 재구현해 필요한 기능을 정적으로 링크할 수 있고, musl을 순수 Zig로 조금씩 다시 작성하면서 C 의존성을 제거하고 있다”는 설명인데, zigbuild 호출 시 musl 타깃 트리플을 사용한 것인지 궁금함. 그렇다면 musl을 사용하지 않는다는 게 조금 의외이긴 하지만, 트리플의 libc 자리에 glibc 말고 어떤 값을 넣을 수 있는지는 잘 모르겠음.
Zig는 트리플을 특정 구현이 아니라 ABI를 요청하는 것으로 취급함. 핵심 개발팀 구성원이 this Lobsters comment에서 그 이유를 설명함.
이런 말은 하고 싶지 않지만, 이렇게 명백하게 의도치 않은 이차 시간 복잡도를 만드는 버그라면 LLM 코드 검토로 충분히 잡았을 것임.
그랬다면 커뮤니티가 모여 함께 즐겁게 수정하는 기회도 놓쳤을 것임. 1.0 이전 언어라면 이 정도는 허용할 만하다고 봄.
LLM까지 필요하지도 않음. 실행 시간 비율 검사만 있으면 됨.
입력 크기를 1배, 10배, 100배, 1,000배로 늘리며 함수 실행 시간을 측정함. 1배 측정은 사실상 초기 준비 시간을 보는 것이고, 더 정교하게 하려면 먼저 10,000배 입력으로 예열해 안정적인 성능에 도달하게 할 수 있음.
측정 시간의 비율을 비교하면 CPU 세부 특성이나 부하의 영향을 어느 정도 피할 수 있음. 선형인지, n log n인지, n²인지 판별하고 오차를 허용해 복잡도 점수를 산출한 뒤, 일정 점수 이하만 통과시키면 끝임.
이런 종류의 작업은 오히려 LLM이 매우 못하는 영역임.
아주 짧은 문자열은 정렬되지 않은 시작과 끝을 처리하는 부담 때문에 벡터화의 이득을 보기 어려움. 긴 문자열에서는 큰 이득이 있지만 버퍼 끝을 넘어 읽으면 문제가 생길 수 있음. 대부분의 아키텍처는 트랩을 발생시키지 않지만, 일부는 정렬된 읽기에서도 트랩을 발생시킴. 문자열이 캐시에 잘 들어맞으면 strlen이 캐시를 미리 데워 주므로 벡터화된 일치 검사가 매우 빨라지지만, 아주 긴 문자열에서는 strlen 비용이 지배적이 됨.
LLM은 코드가 안티패턴처럼 보인다고 할 것임. 하지만 정말 아주 긴 문자열에 최적화하려던 것인지 따져봐야 함. 내가 본 자료에서는 표준 라이브러리 문자열 함수에 전달되는 문자열이 대체로 0바이트~1KiB였고, 0바이트는 예상보다 훨씬 흔하며 약 128바이트를 넘으면 점점 드물어짐.
그렇다면 무엇을 최적화해야 할까? 내가 본 이런 종류의 LLM 조언은 극단적인 사례를 피하는 대신 흔한 사례를 더 느리게 만드는 경향이 있음. 정말 원하던 절충이 그것인지 물어야 함.