Open5

yuniframe: スレッドのQoSインターフェース

okuokuokuoku

いくつかのOSではスレッドのQoSを設定できるようになっている。例えば、WindowsにはMMCSSサービスがあり、オーディオスレッドは遅延が少くなるように調整される。

残念ながら各社に共通集合は無いのでどういう形で抽象化するかはちょっと考える必要がある。

okuokuokuoku

Apple (Darwin)

iOSやmacOSはMachの影響を強く受けていてスレッドの代わりにタスクの語がよく使われる。

最悪の場合、一応AppleでもEコアPコアそれぞれにスレッドアフィニティを設定できるが、そのためにはsysctlbynameでトポロジを取る必要がある https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_system_capabilities

Workgroup

スレッドよりも抽象化されたものとしてWorkgroupを用意している。

AudioWorkIntervalCreate から interval オブジェクトを受けて、それに合わせてスケジュールするように要請できる。

pthread QoS

Energy Efficiency Guide for Mac Apps にひととおり解説がある https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/PrioritizeWorkAtTheTaskLevel.html が、このドキュメントはメンテナンスされていない。

pthreadのAPIとしては pthread_attr_set_qos_class_np が出ている。

https://github.com/apple-oss-distributions/libpthread/blob/d8c4e3c212553d3e0f5d76bb7d45a8acd61302dc/src/qos.c#L632-L645

QOS_CLASS_USER_INTERACTIVE QOS_CLASS_USER_INITIATED QOS_CLASS_UTILITY QOS_CLASS_BACKGROUND の4段階が定義されているが、pthreadのインターフェースでは USER_INTERACTIVE は公開されていない(main thread用)。

okuokuokuoku

Windows

Windows(NT)は長いことマルチスレッドOSとして機能してきたが、いわゆるリアルタイムOSとしての歴史はあまりない。

E-Core/P-Coreの運用をカーネルに任せる

https://www.isus.jp/wp-content/uploads/pdf/Game Dev Guide for Alder Lake Performance Hybrid Architecture_JA.pdf

2.6.5. ハイブリッド・オペレーティング・システム (OS) の検出

Windows* 11 では、インテル® スレッド・ディレクター (ITD) のハードウェア・ヒントを利用して、OS がスレッドを自動的にスケジューリングできます。スレッドのスケジュールを OS や ITD に任せるには、最初にアプリケーションが動作している Windows* のバージョンを検出する必要があります。

Windows11以降では、スレッドの命令ミックスに応じてE-Core/P-Coreの振り分けを行える。

MMCSS と QoS

MMCSS(Multimeda Class Scheduler Service) https://learn.microsoft.com/en-us/windows/win32/procthread/multimedia-class-scheduler-service は名前付きの優先度プロファイルをレジストリに定義させ、 AvSetMmThreadCharacteristics 等のAPIで個々のスレッドをそのプロファイルに設定できる。

MMCSSを使うと、カーネルが管理するスレッドにQoS https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service を設定できる。MMCSSはQoSを設定するための唯一のインターフェースではなく、 SetThreadInformation でEcoQoS/HighQoS等の設定もできる。

Win32スレッドAPI

通常のユーザーのプロセスにもTime critical優先度を公開 https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities している。

okuokuokuoku

Android

AndroidはDarwinやWindowsに見られるような明示的なインターフェースではなく、もう一段階抽象化したPerformance Hintインターフェースを提供している。

Performance Hint API

https://developer.android.com/games/optimize/adpf/performance-hint-api

Androidでは、そもそもアフィニティの設定自体を推奨していない。

このため、デバイスは一般に、ゲームのプロセッサ アフィニティを無視します。

(この記述はあるものの、実際のところどうなのかは不明 -- Performance Hint APIはAndroid12以降の導入)

アフィニティの代わりに、Performance Hint APIを通じて "セッション" を取得し設定することを求めている。このインターフェースにはNDK(C版)も用意されている https://developer.android.com/ndk/reference/group/a-performance-hint

つまり:

  1. APerformanceHint_createSession に必要なフレームあたりのCPU処理時間(ターゲットフレームレートの逆数)を渡してセッションを作成する
  2. (APerformanceHint_setThreads にスレッドIDのリストを渡して追加のスレッドをセッションに紐付ける)
  3. 処理が完了したら APerformanceHint_reportActualWorkDuration2 に実際の処理時間を渡す

Unity等のゲームエンジンはこれを行うプラグインを用意している。

WindowsやDarwinと違い、Androidはアプリケーションからのフィードバックを用意しているあたりが特徴的と言える。...そもそもフレームに関連付けられたスレッドはそれ以外の作業はしないことが多いと思うんだけど。。

アプリケーションからフィードバックする処理時間としては WorkDuration オブジェクトを使う。これにはCPUおよびGPU処理時間、負荷の開始時点の CLOCK_MONOTONIC 時間を含められる。

いわゆるEcoQoSを宣言するためには APerformanceHint_setPreferPowerEfficiency が使える。

その他のAPDF機能

https://developer.android.com/games/optimize/adpf?hl=ja

APDF(Android Dynamic Performance Framework)としてサーマルやゲームモードをサポートしている。

okuokuokuoku

Linuxのpthread

Linuxを含めpthreadプラットフォームでは SCHED_RRSCHED_FIFO の固定優先度ポリシーが使えることが多い。

https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/9/html/monitoring_and_managing_system_status_and_performance/static-priority-scheduling-with-sched_fifo_tuning-scheduling-policy

https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/9/html/monitoring_and_managing_system_status_and_performance/round-robin-priority-scheduling-with-sched_rr_tuning-scheduling-policy

また、Linuxではカーネルのスケジューラーとして SCHED_DEADLINE 等の拡張が存在する。これらはschedparamで直接指定する必要がある。

https://www.kernel.org/doc/html/latest/scheduler/sched-deadline.html