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

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

與核心通訊與 CPU 效能相關的介面

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

摘要

這個 RFC 建議一種機制,使用者空間代理程式可能會針對 CPU 效能而與核心互動,藉此更新核心排程器使用的效能量表,並查詢其狀態。

提振精神

為了在異質架構中有效排程跨 CPU 的工作 (例如 large.LITTLE),Zircon 核心排程器會為 CPU 的相對效能建立模型。撰寫本文時,描述這些相對效能的效能量表是靜態的,由 ZBI 中的資料提供。

對大型 LITTLE 系統執行過熱 CPU 節流時,大型和小型核心的頻率通常不會依相同的因素縮放,因此相對效能會有動態變化。不同於大多數其他作業系統,在 Fuchsia 中,有關核心頻率的修改作業會在使用者空間中執行,而且排程器必須在核心邊界收到與相對 CPU 效能發生變化的通知。通訊需要新的系統呼叫。

設計

效能規模

概念

在評估建議的系統呼叫之前,建議您先瞭解核心排程器中已有的效能量表概念。效能量表說明 CPU 作業在目前速度與系統相依參照效能的比率,使用任何合適的指標 (例如 DMIPS) 都可以測量效能。撰寫當時 (但不一定在日後) 的參考效能是最強大的 CPU 搭載最高速度的運作,因此 1.0 是最高效能擴充值。通常,廠商會為各個 CPU 提供以名計的速度提供效能值,而效能值會假設效能會因 CPU 頻率而異。

例如,在大型 LITTLE 系統中,廠商可能會提供效能資料,指出該大核心的速度是最大速度的兩倍,因此 DMIPS 是以自己的最大速度運作的輕微核心。如果參照效能對應於以最高速度執行的大核心,則該運作條件對應的效能規模為 1.0,而最大速度的小核心則的效能規模為 0.5。將大型核心的速度降至 25%,可讓新的效能向達到 0.75,同時將小型核心的速度降低 25%,也將效能規模變更為 0.375。

更精確來說,如果參照是具有已知效能量表參照的參考頻率,則頻率 fnew 會以效能尺度為new=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}

預定用途

每當 CPU 頻率變更時,都應使用 zx_system_set_performance_info 通知 CPU 效能變更的核心。這個 API 僅支援部分 CPU 的效能調整規格,因為不同的 CPU 可由不同實體控管。

如果將 CPU 頻率降低,建議在頻率變更發生前呼叫 zx_system_set_performance_info。這麼做可讓核心排程器在容量減少前,先降低該 CPU 的負載。(排程器的回應速度應夠快,因此不需進一步協調;導入支援後,將確認這項預期)。

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

無論是哪一種情況,當 CPU 頻率更新失敗時,呼叫端都必須根據產生的 CPU 狀態更新核心排程器。呼叫端應嘗試判斷失敗後的 CPU 頻率,並以此通知獨立的 zx_system_set_performance_info 呼叫。如果無法判斷頻率 (例如相關聯的驅動程式庫失敗時),呼叫端應以合理 (低) 的猜測,判斷產生的 CPU 速度。這項建議可能會隨著進一步考量而改變,例如:https://fxbug.dev/42165500

這個新的 API 最終將由開發中的「CPU 管理員」元件用於使用者空間管理 CPU。因此,希望修改 CPU 頻率的代理程式不會與 CPU 驅動程式直接互動,而希望修改 CPU 頻率的代理程式會向 CPU 管理員註冊要求,而該作業會依照本提案所述,協調核心更新頻率變更。

CPU 管理員也會從 Power Manager 接手 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 底下的行為可讓代理程式確認效能等級與核心使用中的規模一致。

實作

核心

  • 新的 Sys 呼叫必須實作,並由新的資源 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 之間的額外互動,例如嵌入。最後,我們選擇採用狹長型的介面,因為預期這個介面上的用戶端很少,因此未來變更預定介面時的成本相對較小。目前在較一般介面設置的要求通常可以猜測。

替代呼叫結構

請考慮合併的 get/set 作業,取代 zx_system_set_performance_info 的純設定作業。這個運算可回傳已修改資源調度的 CPU 先前的效能擴充。這是為了讓呼叫端能夠在相關頻率變更失敗時,讓呼叫端可還原效能規模變更的變更。

但進一步考量,直接還原變更並不足夠。這會產生一組更複雜的失敗處理建議,並改回較簡單的僅限設定作業。

最後,需要使用 zx_system_get_performance_info 才能支援密封測試,在這類情況下,應「直接」還原變更,並支援診斷用途。

替代 CPU 索引建立功能

我們考慮採用替代的配置來為 CPU 建立索引,例如透過實體 CPU 編號參照這些配置。然而,由於核心不需要其他配置,因此能與 Zircon 的限制範圍一致,讓 API 使用核心現有的邏輯 CPU 編號。這些數字在特定系統中會保持一致,而用戶端可以維持靜態的單板設定,以參照這些 ID,或可能從 ZBI 存取其設定資料。

效能規模的替代方案

我們考慮的是,新 API 可能會採用「速度係數」,而非直接指稱效能調整,排程器會將排程器套用至指定 CPU 的基本效能規模。這麼做可減少用戶端需要瞭解的情境專屬資訊量;他們只需要知道 CPU 新頻率與名目頻率之間的比率,而不是瞭解 CPU 之間的相對效能。

我們選擇不採用這個做法,是因為效能規模旨在做為異質系統中 CPU 溫度節流的基本方式,因此這個 API 預期的用戶端不會因使用速度係數而受益。同時,定義新概念並修改排程器以便使用,會產生費用。

最高效能規模

此提案最初使用代表 [0.0, 1.0] 中實際值的 uint32_t 表示效能量表。請特別注意,允許的最大值為 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 頻率控制對使用者空間的責任委派代表不大,因此相關主題無法進行先前有關。