프로그램의 첫 화면이 늦게 뜨면 시작 시간을 줄이고 싶어진다. 이미 계산해 둔 것을 다음 실행에서 다시 쓰는 캐시는 꽤 직관적인 해법이다. 그런데 파일이 만들어졌다는 사실과 실제 실행에서 그 파일을 잘 썼다는 사실은 다르다. Java AOT 캐시를 검토한다면 속도 측정표 옆에 실행 조건을 적는 칸도 있어야 한다.

Christopher Schnick은 10월 23일 XPipe 글에서 새 AOT 캐시를 적용하며 만난 제약을 설명했다. 10월 29일 Hacker News에서 다시 논의된 글이다. 특정 버전과 도구 조합에서 겪은 작성자의 관찰이므로 모든 Java 프로그램의 결론으로 넓히기는 어렵다. 다만 캐시를 만드는 환경과 사용하는 환경을 함께 봐야 한다는 질문은 가져올 만하다.
참고 원문: 관련 원문을 직접 읽기
캐시가 생기는 순간과 쓰이는 순간
나는 이런 최적화를 읽을 때 결과물보다 그 결과물이 유효한 조건을 먼저 보려고 한다. 학습 실행에서 어떤 클래스를 불러왔는지, 조립과 실제 실행에서 어떤 JDK를 사용했는지, 실행 중 추가 에이전트가 붙는지를 나눠 적는 식이다. AOT라는 이름이 같아도 클래스 정보의 재사용과 네이티브 실행 파일 생성은 같은 작업이 아니다. 다른 도구의 기대를 가져오면 측정부터 어긋난다.
예를 들어 배포 후보를 검토하는 가상 상황을 생각해 보자. 캐시를 만든 빌드 A와 실제 실행 환경 B가 있다. 둘 다 “JDK 25”라고만 적으면 충분할까. 배포판과 패치 버전, 실행 옵션, CPU 기능, 작업 디렉터리, 부착되는 에이전트가 다를 수 있다. 이 예시는 실제 서비스를 시험한 결과가 아니라 확인 항목을 설계하기 위한 가정이다.
처음에는 차이가 없어 보이는 두 열을 만든다. A에는 캐시 생성 당시의 조건을, B에는 배포 대상의 조건을 쓴다. 값이 다른 항목은 실패라고 단정하지 않고 별도의 확인 대상으로 표시한다. 같은 문자열의 버전 이름만으로 호환성을 통과시키지 않는 것이 목적이다. 지원 범위는 사용하는 JDK의 문서와 실제 로그에서 다시 확인해야 한다.
빠르다는 숫자를 읽기 전에
그다음에는 캐시가 없는 실행, 캐시를 명시한 실행, 캐시를 사용할 수 없는 조건의 실행을 구분해 볼 수 있다. 마지막 경우를 일부러 만드는 일은 운영 서비스가 아닌 분리된 시험 환경에서만 제안한다. 캐시를 못 쓰면 평소대로 시작하는지, 경고를 남기는지, 실행 자체가 끝나는지에 따라 배포 판단이 달라진다. 여기서는 어느 경우도 실행했다고 주장하지 않는다.
특히 명령줄 프로그램은 화면 밖의 소비자도 생각해야 한다. 다른 프로그램이 표준 출력을 받아 처리한다면 진단 문구 한 줄이 결과 형식을 바꿀 수 있다. 따라서 시작 시간 외에 정상 출력과 진단 출력의 위치, 종료 코드, 캐시 미사용 시 동작도 함께 관찰할 이유가 있다. 원문의 개별 오류를 내 환경의 필연적인 오류로 복사할 필요는 없다.
측정값에는 실행 횟수와 조건을 붙인다. 첫 실행과 반복 실행, 파일 시스템 캐시의 영향, 입력 작업의 크기를 섞지 않는다. 예컨대 두 번 실행해 한 번이 빨랐다는 기록만으로 배포 이득을 선언하기 어렵다. 실제 채택 기준은 시작 시간이 중요한 프로그램인지, 캐시를 만드는 추가 과정과 유지 부담을 감당할 수 있는지까지 포함해야 한다.
실패했을 때 원래 경로가 남아 있는가
캐시를 도입하는 변경을 일반 기능 변경과 한꺼번에 묶으면 문제가 생겼을 때 무엇을 되돌려야 할지 흐려진다. 내가 제안하는 작은 검토 단위는 동일한 프로그램의 실행 경로에 캐시만 추가하는 것이다. 이전 실행 방식과 산출물은 보존하고, 캐시 경로와 생성 명령을 별도로 남긴다. 실제 운영 변경의 권한과 검증 절차는 이 글의 제안과 별개다.
가상 점검표의 마지막 열은 “다르면 무엇을 할 것인가”다. CPU가 달라지면 대상별로 다시 확인할지, JDK가 바뀌면 캐시를 새로 만들지, 에이전트가 붙는 환경은 일단 제외할지 적는다. 모든 차이를 자동으로 고치는 스크립트를 먼저 만드는 대신, 지원한다고 말할 범위를 작게 정하는 편이 검토하기 쉽다.
이런 기록은 버전이 바뀔 때도 도움이 된다. 어제의 제약이 다음 릴리스에서 해결될 수 있고, 새로운 최적화 단계가 추가될 수도 있다. 따라서 검토일과 문서 버전을 함께 남긴다. 오래된 경험담은 문제를 발견하는 단서이지 현재 릴리스의 최종 사양서가 아니다. 같은 이유로 이 글도 특정 플래그를 모든 환경의 정답으로 권하지 않는다.
검토표에는 사용자의 체감 장면도 하나 넣는다. 하루 한 번 실행하는 짧은 명령과 오랫동안 켜 두는 서비스는 시작 시간이 차지하는 비중이 다르다. 같은 절감 시간이 보여도 앞의 도구에서는 자주 느끼고 뒤의 도구에서는 거의 못 느낄 수 있다. 실제 실행 빈도를 모르면 효과의 크기도 판단하기 어렵다. 그래서 반복 횟수는 가정으로 계산하고, 관찰한 횟수와 따로 적는다.
캐시를 선택하는 기준은 결국 짧은 시작 시간 하나로 끝나지 않는다. 어떤 조건에서 유효하고, 사용 여부를 어떻게 알며, 실패하면 어디로 돌아가는지가 설명되어야 한다. 다음에 실행 시간을 줄일 도구를 검토한다면 나는 밀리초를 쓰기 전에 그 세 문장을 먼저 채워 보고 싶다. 지금의 실행 환경을 그 정도로 구체적으로 설명할 수 있을까?
함께 읽기: 도구 업데이트와 작업 호환성 메모


