ice rabbit programming

[회고] LTCG와 32비트 컴파일러 본문

Development/문제 진단 회고

[회고] LTCG와 32비트 컴파일러

판교토끼 2026. 10. 4. 17:12
728x90

들어가기 전에

AI 도구를 활용한 개발이 이제는 그 전이 까마득한 옛날이었던 것으로 느껴질만큼 상용화되었다. 다만 26년 중반을 넘어가는 현재 시점에서는 사용자의 능력에 따라서 이끌어낼 수 있는 결과물의 정도가 달라진다고 생각하고, 이슈가 있을 경우 이를 추론하고 판단하는 능력이 더욱 중요해졌다고 생각한다. 그렇기 때문에 6년 반 정도 개발자로 일하면서 자연스럽게 훈련된 프로세스인, 이슈를 찾고 원인을 파악해서 해결하는 과정을 조금씩 정리해보고자 한다.

Debug는 되는데 Release만 터진다 — LTCG와 32비트 컴파일러의 함정

발단

서버(C++) 코드를 몇 줄 고치고 Release|x64 빌드를 돌렸더니 실패했다. Debug 빌드는 멀쩡했다. 에러도 하나가 아니라 세 종류가 섞여 나왔다 — C1002, C1083(.ipdb Permission denied), LNK1257. 내가 고친 건 표준 C++ 한두 줄이라 상식적으로 연결이 안 됐다.

파고들기

증상을 먼저 분리했다.

① 진짜 블로커 — C1002 (컴파일러 힙 부족, pass 2)

터진 위치가 protobuf가 자동 생성한 거대 소스 파일이었다. Release|x64에는 WholeProgramOptimization=true(= /GL + /LTCG)가 켜져 있었다. LTCG의 최적화 패스(pass 2)는 프로그램 전체를 한꺼번에 물고 최적화해서 메모리를 크게 먹는다.

그런데 **호스트 컴파일러가 32비트 cl.exe**였다. Visual Studio 기본값이고, PreferredToolArchitecture가 설정돼 있지 않았다. 32비트 프로세스는 주소공간이 약 3 GB로 막혀 있다. 거대 생성 파일 + LTCG + 32비트 컴파일러가 만나 주소공간을 초과했고, out of heap이 됐다.

Debug가 되는 건 최적화(LTCG)가 없어서 pass 2 자체가 안 돌기 때문이다. 이게 "Debug OK / Release 실패"의 정체였다.

② 이차 피해 — C1083 / LNK1257

프로세스 목록을 보니 orphaned MSBuild가 6개나 떠 있었다. 재시도가 중첩된 흔적이다. 이들이 중간 산출물 디렉터리의 .ipdb를 동시에 잠가서 Permission denied가 났다. 그리고 ①에서 코드 생성이 실패하니 링커가 입력을 못 만들어 LNK1257까지 연쇄됐다.

내가 고친 소스는 앞선 컴파일 패스에서 이미 통과했고 C1002·C1083과는 무관하다는 것도 이 시점에 명확해졌다.

해결

먼저 잠금과 중첩을 정리했다. 떠 있던 MSBuild 프로세스를 정리하고 중간 산출물 디렉터리를 지운 뒤 클린 리빌드. 이것으로 ②와 연쇄 에러가 소거된다.

그다음 64비트 호스트 툴체인을 강제했다.

<PreferredToolArchitecture>x64</PreferredToolArchitecture>

64비트 cl.exe는 주소공간 제한이 없어 LTCG pass 2의 OOM이 사라진다. 대안으로는 해당 구성의 WholeProgramOptimization을 끄거나, 문제의 생성 파일 하나만 /GL에서 제외하는 방법이 있다.

교훈

"Debug는 되는데 Release만 터진다"의 절반은 최적화 패스 문제다. /GL·/LTCG의 유무가 만드는 차이를 먼저 떠올려라.

거대한 자동 생성 파일과 LTCG는 32비트 컴파일러엔 무리다. PreferredToolArchitecture=x64는 큰 프로젝트에서 사실상 필수 설정이다.

여러 종류의 에러가 동시에 나오면 근본 하나 + 연쇄 다수로 의심하고 갈라내라. 여기선 C1002가 근본이고 나머지는 중첩 빌드가 만든 잡음이었다.

마치며

Debug와 Release의 환경이 달라서 런타임에 실행 결과가 다른 일은 흔하지는 않지만 가끔 발생한 경험이 있었다. 팀에 와서 다운로드를 위해 메타 파일을 파싱하는 과정에서 양이 매우 많았을 때 Debug와 Release의 메모리 처리가 달라서 Release에서만 터졌었던 경험이 있었다.

이번 상황에서도 쉽게 예상하기는 힘들었지만 우선 Debug로 바꿔보고 돌려봤을 때 잘 된다는 것을 찾은 이후로는 생성형 AI 도구를 이용해 수월하게 찾아낼 수 있었다. C++의 경우 경험상 빌드 환경에 따라서 에러를 잡아내는 것이 까다로운 편인데, 때문에 항상 염두에 두고 있었고 크게 시간 들이지 않고 잡아낼 수 있었다.

 

 

728x90