行動式 C++ 程式設計¶
注意:本文件涵蓋了需要在目標硬體環境中構建和執行的程式碼。這適用於核心執行執行時以及此倉庫中的核心和後端實現。這些規則不一定適用於僅在開發主機上執行的程式碼,例如創作或構建工具。
ExecuTorch 執行時程式碼旨在實現可移植性,應能在各種系統上構建,從伺服器到行動電話再到 DSP,從 POSIX 到 Windows 再到裸機環境。
這意味著它不能假定存在以下內容:
檔案
執行緒
異常
stdout,stderrprintf(),fprintf()一般的 POSIX API 和概念
它也不能假定
64 位指標
給定整數型別的大小
char的有無符號性
為了使二進位制檔案大小最小化,並嚴格控制記憶體分配,程式碼不得使用
malloc(),free()new,delete大多數
stdlibc++型別;特別是那些管理自身記憶體的容器型別,如string和vector,或者記憶體管理包裝型別,如unique_ptr和shared_ptr。
為了幫助降低複雜性,程式碼不得依賴於任何外部依賴項,除了
flatbuffers(用於.pte檔案反序列化)flatcc(用於事件跟蹤序列化)Core PyTorch(僅用於 ATen 模式)
平臺抽象層 (PAL)¶
為了避免假定目標系統的能力,ExecuTorch 執行時允許客戶端在其平臺抽象層 (PAL) 中覆蓋低階函式,PAL 定義在 //executorch/runtime/platform/platform.h 中,用於執行以下操作:
獲取當前時間戳
列印日誌訊息
使系統崩潰
記憶體分配¶
執行時程式碼應使用客戶端提供的 MemoryManager(//executorch/runtime/executor/memory_manager.h)來分配記憶體,而不是使用 malloc() 或 new。
檔案載入¶
客戶端不應直接載入檔案,而應提供已載入資料的緩衝區,或封裝在如 DataLoader 這樣的型別中。
整數型別¶
ExecuTorch 執行時程式碼不應假定基本型別(如 int, short 或 char)的任何大小。例如,C++ 標準只保證 int 至少有 16 位寬。ARM 工具鏈將 char 視為無符號型別,而其他工具鏈通常將其視為有符號型別。
相反,執行時 API 使用一組更可預測但仍然標準的整數型別
<cstdint>型別,如uint64_t,int32_t`;這些型別保證了位寬和有無符號性,與架構無關。當你需要非常特定的整數寬度時使用這些型別。size_t用於計數或記憶體偏移量。size_t保證足夠大,可以表示任何記憶體位元組偏移量;也就是說,它的寬度將與目標系統的原生指標型別相同。對於計數/偏移量,優先使用它而不是uint64_t,這樣 32 位系統就不需要為 64 位值的額外開銷付費。在某些 ATen 相容場景下,
Tensor返回有符號計數時使用ssize_t。儘可能優先使用size_t。
浮點算術¶
並非所有系統都支援浮點算術:有些甚至不在其工具鏈中啟用浮點模擬。因此,核心執行時程式碼在執行時不得執行任何浮點算術,儘管可以簡單地建立或管理 float 或 double 值(例如,在 EValue 中)。
核心,作為核心執行時之外的部分,允許執行浮點算術。儘管有些核心可能會選擇不這樣做,以便它們可以在不支援浮點運算的系統上執行。
日誌記錄¶
ExecuTorch 執行時提供了 //executorch/runtime/platform/log.h 中的 ET_LOG 介面和 //executorch/runtime/platform/assert.h 中的 ET_CHECK 介面,而不是使用 printf(), fprintf(), cout, cerr,或像 folly::logging 或 glog 這樣的庫。訊息使用 PAL 中的一個鉤子列印,這意味著客戶端可以將它們重定向到任何底層日誌系統,或者如果可用,直接列印到 stderr。
日誌格式可移植性¶
定寬整數¶
當你有一個日誌語句,例如
int64_t value;
ET_LOG(Error, "Value %??? is bad", value);
對於 %??? 部分,應該填寫什麼來匹配 int64_t?在不同的系統上,int64_t 的 typedef 可能是 int`, `long int 或 long long int。選擇像 %d`, `%ld 或 %lld 這樣的格式可能在一個目標系統上有效,但在其他系統上會出錯。
為了實現可移植性,執行時程式碼使用了標準(但確實有點彆扭)的 <cinttypes> 輔助宏。每種可移植整數型別都有一個對應的 PRIn## 宏,例如
int32_t->PRId32uint32_t->PRIu32int64_t->PRId64uint64_t->PRIu64更多資訊請參閱 https://cppreference.tw/w/cpp/header/cinttypes
這些宏是字面量字串,可以與格式字串的其他部分連線,例如
int64_t value;
ET_LOG(Error, "Value %" PRId64 " is bad", value);
請注意,這需要將字面格式字串切分(額外的雙引號)。它還需要在宏前面加上前導的 %。
但是,透過使用這些宏,可以保證工具鏈將為該型別使用適當的格式模式。
型別轉換¶
有時,特別是在跨越 ATen 和 lean 模式的程式碼中,值本身的型別在不同構建模式下可能不同。在這種情況下,將值轉換為 lean 模式型別,例如
ET_CHECK_MSG(
input.dim() == output.dim(),
"input.dim() %zd not equal to output.dim() %zd",
(ssize_t)input.dim(),
(ssize_t)output.dim());
在這種情況下,Tensor::dim() 在 lean 模式下返回 ssize_t,而 at::Tensor::dim() 在 ATen 模式下返回 int64_t。由於它們在概念上都返回(有符號的)計數,ssize_t 是最合適的整數型別。int64_t 也能工作,但在 lean 模式下會不必要地要求 32 位系統處理 64 位值。
這是唯一需要進行型別轉換的情況,即當 lean 模式和 ATen 模式不一致時。否則,請使用與型別匹配的格式模式。