驅動程式堆疊效能

本文旨在概述在 Fuchsia 中撰寫新驅動程式或與現有驅動程式互動時,應避免的錯誤做法和應採取的正確做法。

本文涵蓋下列主題:

API 定義

為新的驅動程式庫堆疊編寫 API 時,應考量一些重要的效能洞察資料。驅動程式堆疊 API 分為應用程式或驅動程式庫類別。

應用程式 API 可讓一般非驅動程式庫元件存取硬體資源,而驅動程式庫程式 API 則可讓驅動程式彼此通訊。舉例來說,fuchsia.hardware.network 是與網路驅動程式互動的應用程式 API,而 fuchsia.hardware.network.driver 則定義網路裝置的較低層級驅動程式庫 API,這些裝置使用驅動程式庫傳輸,透過共置功能提高驅動程式庫間的呼叫效率。

裝置驅動程式庫 API 通常是 fuchsia.hardware.* FIDL API。

避免同步作業

屬於快速路徑的工作單元應避免同步完成,尤其是 (但不限於) 作業需要跨越程序界線時。以 FIDL API 來說,如果設計上規定必須完成上一個工作單元,才能啟動新的工作單元,就可能產生不必要的同步性,也就是說,呼叫端必須閒置等待,直到可以安全要求下一個工作單元為止。

從效能角度來看,同步作業在慢速路徑作業中是可以接受的,例如設定、終止和設定。

鼓勵批次處理

如果 API 定義明確定義了一批工作,而非一律傳輸單一單位,建議 API 使用者透過應用程式或驅動程式進行批次處理。

批次處理的重要功能是減少 API 執行次數,以完成一定量的工作。如果 API 跨越程序界線,則會直接轉換為減少的系統呼叫和排程負擔,有助於提升效能。

此外,如果 API 定義本身是以批次形式提供工作單元,裝置驅動程式就能更輕鬆地透過硬體加速,將一批工作項目合併為單一單元。許多裝置驅動程式都會使用 DMA,例如將工作排入佇列,交由驅動的特定硬體執行。如果一次收到一批工作項目,或許可以減少與硬體的交易次數。如果沒有明確的批次界線,裝置驅動程式就必須與硬體互動,次數會超出必要範圍,或是採用啟發式方法 (例如輪詢間隔),以減輕硬體通訊負擔。

避免複製資料

在網路、音訊或影片等高頻寬或低延遲應用程式中,避免複製資料尤其重要。盡可能讓大型酬載在 VMO 中跨越 API 邊界。 常見策略是在設定時協商有限數量的 VMO,並在作業期間交換這些 VMO 中的區域參照。

說明流量控制

如果 API 批次作業正常運作,且未如上所述設定嚴格的同步作業期望,則控制 API 伺服器和用戶端之間的資訊流,對於正確性和效能而言都很重要。

在正常運作情況下,API 的流量控制定義必須允許所有部分執行工作,且不會因等待對應部分而遭到封鎖,方法是維持系統可執行的工作總量 (由定義固定或在設定時預先協商) 的某些不變量。

舉例來說,fuchsia.hardware.network 會在設定期間定義有限的「工作單元」(即網路封包),藉此強制執行流量控管。在任何時間點,雙方都可以只使用本機維護的狀態,瞭解透過 API 邊界仍可推送多少封包。

訂購帳戶

在某些應用程式中,所有工作單元都必須依特定順序執行,才能確保正確性。但在其他應用程式中,部分排序限制可能會放寬或完全解除。

如果工作項目串流不必依確切順序執行,API 就能反映這點,方便驅動程式和應用程式輕鬆找出可平行處理的工作。

舉例來說,網路封包通常需要維持順序,以免中斷應用程式通訊協定,但這一般只適用於每個應用程式串流執行個體 (稱為「流程」)。網路介面卡常見的策略是定義一組封包佇列數量,並將每個應用程式流程確定指派給其中一個佇列。因此,網路堆疊可以安全地平行運作佇列,不必查看封包內容。觀察這類常見的硬體設施,並在 API 中充分運用這些設施,可大幅提升效能。

實作

本節列出實作程式碼時應注意的效能模式和反模式,這些程式碼會提供或使用裝置驅動程式庫 API。實作項目可以是驅動程式庫本身,也可以是與裝置驅動程式提供的服務互動的應用程式。

撰寫快速路徑上的程式碼時,請務必遵守下列事項。請注意,對於裝置驅動程式庫實作,快速路徑的一部分通常位於中斷執行緒中。

  • 避免配置記憶體或建立 Zircon 物件。快速路徑上使用的資源一律應預先分配。使用 FIDL 繫結 (特別是 hlcpp) 或 fit::function 等可能會導致隱含分配的程式庫時,請務必特別小心。
  • 留意系統呼叫。系統呼叫的運作成本可能很高,因此在效能敏感的作業中,必須謹慎避免不必要的呼叫核心。完全消除系統呼叫可能無法實現,但減少每個工作單位的呼叫次數 (例如使用批次處理) 可以大幅降低系統負載。
  • 快速傳回共用資源。如果 API 定義了有限的共用資源集區 (例如共用記憶體區域),應盡快重複使用這些資源或將其傳回「可用」集區,最好是以批次形式傳回。
  • 盡可能以唯讀模式而非讀寫模式對應 VMO。以唯讀方式對應 VMO 可減少 DMA 需要執行的快取作業數。舉例來說,使用 DMA 寫入可能含有髒快取行的共用 VMO 時,必須在 DMA 前後排清並使快取失效。另一方面,如果是唯讀對應,快取行一律為乾淨,表示 DMA 完成後,只需要排清並失效即可。