프레임 속도

평균 FPS

원활하고 안정적인 프레임 속도는 Android 기기에서 고품질 게임 환경을 제공하는 데 매우 중요합니다. 게임 성능을 측정할 때는 평균 FPS를 기준으로 측정하여 환경을 기본적으로 이해해야 합니다. 훌륭한 게임 환경을 보장하려면 평균 프레임 속도가 60FPS가 되도록 게임을 최적화해야 합니다.

안정성을 위한 P90 및 P99 FPS

평균 60FPS로 원활하게 실행되는 게임에서도 간헐적인 글리치, 미세한 끊김 현상, 예측할 수 없는 입력 지연이 발생하여 플레이어 경험이 저하될 수 있습니다.

따라서 프레임 안정성은 평균 프레임 속도를 추적하는 것만큼 중요합니다. 여기에서 P90 및 P99 프레임 속도 측정항목을 각각 일관된 기준선과 끊김 현상 표시기로 측정해야 합니다. 이러한 측정항목은 플레이어 환경의 원활함을 최적화할 수 있도록 성능의 '테일 엔드'를 포착합니다.

측정항목

  • 평균 FPS (기준): 이 기본 측정항목은 게임 성능의 일반적인 기준을 제공합니다. 표준 벤치마크이지만 평균 계산은 간헐적인 프레임 드롭과 마이크로 스터터링을 감지할 수 없으므로 플레이어 환경을 단독으로 나타내기에는 불충분합니다.
  • P90 FPS (10% 백분위수의 일관된 기준): 프레임의 90%가 이 일관된 기준을 초과했으며 가장 느린 프레임 10% 만 렌더링하는 데 시간이 더 걸렸음을 나타냅니다. P90 프레임 속도가 높고 평균에 가까우면 세션의 대부분에서 게임이 일관되게 잘 실행되고 있는 것입니다.
  • P99 FPS (1% 백분위수의 끊김 현상 표시기): 프레임의 99% 가 이 끊김 현상 표시기를 초과했음을 나타내며, 가장 느린 프레임 1% 를 구체적으로 분리합니다. 이 측정항목은 눈에 띄는 끊김 현상을 유발하는 마이크로 스터터, 애셋 로드 지연, 갑작스러운 애셋 과부하 렌더링 급증을 포착하는 데 필수적입니다.

평균 FPS를 P90 및 P99 측정항목과 비교하면 게임의 기본 동작을 정확하게 진단할 수 있습니다.

시나리오 1: 최적의 곡선 (최적화된 게임)

  • 평균: 60FPS (16.6ms)
  • P90: 58 FPS (17.2 ms)
  • P99: 52 FPS (19.2 ms)
  • 분석: 측정항목이 밀집되어 있습니다. 게임이 매우 부드럽고 일관성 있게 느껴집니다. 마이크로 스터터링이 없으며 최악의 프레임 1% 조차 사람의 눈에는 거의 눈에 띄지 않습니다.

시나리오 2: 로드 병목 현상 (CPU/GPU 바운드)

  • 평균: 45FPS (22.2ms)
  • P90: 40 FPS (25.0 ms)
  • P99: 38 FPS (26.3 ms)
  • 분석: 평균 프레임 속도가 낮지만 일관되게 낮습니다. P99는 평균에 비해 급격히 떨어지지 않습니다. 이는 시스템이 그래픽 설정이나 해상도 제약으로 인해 과부하가 걸렸음을 나타냅니다. 게임이 끊김 현상이 발생하는 것이 아니라 느리게 느껴집니다. 그래픽 설정을 낮추면 일반적으로 이러한 측정항목이 균등하게 확장됩니다.

시나리오 3: 불안정한 60FPS (셰이더 컴파일 / 애셋 스트리밍 끊김 현상)

  • 평균: 60FPS (16.6ms)
  • P90: 45 FPS (22.2 ms)
  • P99: 15 FPS (66.6 ms)
  • 분석: 최악의 시나리오입니다. 평균 프레임 속도는 우수해 보이지만 P99는 심각한 문제를 보여줍니다. 66.6ms의 P99는 게임이 한 번에 여러 프레임 동안 완전히 멈춘다는 의미입니다. 이는 심각한 이상치를 나타내며 일반적으로 CPU 병목 현상, 애셋 스트리밍 지연(예: 느린 RAM 또는 스토리지) 또는 셰이더 컴파일로 인한 히치로 인해 발생합니다.

측정

평균 FPS, P90, P99 FPS를 효과적으로 측정하려면 다음 두 가지 방법을 사용하면 됩니다. 첫 번째 방법은 성능 프로파일링 도구인 Android Performance Analyzer (APA)를 사용하여 시스템 트레이스를 분석하는 것입니다. 두 번째 방법은 기존 adb dumpsys SurfaceFlinger --timestats 명령어를 사용하는 것입니다.

1. APA를 사용한 측정

APA를 사용하면 시스템 트레이스를 기록하고 SQL 쿼리를 통해 프레임 데이터를 정확하게 분석할 수 있습니다. 측정항목을 측정하려면 다음 단계를 따르세요.

  1. APA로 트레이스 캡처: 게임을 실행하고 APA를 사용하여 분석하려는 세그먼트 (예: 프레임 드롭이 의심되는 게임플레이 중 지점) 중에 시스템 트레이스를 캡처합니다. 기기가 연결되고 트레이스 녹화가 완료되면 트레이스 데이터가 APA 인터페이스에 로드됩니다.

    트레이스 캡처 화면 또는 로드된 트레이스 화면
  2. APA에서 SQL 탭 클릭: 추적 분석 화면이 열리면 UI의 상단 또는 측면 탐색 영역에서 SQL 탭을 클릭하여 데이터를 직접 쿼리할 수 있는 추적 프로세서 환경을 엽니다.

  3. APA SQL 탭에 SQL 쿼리 붙여넣기: 다음 SQL 쿼리를 복사하여 쿼리 입력 필드에 붙여넣습니다. 이 쿼리는 SurfaceFlinger 프로세스를 식별하고, 실제 디스플레이 업데이트 타임스탬프를 기반으로 프레임 간격을 계산하고, 평균 FPS, 하위 10% (P90) FPS, 하위 1% (P99) FPS를 도출합니다.

    WITH target_process AS (
    -- 1. Get SurfaceFlinger process ID where frames were identified in debugging step 3
    SELECT upid
    FROM process
    WHERE name = '/system/bin/surfaceflinger'
    ),
    actual_present_times AS (
    -- 2. Calculate the hardware display timestamps when SurfaceFlinger actually updated the screen
    SELECT
        (ts + dur) AS present_ts
    FROM actual_frame_timeline_slice
    WHERE upid IN (SELECT upid FROM target_process)
        AND dur > 0
    ),
    present_intervals AS (
    -- 3. Calculate intervals between physical screen refreshes
    SELECT
        (LEAD(present_ts) OVER (ORDER BY present_ts ASC) - present_ts) / 1000000.0 AS p2p_ms
    FROM actual_present_times
    ),
    valid_intervals AS (
    -- 4. Filter for valid frame intervals
    SELECT p2p_ms
    FROM present_intervals
    WHERE p2p_ms IS NOT NULL AND p2p_ms > 0
    ),
    ordered_frames AS (
    -- 5. Sort in ascending order to calculate percentiles
    SELECT
        p2p_ms,
        ROW_NUMBER() OVER (ORDER BY p2p_ms ASC) AS row_num,
        COUNT(1) OVER () AS total_frames
    FROM valid_intervals
    )
    -- 6. Output final metrics
    SELECT
    (SELECT COUNT(1) FROM valid_intervals) AS total_presented_frames,
    ROUND(1000.0 / NULLIF((SELECT AVG(p2p_ms) FROM valid_intervals), 0), 2) AS average_fps,
    ROUND(1000.0 / NULLIF((SELECT p2p_ms FROM ordered_frames WHERE row_num = CAST(total_frames * 0.90 AS INT)), 0), 2) AS low_10_fps,
    ROUND(1000.0 / NULLIF((SELECT p2p_ms FROM ordered_frames WHERE row_num = CAST(total_frames * 0.99 AS INT)), 0), 2) AS low_1_fps;
    
  4. '쿼리 실행' 클릭: 쿼리 입력 필드 근처에 있는 쿼리 실행 버튼 (또는 실행 아이콘)을 클릭합니다. 쿼리 실행이 완료되면 측정된 총 프레임 (total_presented_frames), 평균 프레임 속도(average_fps), 하위 10% FPS (low_10_fps), 하위 1% FPS(low_1_fps)가 결과 창의 표에 표시됩니다.

    실행된 SQL 쿼리와 표에 표시된 4개의 결과 측정항목을 보여주는 화면

2. adb (dumpsys SurfaceFlinger)를 사용하여 측정

평균 FPS, P90, P99를 효과적으로 측정하려면 Android dumpsys surfaceflinger timestats 명령어를 사용하면 됩니다. 이 도구는 렌더링되는 모든 레이어의 평균 FPS와 presentToPresent 타이밍 히스토그램을 제공합니다. 프레임의 presentToPresent 시간은 현재 프레임과 그려진 이전 프레임 사이의 간격입니다.

게임의 이러한 측정항목을 수집하고 계산하는 단계별 안내는 다음과 같습니다.

  1. 캡처 시작: enable 및 clear 플래그와 함께 다음 명령어를 실행하여 정보 캡처를 시작합니다.

    adb shell dumpsys SurfaceFlinger --timestats -clear -enable
    
  2. 정보 덤프: 게임을 충분히 플레이한 후 덤프 플래그를 사용하여 명령어를 다시 실행하여 정보를 출력합니다.

    adb shell dumpsys SurfaceFlinger --timestats -dump
    
  3. 레이어별 필터링: 덤프된 정보는 SurfaceFlinger에서 렌더링한 모든 레이어의 데이터를 제공합니다. layerName (예: layerName = SurfaceView[com.example.yourgame...])를 기준으로 필터링하여 게임에 해당하는 섹션을 찾아야 합니다.

    layerName = SurfaceView[com.google.test/com.devrel.MainActivity]@0(BLAST)#132833
    
  4. 평균 FPS 식별: 각 레이어의 평균 FPS가 자동으로 계산되고 덤프 출력에 직접 표시됩니다 (예: averageFPS = 30.179).

    ...
    averageFPS = 30.179
    ...
    
  5. P90 및 P99 FPS 계산: P90 및 P99 측정항목을 찾으려면 덤프에 제공된 totalFrames 및 presentToPresent 타이밍 히스토그램을 분석해야 합니다.

    totalFrames = 1000
    ...
    presentToPresent histogram is as below:
    0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=0 9ms=0 10ms=0 11ms=0 12ms=0
    13ms=0 14ms=0 15ms=0 16ms=850 17ms=0 18ms=0 19ms=0 20ms=0 21ms=0 22ms=0 23ms=0
    24ms=0 25ms=0 26ms=0 27ms=0 28ms=0 29ms=0 30ms=0 31ms=0 32ms=0 33ms=100 34ms=0
    36ms=0 38ms=0 40ms=0 42ms=0 44ms=0 46ms=0 48ms=0 50ms=35 54ms=0 58ms=0 62ms=0
    66ms=10 70ms=0 74ms=0 78ms=0 82ms=0 86ms=0 90ms=0 94ms=0 98ms=0 102ms=5 106ms=0
    110ms=0 114ms=0 118ms=0 122ms=0 126ms=0 130ms=0 134ms=0 138ms=0 142ms=0 146ms=0
    150ms=0 200ms=0 250ms=0 300ms=0 350ms=0 400ms=0 450ms=0 500ms=0 550ms=0 600ms=0
    650ms=0 700ms=0 750ms=0 800ms=0 850ms=0 900ms=0 950ms=0 1000ms=0
    

    A. 개념적 예시 (누적 분포 표) 게임 세션에서 totalFrames 수가 1,000으로 기록되었다고 가정합니다. P90과 P99를 찾으려면 가장 낮은 밀리초 버킷부터 누적 프레임 수가 각각 900프레임 (90%)과 990프레임 (99%)에 도달하는 밀리초 기준을 계산합니다.

    프레임 시간 (ms) 프레임 수 (히스토그램) 누적 프레임 수 백분위수 상태 / 계산
    16ms 850 850 85.0%
    33ms 100 950 95.0%
    (P90 타겟 900 달성 → 1000/33 = 30.3FPS)
    50ms 35 985 98.5%
    66ms 10 995 99.5%
    (P99 목표 990 달성! → 1000/66 = 15.1 FPS)
    102ms 5 1,000 100%

    B. 구현 로직 (의사코드) Python 스크립트나 로그 파서를 사용하여 이 분석을 자동화하는 경우 히스토그램에서 P90 및 P99 값을 추출하는 로직은 다음과 같이 구현할 수 있습니다.

    # Define target thresholds based on total frame count
    p90_target = totalFrames * 0.90
    p99_target = totalFrames * 0.99
    
    cumulative_frames = 0
    p90_fps = None
    p99_fps = None
    
    # Iterate through the parsed SurfaceFlinger histogram data (sorted by millisecond)
    for ms_bucket, frame_count in present_to_present_histogram:
        cumulative_frames += frame_count
    
        # Capture P90 when cumulative frames cross the 90% threshold
        if p90_fps is None and cumulative_frames >= p90_target:
            p90_fps = 1000 / ms_bucket
    
        # Capture P99 when cumulative frames cross the 99% threshold
        if p99_fps is None and cumulative_frames >= p99_target:
            p99_fps = 1000 / ms_bucket
            break # Optimization: stop iterating once both targets are found
    
  6. 캡처 중지: 필요한 정보를 모두 수집한 후 사용 중지 플래그를 사용하여 timestats를 사용 중지해야 합니다.

    adb shell dumpsys SurfaceFlinger --timestats -disable
    

느린 세션

느린 세션은 광범위한 실제 성능 문제를 식별합니다. 프레임의 25% 이상이 기준점 (예: 20FPS) 미만으로 떨어지면 세션이 '느린' 세션입니다. 이 측정항목은 심각한 빌드 문제를 파악하는 데 유용하지만, 이 측정항목만으로는 고품질의 지속 가능한 환경을 보장할 수 없습니다. 게임이 느린 세션 기준을 피할 수 있지만 매끄러운 60FPS 환경을 저해하는 마이크로 스터터링이 발생할 수 있습니다.

프레임 시간에서 파생되지만 '느린 세션'과 '프레임 속도'는 서로 다른 역할을 합니다. 평균, P90, P99 FPS 측정항목은 성능의 품질과 지속 가능성을 측정하여 느린 세션 측정항목에서 간과할 수 있는 순간적인 드롭과 일관되지 않은 페이싱을 감지합니다.

결론

성공적인 실적 최적화에는 포괄적인 전략이 필요합니다. 개발자는 심각한 성능 저하를 포착하는 기본 레이더로 느린 세션을 사용한 다음 평균 FPS, P90, P99를 검사하여 기본 원인을 진단하고 실제 게임플레이의 원활함을 확인해야 합니다. 이러한 측정항목을 통합하면 애플리케이션이 지속 가능하고 뛰어난 사용자 환경을 일관되게 제공할 수 있습니다.

추가 리소스

고급 프로파일링 기술, 프레임 페이싱 API 구현, 엔진별 최적화 전략에 대해 자세히 알아보려면 공식 Android 개발자 문서를 참고하세요.

  • Android vitals: 느린 세션: Google Play에서 사용자 환경에 직접적인 영향을 미치는 지속적인 느린 렌더링 기간을 측정하고 보고하는 방법을 알아봅니다. '느린 세션'은 프레임의 25% 이상이 느린 (예: 50ms 이상, 20FPS에 해당) 사용자 세션으로 정의됩니다.
  • Android 개발자: 게임 성능 최적화: Android 게임 최적화의 중앙 허브를 살펴보세요. 이 포괄적인 가이드에서는 게임의 전반적인 성능을 극대화하는 데 도움이 되는 권장사항, 프로파일링 도구 (예: APAPerfetto)를 다룹니다.