개발

Clutch가 있는 iOS와 Android의 체감 차이를 스케줄러 관점에서 보기

Clutch와 EEVDF를 비교하다 보니 자연스럽게 다음 질문이 생겼다. 이 차이가 실제 기기를 만지는 사용자에게는 어떻게 느껴질까. 단순히 “iOS가 빠르다”거나 “Android가 느리다”로 정리하면 너무 거칠다. 사용자가 느끼는 반응성은 스케줄러뿐 아니라 SoC, 메모리, 디스플레이, 런타임, 앱 구현, 발열 정책까지 같이 만든다.

그래도 스케줄러 관점에서만 떼어 보면 기억할 만한 축은 하나다. Clutch는 사용자가 기다리는 workload의 의미를 더 직접적으로 보려 하고, Android/Linux 쪽은 EEVDF, EAS, uclamp, task profiles 같은 장치를 조합해 공정성, 에너지, 정책 힌트를 맞춘다.

iOS Clutch와 Android 스케줄러 개선 포인트

내가 이해한 개념

Clutch가 흥미로운 이유는 thread 하나만 보지 않는다는 점이다. Apple XNU의 Clutch 문서는 Scheduling Bucket, Thread Group, Thread의 3단계 구조를 설명한다. 특히 thread group level은 특정 workload를 위해 함께 움직이는 thread 묶음을 다루고, interactive application을 compute-intensive batch workload보다 선호하도록 설계되어 있다.

내 말로 바꾸면 Clutch는 “이 thread가 CPU를 얼마나 썼나”만 보지 않고, “이 thread가 지금 사용자가 기다리는 일의 일부인가”를 같이 본다. 그래서 UI와 직접 이어진 앱 작업, 백그라운드 daemon, 계산 위주의 batch 작업을 같은 의미로 줄 세우지 않으려 한다.

Android 쪽은 한 장으로 끝나지 않는다. Linux의 EEVDF는 lag가 0 이상인 task 중 virtual deadline이 가장 빠른 task를 고른다. Energy Aware Scheduling은 이기종 CPU에서 에너지 모델을 보고 task를 어느 CPU에 둘지 판단한다. uclamp는 task나 cgroup이 필요한 최소/최대 성능 지점을 힌트로 줄 수 있게 한다. Android는 여기에 cgroup과 task profiles를 얹어 top-app 같은 그룹 이동과 성능 정책을 추상화한다.

내가 받아들인 차이는 이렇다. Clutch는 workload 의미를 kernel scheduler 안에서 더 강하게 들고 가는 쪽이고, Android는 범용 Linux scheduler 위에 Android framework와 vendor 정책이 힌트를 주는 쪽에 가깝다.

사용자가 느낄 수 있는 차이

  • 터치 직후 반응성은 iOS 쪽이 더 일정하게 느껴질 가능성이 있다. Clutch가 interactive thread group과 앱 workload를 구분해 다루기 때문이다.
  • 스크롤 중 백그라운드 작업이 섞일 때도 iOS는 사용자가 보고 있는 작업을 더 강하게 보호하는 느낌을 줄 수 있다.
  • Android도 top-app, foreground, background 분류와 uclamp/EAS를 통해 비슷한 보호를 할 수 있다. 다만 SoC, 커널 버전, vendor task profile, thermal policy에 따라 편차가 생기기 쉽다.
  • Android에서 순간 boost를 세게 주면 처음에는 빠르게 느껴질 수 있지만, 발열 후 throttling으로 긴 세션의 프레임 안정성이 흔들릴 수 있다.
  • 게임, 카메라, 지도처럼 오래 켜 두는 작업에서는 최고 성능보다 sustained performance가 더 중요해진다.

여기서 중요한 건 평균 CPU 성능이 아니다. 사용자는 평균 FPS보다 한 번 튀는 jank를 더 크게 느낀다. UI thread가 runnable 상태로 기다린 시간, SurfaceFlinger나 RenderThread가 frame deadline 근처에서 밀린 시간, input이 앱 처리까지 도달하는 시간이 체감에 더 직접적이다.

Android에서 개선할 수 있는 방향

  • top-app을 더 정확히 보호해야 한다. 앱 프로세스 하나만 보는 것이 아니라 input, app main thread, RenderThread, SurfaceFlinger, GPU submit 경로까지 한 프레임을 만드는 묶음으로 봐야 한다.
  • boost는 길게 주는 것보다 짧고 정확해야 한다. 터치 직후나 frame deadline 근처에 필요한 만큼 uclamp_min을 올리고, 프레임이 지나면 바로 낮추는 쪽이 낫다.
  • background 작업에는 상한이 필요하다. sync, indexing, logging, backup, ML prefetch 같은 작업은 화면이 꺼졌거나 충전 중이거나 idle일 때 크게 허용하고, 사용자가 만지는 순간에는 uclamp_max나 cgroup 정책으로 눌러야 한다.
  • binder chain을 따라가는 임시 보호가 필요하다. top-app의 요청을 처리하는 system_server, media, camera 관련 thread가 낮은 그룹에 남아 있으면 앱 thread만 올려도 끝까지 빨라지지 않는다.
  • EAS energy model과 thermal policy를 같이 봐야 한다. big core로 빨리 올리는 것만으로는 충분하지 않다. 어느 OPP에서 오래 버틸 수 있는지, 어떤 core 배치가 전체 전력에 유리한지까지 맞아야 한다.
  • 측정은 평균이 아니라 jank 중심으로 해야 한다. Perfetto나 ftrace로 runnable latency, missed frame, input latency, CPU frequency transition, thermal throttling 시점을 같이 봐야 한다.

Clutch를 그대로 Android에 옮기는 것이 답이라는 뜻은 아니다. Android는 기기 종류가 너무 많고, Linux scheduler와 vendor 정책의 조합으로 돌아간다. 대신 Clutch에서 가져올 수 있는 관점은 분명하다. 사용자가 지금 기다리는 작업 묶음을 더 명확히 식별하고, 그 묶음이 다른 일에 밀리지 않게 짧게 보호하는 것이다.

확인한 자료

  • Apple XNU의 sched_clutch.md는 Clutch의 Scheduling Bucket, Thread Group, Thread 구조와 interactive thread group 선호를 설명한다.
  • Linux kernel의 EEVDF 문서는 lag와 virtual deadline을 기준으로 다음 task를 고르는 흐름을 설명한다.
  • Linux kernel의 Energy Aware Scheduling 문서는 이기종 CPU에서 에너지 모델을 보고 task placement를 판단하는 방식을 설명한다.
  • Linux kernel의 utilization clamping 문서는 uclamp가 task의 성능 요구와 제한을 scheduler에 전달하는 힌트라고 설명한다.
  • Android의 Cgroup abstraction layer 문서cgroups.json, task_profiles.json, task_profiles 명령, top-app 그룹 이동 같은 추상화 흐름을 설명한다.

결론 및 리마인드

사용자가 느끼는 차이는 “어느 OS가 CPU를 더 빨리 쓰는가”보다 “내가 기다리는 일이 다른 일에 밀리지 않는가”에 가깝다. Clutch는 그 질문을 thread group과 workload 의미로 풀고, Android는 EEVDF, EAS, uclamp, task profiles를 조합해 풀어야 한다.

나중에 다시 보면 이 한 줄만 먼저 떠올리면 된다.

Android에서 개선할 지점은 Clutch를 그대로 복제하는 것이 아니라, foreground UI workload를 더 정확히 묶고 더 짧게 보호하는 것이다.