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

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

與核心通訊,瞭解 CPU 效能的介面

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

摘要

這項 RFC 提議一種機制,讓使用者空間代理程式可與核心互動,瞭解 CPU 效能,並更新核心排程器使用的效能比例,以及查詢其狀態。

提振精神

為了在異質架構 (例如 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);
}

系統呼叫 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 的修改效能比例,一般來說,呼叫傳回後會發生這種情況。如果 CPU 未在 new_info 中參照,核心就不會修改其效能比例。

    這項呼叫所做的變更會持續生效,直到重新啟動或進一步使用這個 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 效能的變更。由於不同 CPU 可能由不同實體控制,因此 API 僅支援部分 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 的熱節流責任 (這項提案的動機使用案例),取代電源管理工具。

系統呼叫 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 下的行為可讓代理程式確認效能比例是否與核心使用的比例一致。

實作

Kernel

  • 必須實作新的系統呼叫,並以新的資源 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 會導致排程器以不同方式分配工作,將工作轉移至相對於所有效能比例總和,效能比例增加的 CPU 核心,並從效能比例減少的 CPU 核心轉移工作。重新排程程序本身不會對排程器造成大量負載。

重新排定時間會導致系統效能出現預期變化。測試這些變更等同於測試排程器是否功能正確,這部分會在「測試」中說明。

安全性考量

這兩個新系統呼叫都會受到新資源控制代碼的限制 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 syscall,最終可處理核心和 CPU 之間的額外互動,例如離線。最終,我們選擇採用窄介面,因為預期使用這個介面的用戶不多,因此未來對建議介面進行變更的成本相對較低。此時對更一般的介面設定要求,大多只能是猜測。

替代通話結構

除了 zx_system_set_performance_info 的僅設定作業外,我們也考慮採用合併的取得/設定作業,針對已修改比例的 CPU 傳回先前的效能比例。這是為了確保如果相關頻率變更的較低層級執行作業失敗,呼叫端能夠還原效能規模變更。

不過,經過進一步考量後,我們發現單純還原變更並不足以解決問題。這導致失敗處理建議更加複雜,因此我們回歸到較簡單的僅設定作業。

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

替代 CPU 索引

我們曾考慮使用替代方案為 CPU 編列索引,例如依實體 CPU 編號參照 CPU。不過,由於核心不需要其他這類配置,因此 API 使用核心現有的邏輯 CPU 編號,最符合 Zircon 的有限範圍。這些數字在特定系統上會保持一致,而用戶端可以維護靜態的單板設定來參照這些數字,或從 ZBI 存取設定資料。

替代效能等級

我們認為,新的 API 可能會使用「速度係數」,由排程器套用至特定 CPU 的基本效能規模,而非直接參照效能規模。這樣做可減少用戶端需要瞭解的特定情境資訊量;用戶端不必瞭解 CPU 之間的相對效能,只需要知道 CPU 的新頻率與名義頻率之間的比例。

我們選擇不採用這種做法,是因為效能比例的用途是在異質系統上,以基本方式進行 CPU 熱節流,因此這個 API 的預期用戶端不會從使用速度因數獲得任何有意義的好處。同時,我們也會產生定義新概念和修改排程器以利用該概念的成本。

最高效能規模

這項提案最初是使用 uint32_t 代表 [0.0, 1.0] 中的實值,來表示成效規模。特別是,這允許表示最大值 1.0。

雖然在撰寫本文時,Zircon 排程器支援的最大效能比例為 1.0,但我們決定允許輸入大於 1.0 的值,以支援未來的用途,例如渦輪模式。此外,先前的表示法並非定點,因此導致排程器無法直接使用這些值。

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 頻率控制的責任委派給使用者空間,對作業系統來說並不常見,因此無法取得這項主題的先前技術。