프로필 기반 최적화(PGO)의 작동 방식

프로필 기반 최적화(PGO 또는 'pogo'라고도 함)는 실제로 플레이될 때 게임이 동작하는 방식에 관한 정보를 사용하여 게임의 최적화된 빌드를 더욱 최적화하는 방법입니다. 이러한 방식으로 오류나 극단적인 사례와 같은 자주 실행되지 않는 코드가 코드의 중요 실행 경로에서 강조되지 않아 속도가 향상됩니다.

PGO 작동 방식의 시각적 개요를 보여주는 다이어그램

그림 1. PGO 작동 방식 개요

PGO를 사용하려면 먼저 빌드를 계측하여 컴파일러가 작업할 수 있는 프로필 데이터를 생성합니다. 그런 다음 해당 빌드를 실행하고 프로필 데이터 파일을 하나 이상 생성하여 코드를 실행합니다. 마지막으로 이러한 파일을 기기에서 다시 복사하고, 캡처한 프로필 정보를 사용하여 실행 파일을 최적화하는 데 컴파일러와 함께 이를 사용합니다.

PGO 없이 최적화된 빌드의 작동 방식

프로필 데이터를 사용하지 않고 최적화된 빌드는 최적화된 코드를 생성하는 방법을 결정할 때 많은 휴리스틱을 사용합니다.

일부는 개발자가 명시적으로 신호를 보냅니다. 예를 들어 C++ 20 이상에서는 [[likely]][[unlikely]]와 같은 브랜치 방향 힌트를 사용합니다. 또 다른 예로는 inline 키워드를 사용하거나 __forceinline을 사용하는 경우를 들 수 있습니다(일반적으로 앞에 예로 든 방법이 더 나으며 유연함). 기본적으로 일부 컴파일러는 브랜치의 첫 번째 구간(즉, else 부분이 아닌 if 문)이 가장 가능성이 높다고 가정합니다. 최적화 도구는 코드의 정적 분석을 통해 실행 방법에 관해 가정할 수도 있지만 일반적으로 범위가 제한적입니다.

이러한 휴리스틱의 문제는 전체 수동 마크업을 사용해도 모든 상황에서 컴파일러에 올바르게 도움을 줄 수 없다는 것입니다. 따라서 생성되는 코드가 일반적으로 최적화는 잘되지만 런타임 시 코드의 동작에 관한 추가 정보가 컴파일러에 있는 경우만큼 좋지는 않습니다.

프로필 생성

실행 파일이 계측 모드에서 PGO를 사용 설정한 상태로 빌드되면 실행 파일은 모든 코드 블록의 시작 부분(예: 함수의 시작 부분 또는 브랜치의 각 갈래의 시작 부분)에 코드로 보강됩니다. 이 코드는 컴파일러가 나중에 최적화된 코드를 생성하는 데 사용할 수 있는 실행 중인 코드에 의해 블록이 입력될 때마다 횟수를 추적하는 데 사용됩니다.

다른 추적 작업도 실행됩니다. 예를 들어 블록의 일반적인 복사 작업 크기로 인해 나중에 빠른 인라인 버전의 작업이 생성될 수 있습니다.

게임에서 일종의 대표 작업을 실행한 후에 실행 파일은 함수(__llvm_profile_write_file())를 호출하여 기기에서 맞춤설정 가능한 위치에 프로필 데이터를 작성해야 합니다. 이 함수는 빌드 구성에 PGO 계측이 사용 설정되면 게임에 자동으로 연결됩니다.

작성된 프로필 데이터 파일을 호스트 컴퓨터로 다시 복사해야 하며, 함께 사용할 수 있도록 동일한 빌드의 다른 프로필과 함께 한 위치에 보관하는 것이 좋습니다.

예를 들어 현재 게임 장면이 끝나면 __llvm_profile_write_file()을 호출하도록 게임 코드를 수정할 수 있습니다. 그런 다음 프로필을 가져오려면 계측이 사용 설정된 상태에서 게임을 빌드하고 Android 기기에 배포합니다. 실행되는 동안 프로필 데이터가 자동으로 캡처됩니다. 품질보증 엔지니어가 게임을 실행하여 다양한 시나리오를 연습하거나 단순히 일반 테스트 단계를 진행합니다.

게임의 여러 부분을 실행했으면 기본 메뉴로 돌아가 현재 게임 장면을 종료하고 프로필 데이터를 쓸 수 있습니다.

그런 다음 스크립트를 사용하여 테스트 기기에서 프로필 데이터를 복사하고 중앙 저장소에 업로드하면 이를 캡처하여 나중에 사용할 수 있습니다.

프로필 데이터 병합

기기에서 프로필을 가져온 후에는 계측 빌드에서 생성된 프로필 데이터 파일을 컴파일러에서 사용할 수 있는 형식으로 변환해야 합니다. AGDE는 프로젝트에 추가하는 모든 프로필 데이터 파일에 이 작업을 자동으로 실행합니다.

PGO는 여러 계측 프로필 실행 결과를 결합하도록 설계되었으며, AGDE는 단일 프로젝트에 여러 파일이 있는 경우에도 자동으로 이 작업을 실행합니다.

프로필 데이터 세트를 병합하는 것이 얼마나 유용한지에 관한 예로, 모두 다양한 레벨의 게임을 플레이하는 품질보증 엔지니어들로 가득한 실험실이 있다고 가정해 보겠습니다. 각 게임 플레이는 기록되어 PGO 계측 게임 빌드에서 프로필 데이터를 생성하는 데 사용됩니다. 프로필을 병합하면 서로 다른 여러 테스트 실행(코드에서 상당히 다른 여러 부분을 실행할 수 있음)의 결과를 결합하여 더 나은 결과를 얻을 수 있습니다.

더 좋은 점은 내부 출시에서 내부 출시로 프로필 데이터 사본을 보관하는 종적 테스트를 실행할 때 재빌드가 이전 프로필 데이터를 반드시 무효화하지는 않는다는 것입니다. 대부분의 경우 코드는 출시 간에 상대적으로 안정적이므로 이전 빌드의 프로필 데이터가 여전히 유용할 수 있으며 즉시 무효화되지는 않습니다.

프로필 기반 최적화 빌드 생성

프로젝트에 프로필 데이터를 추가하면 빌드 구성에서 최적화 모드로 PGO를 사용 설정하여 실행 파일을 빌드하는 데 이를 사용할 수 있습니다.

이렇게 하면 최적화 결정을 내릴 때 이전에 캡처한 프로필 데이터를 사용하도록 컴파일러의 최적화 도구에 지시합니다.

프로필 기반 최적화를 사용하는 경우

PGO는 개발 초반이나 일상적인 코드 반복 기간에 사용 설정할 수 있는 것이 아닙니다. 개발 중에는 훨씬 더 많은 이점을 제공하는 알고리즘과 데이터 레이아웃 기반의 최적화에 집중해야 합니다.

PGO는 개발 프로세스의 후반부에서 출시를 위해 개선할 때 제공됩니다. 프로필 기반 최적화는 개발자가 이미 코드를 직접 최적화한 후에 코드의 성능을 조금이라도 더 개선할 수 있도록 해 주는 마지막 도구라고 생각하면 됩니다.

PGO 사용 시 예상되는 성능 개선

이는 프로필의 포괄성 및 오래된 정도, 기존의 최적화 빌드 사용 시 코드의 최적화 정도 등 여러 요인에 따라 달라집니다.

일반적으로 매우 보수적인 추정치는 주요 스레드에서 CPU 비용이 최대 5% 감소한다는 것입니다. 다른 결과가 나올 수도 있습니다.

계측 오버헤드

PGO의 계측은 포괄적이며 자동으로 생성되지만 무료는 아닙니다. PGO 계측의 오버헤드는 코드베이스에 따라 다를 수 있습니다.

프로필 기반 계측의 성능 비용

계측 빌드를 사용하면 프레임 속도가 감소할 수 있습니다. 정상 작동 시 CPU를 100%에 얼마나 가깝게 활용하는지에 따라 이 감소 폭이 너무 커서 일반 게임플레이가 어려워지는 경우도 있습니다.

대부분의 개발자는 게임에 반확정적인 재생 모드를 빌드하는 것이 좋습니다. 이러한 종류의 기능을 통해 품질보증팀이 게임에서 알려진 반복 가능한 시작 위치(예: 저장 게임 또는 특정 테스트 레벨)에서 게임을 시작한 후 입력을 기록할 수 있습니다. 테스트 빌드에서 기록된 이 입력은 개별 프레임을 처리하는 데 걸리는 시간과 관계없이 PGO 계측 빌드에 제공되고 재생되어 실제 프로필 데이터를 생성할 수 있습니다. 게임이 너무 느리게 실행되어 플레이할 수 없는 경우에도 마찬가지입니다.

이러한 기능에는 테스터의 활동 증대와 같은 다른 주요 이점도 있습니다. 한 테스터가 기기에서 입력을 기록한 후 스모크 테스트를 위해 여러 유형의 기기에서 재생할 수 있습니다.

이러한 재생 시스템은 생태계에 수많은 기기 변형이 있는 Android에서 큰 이점을 얻을 수 있으며 이점은 이것으로 끝이 아닙니다. 지속적 통합 빌드 시스템의 핵심 부분도 형성할 수 있어 정기적인 야간 성능 회귀 및 스모크 테스트를 실행할 수 있습니다.

기록은 게임의 입력 메커니즘 내에서 가장 적절한 지점에 사용자 입력을 기록해야 합니다(직접적인 터치스크린 이벤트가 아니라 대신 결과를 명령어로 기록할 가능성이 높음). 이러한 입력에는 재생 중에 재생 메커니즘이 이벤트를 트리거해야 하는 적절한 프레임을 기다릴 수 있도록 게임플레이 중에 단조롭게 증가하는 프레임 수도 포함되어야 합니다.

재생 모드에서 게임은 온라인 로그인을 피해야 하고, 광고를 표시해서는 안 되며, 고정된 시간 단계(타겟 프레임 속도)로 작동해야 합니다. vsync는 사용 중지하는 것이 좋습니다.

중요한 것은 게임의 모든 요소(예: 파티클 시스템)가 완전히 확정적으로 반복 가능한 것이 아니라 동일한 작업으로 동일한 게임 내 결과를 제공하는 것, 즉 게임플레이가 동일해야 한다는 것입니다.

프로필 기반 계측의 메모리 비용

PGO 계측의 메모리 오버헤드는 컴파일되는 특정 라이브러리에 따라 크게 다릅니다. 테스트에서는 테스트 실행 파일의 크기가 전반적으로 최대 2.2배 증가했습니다. 이러한 크기 증가에는 코드 블록을 계측하는 데 필요한 추가 코드와 카운터를 저장하는 데 필요한 공간이 모두 포함되었습니다. 이러한 테스트는 포괄적이지 않으므로 상황에 따라 결과가 다를 수도 있습니다.

프로필 데이터를 업데이트하거나 삭제해야 하는 경우

코드(또는 게임 콘텐츠)를 크게 변경할 때마다 프로필을 업데이트해야 합니다.

이 말의 의미는 빌드 환경과 개발 단계에 따라 다릅니다.

앞에서 언급했듯이 프로필 데이터를 주요 빌드 환경 변경사항에 적용하면 안 됩니다. 이로 인해 빌드가 방해받거나 중단되지는 않지만 새 빌드 환경에 적용할 수 있는 프로필 데이터가 거의 없기 때문에 PGO 사용으로 얻는 성능 이점이 줄어듭니다. 그러나 이것이 프로필 데이터가 비활성 상태가 되는 유일한 경우는 아닙니다.

성능 중심 엔지니어가 출시일이 다가올 때 예상치 못한 문제가 없는지 확인할 수 있도록 주간 캡처를 수집하는 등 개발이 거의 끝나는 출시 준비 단계까지 PGO를 사용하지 않는다고 가정하면서 시작해 보겠습니다.

이는 품질보증팀이 매일 테스트하고 철저하게 게임을 실행해 보는 출시 기간에 가까워지면 달라집니다. 이 단계에서는 매일 해당 데이터에서 프로필을 생성하고 이를 사용하여 성능 테스트 및 자체 성능 예산 조정을 위해 향후 빌드에 정보를 제공할 수 있습니다.

출시를 준비할 때는 출시하려는 빌드 버전을 잠그고 품질보증팀에서 해당 버전을 실행하여 새 프로필 데이터를 생성하도록 해야 합니다. 그런 다음 이 데이터를 사용하여 빌드하고 최종 버전의 실행 파일을 생성합니다.

그러면 품질보증팀에서 이렇게 최적화된 배송 빌드를 최종적으로 실행하여 출시하기에 적합한지 확인합니다.