RFC-0123:CPU 效能資訊系統呼叫

RFC-0123:CPU 效能資訊系統呼叫
狀態已接受
區域
  • 核心
說明

與核心通訊的介面,可用於 CPU 效能

Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2021-07-20
審查日期 (年-月-日)2021-08-18

摘要

本 RFC 提出了一種機制,讓使用者空間代理程式可與核心互動,以便更新核心排程器使用的效能刻度,並查詢其狀態。

提振精神

為了在 big.LITTLE 等異質架構中,有效地在多個 CPU 上排程工作,Zircon 核心排程器會模擬 CPU 的相對效能。在撰寫本文時,描述這些相對成效的成效評估標準是靜態的,由 ZBI 中的資料提供。

對 big.LITTLE 系統執行 CPU 熱控時,大核心和小核心的頻率通常不會以相同的因素進行調整,因此相對效能會動態變化。與大多數其他作業系統不同,在 Fuchsia 中,核心頻率的修改作業會在使用者空間中執行,且排程器必須跨核心邊界通知相對 CPU 效能的變更。這項通訊需要新的系統呼叫。

設計

效能評分

概念

在考慮建議的系統呼叫之前,建議您先瞭解效能規模的概念,因為這個概念已存在於核心排程器中。效能等級是指 CPU 以目前速度運作時,其效能與系統相關參考效能的比率,其中效能可使用任何適當的指標進行評估,例如 DMIPS。在撰寫本文時 (但不一定會在未來),參考效能是指以最高速度運作的最強大 CPU,因此 1.0 是最高效能評分值。通常,供應商會為每個以額定速度運作的 CPU 提供效能值,且效能會與 CPU 頻率成線性變化。

舉例來說,在 big.LITTLE 系統中,供應商可能會提供效能資料,指出大核心在最高速度下執行的 DMIPS 是小核心在其最高速度下執行的兩倍。如果參考效能對應於以最高速度運作的大核心,則該運作條件對應的效能等級為 1.0,而以最高速度運作的子核心則會具有 0.5 的效能等級。將大核心的速度降低 25%,可讓其效能等級為 0.75,而將小核心的速度降低 25%,可讓其效能等級為 0.375。

更精確地說,如果 fref 是參考頻率,且具有已知的效能比例 sref,那麼頻率 fnew 就會具有效能比例 snew=sreffnew/fref。一般來說,系統中每個不同的 CPU 架構都需要一個參考頻率。

一般來說,特定系統只支援固定數量的頻率組合。舉例來說,同一個叢集中的 CPU 通常必須具有相同的頻率,且每個叢集只支援相對較少的不同頻率。不過,追蹤哪些效能刻度有效,超出核心的範圍。因此,核心會信任使用者空間提供的實際值,並盡可能使用透過建議 API 提供的值。

固定小數表示法

為避免使用浮點數,效能刻度會使用結構體指定的固定點數表示

  typedef struct zx_cpu_performance_scale {
    uint32_t integral_part;
    uint32_t fractional_part;  // Increments of 2**-32
  } zx_cpu_performance_scale_t;

integral_partfractional_part 分別描述整數和小數部分,fractional_part 指定 2-32 的增量。實數和固定點表示法之間的轉換應按照下列函式執行:

zx_status_t ToFixedPoint(double real, zx_cpu_performance_scale_t* scale) {
  double integer;
  double fraction = std::modf(real, &integer);

  // Converting from double to fixed point should fail if the input's integer
  // part is too large.
  if (integer > static_cast<double>(UINT32_MAX)) {
    return ZX_ERR_INVALID_ARGS;
  }

  scale->integral_part = static_cast<uint32_t>(integer);

  // Rounding down the fractional part is suggested but should not matter
  // much in practice. A difference of 1 in the output is a difference of only
  // 2**-32 in the corresponding real value.
  scale->fractional_part = static_cast<uint32_t>(std::ldexp(fraction, 32));

  return ZX_OK;
}

double FromFixedPoint(zx_cpu_performance_scale_t scale) {
  return static_cast<double>(scale.integral_part)
    + std::ldexp(scale.fractional_part, -32);
}

Syscall 1:zx_system_set_performance_info

第一個系統呼叫可讓使用者空間代理程式設定核心排程器使用的效能比例:

zx_status_t zx_system_set_performance_info(
    zx_handle_t resource,
    uint32_t topic,
    const void* new_info,
    size_t info_count
);

其引數如下:

  • resource:授予此呼叫權限的資源。必須是 ZX_RSRC_SYSTEM_CPU_BASE,這是專為此 API 推出的新資源,否則呼叫會失敗。

  • topic:此呼叫參照的表現類型。必須是 ZX_CPU_PERF_SCALE,這會在提案實作時定義。

  • new_info:有效的 zx_cpu_performance_info_t[],其元素由

    typedef struct zx_cpu_performance_info {
        uint32_t logical_cpu_number;
        zx_cpu_performance_scale_t performance_scale;
    } zx_cpu_performance_info_t;
    

    其中 zx_cpu_performance_t上述定義

    logical_cpu_number 會使用核心使用的相同編號系統,指定結構體所描述的 CPU 每個 logical_cpu_number 都必須是有效的 CPU ID。new_info 的元素必須依 logical_cpu_number 的遞增順序排序 (因此,每個 logical_cpu_number 只能出現一次)。

    performance_scale 代表指定 CPU 的新效能等級,應與前述所述的 CPU 新頻率相符。不過,核心不會根據支援的 CPU 頻率驗證輸入內容;任何正值都可以做為輸入內容。

    {.integral_part = 0, .fractional_part = 0} 的輸入比例無效,因此請勿將其與要求將核心離線的程序混淆,該程序採用不同的機制,日後將採用不同的 API。

    核心可能會在內部將有效輸入值覆寫為排程器可使用的最接近值。舉例來說,在撰寫本文時,所支援的最大效能比例為 1.0。因此,如果 performance_scale 代表的值大於 1.0,則核心會在內部將其箝制為 {.integral_part = 1, .fractional_part = 0}

    如果呼叫 zx_system_set_performance_info 失敗,則核心不會採取任何行動,new_info 也沒有任何效果。

    如果呼叫成功,核心排程器會從下一個重新排程作業開始,使用與 new_info 對應的修改版效能刻度,而這項作業通常會在呼叫傳回後的某個時間點發生。核心不會修改 new_info 中未參照的 CPU 的效能等級。

    這個呼叫所做的變更會持續存在,直到重新啟動或進一步使用此 API 覆寫為止。

  • info_countnew_info 中的元素數量。必須為正數,且不得超過系統中的 CPU 數量。

錯誤狀況

ZX_ERR_BAD_HANDLE

  • resource 不是有效的句柄。

ZX_ERR_WRONG_TYPE

  • resource 不是有效的資源句柄,或不是 ZX_RSRC_KIND_SYSTEM 類型。

ZX_ERR_INVALID_ARGS

  • topic 不是 ZX_CPU_PERF_SCALE
  • new_info 是無效的指標。
  • new_info 並未依遞增的 logical_cpu_number 排序。

ZX_ERR_OUT_OF_RANGE

  • resource 屬於 ZX_RSRC_KIND_SYSTEM,但不等於 ZX_RSRC_SYSTEM_CPU_BASE
  • info_count0 或超過 CPU 數量。
  • logical_cpu_number 無效。
  • 輸入的 performance_scale{.integral_part = 0, .fractional_part = 0}

預定用途

zx_system_set_performance_info 應用於在 CPU 頻率變更時,通知核心 CPU 效能變化。由於不同的 CPU 可能由不同的實體控制,因此 API 僅支援指定部分 CPU 的效能比例。

如果要降低 CPU 的頻率,建議在頻率變更發生前呼叫 zx_system_set_performance_info。這樣一來,核心排程器就能在 CPU 容量降低前,減少該 CPU 的負載。(排程器應快速回應,不需進一步協調;這項功能實作後,我們會確認這項預期)。

反之,如果要增加 CPU 的頻率,建議在頻率變更後才呼叫 zx_system_set_performance_info,並在可用時通知排程器新的容量。

無論是哪種情況,如果 CPU 頻率更新失敗,呼叫端必須根據產生的 CPU 狀態更新核心排程器。呼叫端應嘗試判斷失敗後的 CPU 頻率,並使用該頻率通知對 zx_system_set_performance_info 的個別呼叫。如果無法判斷頻率 (例如相關聯的驅動程式庫已完全失敗),則呼叫端應對產生的 CPU 速度做出悲觀 (低) 的預測。這項建議可能會隨著進一步考量而有所變動,例如 https://fxbug.dev/42165500

新的 API 最終將由即將開發的「CPU Manager」元件使用,該元件負責管理 CPU 的使用者空間。如要修改 CPU 頻率,代理程式會向 CPU 管理工具註冊要求,而非直接與 CPU 驅動程式互動,如本提案所述,CPU 管理工具會協調頻率變更與核心更新。

CPU 管理員也會從電源管理工具接手 CPU 的溫度節流作業,這是這項提案的動機用途。

Syscall 2:zx_system_get_performance_info

第二個系統呼叫會擷取所有 CPU 的效能資訊:

zx_status_t zx_system_get_performance_info(
    zx_handle_t resource,
    uint32_t topic,
    void* info,
    size_t info_count
    size_t* output_count
);

其引數如下:

  • resource:授予此呼叫權限的資源。必須為 ZX_RSRC_SYSTEM_CPU_BASE

  • topicZX_CPU_PERF_SCALEZX_CPU_DEFAULT_PERF_SCALE,會在提案實作時定義。主題會決定寫入 info 的內容,如下所述。

  • info:有效的 zx_cpu_performance_info_t[],長度等於系統中的 CPU 數量。

    如果呼叫失敗,info 就不會修改。

    如果呼叫成功,則返回時 info 會為每個 CPU 包含一個元素,並依 logical_cpu_number 的遞增順序排序。每個元素的 performance_scale 會根據 topic 填入:

    • ZX_CPU_PERF_SCALEperformance_scale 會儲存指定 CPU 的目前核心效能等級。即使下一個重新排程作業尚未執行,系統提供的值仍會反映最近一次對 zx_system_set_performance_info 的呼叫。

    • ZX_CPU_DEFAULT_PERF_SCALEperformance_scale 會儲存核心在啟動時,針對指定 CPU 使用的預設效能等級。

  • info_countinfo 陣列的長度;必須等於系統中的 CPU 數量。

  • output_count:如果呼叫成功,則會包含寫入 info 的元素數量。如果呼叫失敗,則其值未指定。

錯誤狀況

ZX_ERR_BAD_HANDLE

  • resource 不是有效的句柄。

ZX_ERR_WRONG_TYPE

  • resource 不是有效的資源句柄,或不是 ZX_RSRC_KIND_SYSTEM 類型。

ZX_ERR_INVALID_ARGS

  • topic 不是 ZX_CPU_PERF_SCALEZX_CPU_DEFAULT_PERF_SCALE
  • info 是無效的指標。

ZX_ERR_OUT_OF_RANGE

  • resource 屬於 ZX_RSRC_KIND_SYSTEM,但不等於 ZX_RSRC_SYSTEM_CPU_BASE
  • info_count 不等於系統中的 CPU 總數。

預定用途

ZX_CPU_PERF_SCALE 下的行為可讓使用者空間代理程式查詢效能等級,以利診斷。這項功能可能會很實用,例如代理程式可在系統首次啟動時評估系統狀態,或用於當作當機報告的訊號。

ZX_CPU_DEFAULT_PERF_SCALE 中的行為可讓代理程式確認其設定的效能縮放與核心使用的效能縮放一致。

實作

核心

  • 新的系統呼叫必須實作,並由新的資源 ZX_RSRC_SYSTEM_CPU_BASE 控管。

  • 您必須修改核心排程器,以便支援動態效能等級,並將其更新為使用 zx_system_set_performance_info 提供的最新值,並將目前使用的預設效能等級公開給 zx_system_get_performance_info

元件管理員

您必須定義新的通訊協定 CpuResource,並由元件管理服務工具實作,才能提供 ZX_RSRC_SYSTEM_CPU_BASE 資源。這項功能遵循系統中現有的模式,適用於限制系統呼叫的資源。

成效

新的系統呼叫本身執行所需的時間微乎其微,因為它們只會接觸與 CPU 數量成比例的少量資料。

使用 zx_cpu_set_performance_info 會導致排程器以不同的方式分配工作,將工作轉移至效能等級相對於所有效能等級總和增加的核心,並遠離效能等級相對於所有效能等級總和減少的核心。重新排程程序本身不會對排程器造成大量負載。

重新排程會導致系統效能出現預期變化。測試這些變更等同於測試排程器的功能正確性,相關說明請參閱「測試」一節。

安全性考量

這兩個新的系統呼叫都受到新資源句柄 ZX_RSRC_SYSTEM_CPU_BASE 的限制。對於 zx_system_set_performance_info,這項防護措施可解決排程器遭到惡意干擾的疑慮。對於 zx_system_get_performance_info,資料外洩問題更為微妙;不受信任的實體不應得知核心的效能規模,因為這通常會提供系統支援的 P 狀態資訊。

隱私權注意事項

這項提案對隱私權沒有重大影響。

測試

  • 我們會新增核心測試,以便執行基本成功和失敗條件。
  • 我們會新增單元測試,驗證排程器處理更新的效能評量標準的方式。這些測試會驗證,如果截止時間執行緒繫結至 CPU,且 CPU 的效能等級經過係數 α 修改,則分配給執行緒的實際時間會乘以 1/α。

說明文件

Zircon 系統呼叫說明文件將更新,納入新的 API。

缺點、替代方案和未知事項

通用性

我們考慮了更通用的介面,例如 zx_set_cpu_properties 系統呼叫,最終可處理核心和 CPU 之間的其他互動,例如離線。最終,我們選擇了較窄的介面,因為這個介面的客戶預期很少,因此未來對所提介面進行變更的成本相對較低。在更一般性的介面上放置要求,目前只能靠猜測。

替代呼叫結構

除了 zx_system_set_performance_info 的 set-only 作業之外,我們也考慮了結合 get/set 作業的做法,針對已修改比例的 CPU 傳回先前的效能比例。這項功能旨在確保呼叫端能夠在執行相關頻率變更的低階執行作業失敗時,還原效能等級變更。

不過,進一步考量後發現,單純將變更內容還原是不夠的。這會導致更複雜的失敗處理建議,並導致更簡單的僅限集合運算。

最後,zx_system_get_performance_info 需要支援密封測試,在這種情況下,直接回復變更適當的做法,且支援診斷用途。

替代 CPU 索引

我們考慮使用其他索引 CPU 的配置,例如以物理 CPU 編號來參照。不過,由於核心不需要這種配置,因此讓 API 使用核心現有的邏輯 CPU 編號,最符合 Zircon 的限制範圍。這些編號在特定系統中一致,用戶端可以維護靜態的個別主機設定,以便參照這些編號,或從 ZBI 存取設定資料。

效能評估的替代方案

我們認為,新 API 可能會使用「速度係數」,而非直接參照效能等級,以便排程器將其套用至特定 CPU 的基本效能等級。這樣一來,用戶端就不需要瞭解太多特定情境的資訊,只需要知道 CPU 的新頻率與額定頻率之間的比例,而不需要瞭解 CPU 之間的相對效能。

我們選擇不採用這種做法,因為效能比例的用途是用於異質系統的 CPU 溫度調節,因此這個 API 的預期用戶不會從使用速度因子中獲得任何實質好處。同時,我們必須定義新概念,並修改排程器以便使用新概念,因此會產生成本。

最高效能比例

這項提案原本是使用 uint32_t 表示效能等級,該值代表 [0.0, 1.0] 中的實際值。具體來說,這個值允許的最大值為 1.0。

雖然 1.0 是 Zircon 排程器在撰寫時支援的最大效能等級,但我們決定允許輸入值大於 1.0 的值,以便支援日後的用途,例如 Turbo 模式。此外,先前的表示法並非固定點,因此導致排程器無法直接使用這些值。

performance_scale 的表示法

performance_scale 原本是 uint64_t,其中上半部的 32 位元會保留整數部分,下半部的 32 位元會保留小數部分。這會在 zx_cpu_performance_info_t 的欄位之間產生 32 位元填充,導致潛在的洩漏向量。新的表示法可避免這個陷阱。

performance_scale 的有效值

我們已仔細考量 zx_system_set_performance_info 應允許哪些值做為 performance_scale 的輸入內容。我們判定代表 0.0 的值太容易與 CPU 離線指令混淆,而這項動作目前並未獲得 Zircon 支援,但預計未來會使用其他 API。因此,系統判定代表 0.0 的值為錯誤。

請特別留意極小的值。舉例來說,{.integral_part = 0, .fractional_part = 1} 的輸入值會代表 2-32,而這可以合理地視為 0.0,有效地將對應的核心設為離線。雖然可以透過強制規定允許的最低值來解決這個問題,但目前任何這類門檻都是任意設定,也會進一步複雜化核心和使用者空間之間的合約。我們認為,將新 API 視為提示機制是最簡單的方法,讓核心在需要時可自由覆寫輸入內容,而不會揭露與此選擇相關的內部詳細資料。

日後的作業

設定管理

理想情況下,使用者空間代理程式會使用 ZBI 共用核心排程器使用的完全相同的 CPU 設定資料。目前尚不清楚是否可行。

此外,您必須小心確保核心和使用者空間代理程式會將預設效能量尺與相同的標稱頻率建立關聯。

效能評分標準的下限

原則上,排程器可根據目前的截止時間執行緒和 CPU 負載,判斷系統應維持的最低效能規模。這些邊界動態版本會是使用者空間代理程式的重要輸入內容,因為該代理程式會嘗試利用較低的 CPU 頻率來提高能源效率。zx_system_get_performance_info 的其他選項會提供自然的公開方式。

CPU 歸因

您應建立某些方法,將執行緒的歸屬 CPU 時間與其排定的 CPU 效能建立關聯。這類關聯已與建立效能指標相關,這些指標可在大型核心和小型核心上穩健排程,而隨著我們開發頻率修改機制 (如這項提案),這項關聯就變得更加重要。

保證執行節流代理程式

執行溫度調節時,降低 CPU 頻率可能會導致 CPU 飢餓,進而導致節流代理程式的程序不太可能按時排程。執行節流代理程式時,應以適當方式優先處理。

既有技術與參考資料

將 CPU 頻率控制權委派給使用者空間對作業系統而言並不常見,因此無法提供這方面的先前技術。