使用 Python 的原生執行檔

GraalPy 支援 GraalVM Native Image,可產生使用 GraalPy 的 Java 應用程式的原生二進位檔。

快速入門 #

如果您從Maven 原型開始,產生的 *pom.xml* 可讓您輕鬆使用用於 Native Image 建置的 Maven 外掛程式產生原生執行檔。

若要建置應用程式,請執行

mvn -Pnative package

此命令會封裝專案並建立原生執行檔。

請查看產生的檔案 *pom.xml* 和 *Main.java*。它們有文件說明 Python 資源如何包含在產生的二進位檔中。產生的專案應視為起點。它包含整個 Python 標準程式庫,因此 Python 程式碼可以叫用所有標準程式庫程式碼。可以手動修剪資源,以減少包含的 Python 程式庫,以減少套件的大小和啟動時間。此 Java 範例示範 Python 環境的一些實用預設選項,但可能需要其他設定來進一步控制 Python 程式碼允許執行的動作。

縮減二進位檔大小 #

Python 是一種大型語言。「內含電池」長期以來一直是 CPython 的核心原則。作為相容的替代方案,GraalPy 也包含大部分「電池」。當在 Java 應用程式中包含 GraalPy 時,這可能會導致二進位檔大小顯著增加。

只有您作為開發人員,才知道您的特定嵌入情境。預設值可能包含任何特定應用程式所需的多得多。嵌入式 Java 中的 Python 應用程式,其 Python 直譯器的使用案例通常比完整的 GraalPy 發行版更有限,而且您通常可以提前知道是否需要某些功能。在某些情況下,甚至可能不希望包含某些功能 (例如,密碼編譯演算法或 Socket 存取)。因此,當在 Java 應用程式中嵌入 GraalPy 時,可以透過排除 Python 語言的元件來縮減二進位檔大小並提高安全性。

排除 Python 元件 #

GraalPy 定義一些系統屬性,這些屬性可以在 `native-image` 命令列上傳遞,以排除語言的某些方面。這些選項在合併使用時,可以將執行檔的大小縮減約 20%。這些是

  • python.WithoutSSL=true - 此選項會移除 `ssl` 模組。如果不需要安全網路存取或憑證檢查,這會移除 Java 的 SSL 類別和 BouncyCastle 程式庫。
  • python.WithoutDigest=true - 此選項會移除 `_md5`、`_sha1`、`_sha256`、`_sha512`、`_sha3` 和 `_hashlib` 模組。這會移除 GraalPy 中 `java.security.MessageDigest` 和 `javax.crypto.Mac` 的直接用法。
  • python.WithoutPlatformAccess=true - 這會移除 `signal` 和 `subprocess` 模組,移除對程序屬性 (例如 Unix UID 和 GID) 的存取,或設定 Java 預設時區。這對二進位檔大小沒有顯著影響,但是如果這些是不需要的功能,並且已透過環境選項動態停用,則也可以提前移除它們。
  • python.WithoutCompressionLibraries=true - 這會移除 `zlib`、`lzma`、`bzip2` 和 `zipimporter` 模組和相關類別。這些模組同時具有原生和純 Java 實作 (前者為了效能,後者為了更好的沙箱化);但是,如果不需要它們,則可以完全移除它們。
  • python.WithoutNativePosix=true - 當嵌入 GraalPy 而不是透過其啟動器執行時,預設 `os` 模組後端是純 Java 實作。GraalPy 的原生 POSIX 後端僅建議用於與 CPython 的 POSIX 介面 100% 相容,如果未使用,可以使用此選項從組建中移除。
  • python.WithoutJavaInet=true - Python 的 `socket` 模組的 Java 實作基於 Java 的網路類別。如果拒絕嵌入情境的網路存取,此選項可以進一步縮減二進位檔大小。
  • python.AutomaticAsyncActions=false - 信號處理、Python 弱參考回呼和清除原生資源通常是透過產生 GraalPy 精靈執行緒來自動完成,這些執行緒會將安全點動作提交到 Python 主執行緒。這會使用具有執行緒集區的 `ExecutorService`。如果您想要禁止這類額外執行緒或避免提取 `ExecutorService` 和相關類別,則請將此屬性設定為 `false`,並從環境的多語系繫結中擷取 `PollPythonAsyncActions` 物件。此物件是可執行的,可用於在您想要的位置觸發 Python 非同步動作。
  • python.WithoutJNI=true - 此選項會移除任何使用 JNI 的程式碼。因此,您無法使用 HPy JNI 後端,並且可能會移除其他依賴 JNI 的部分。

移除預先初始化的 Python 堆積 #

另一個縮減原生執行檔大小的實用選項是從執行檔中省略預先初始化的 Python 環境。依預設,預設的 Python 環境已預先初始化並準備好立即執行。這可以稍微改善啟動,但代價是在二進位檔中包含數千個 Python 物件。在使用自訂多語系引擎以允許環境共用的嵌入式應用程式中,預先初始化的環境根本無法使用,而且包含這些物件是浪費。可以透過將以下內容傳遞至 `native-image` 命令來省略預先初始化的堆積

-Dimage-build-time.PreinitializeContexts=

停用 Python 程式碼的執行階段編譯 #

如果二進位檔大小比執行速度重要得多 (如果預期所有 Python 指令碼都是短時間執行、執行大量 I/O,或很少執行多次指令碼,則可能是這種情況),則可能完全停用 JIT 編譯是合理的。請注意,這可能會顯著影響您的 Python 效能,因此請務必在選擇使用此選項時,測試實際使用案例的執行階段行為。這可以透過傳遞以下選項來實現

-Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime \
-Dpolyglot.engine.WarnInterpreterOnly=false

摘要 #

結合所有這些方法可以將 GraalPy 二進位檔的大小減半。每個嵌入式應用程式都不同,而且 Java 程式碼的其他部分所提取的程式碼也很重要,因此應嘗試這些選項的組合,以判斷它們在特定執行個體中的影響。

運送 Python 套件 #

我們的 Maven 原型依預設設定為將所有需要的 Python 檔案包含在原生二進位檔本身中,因此映像檔是獨立的。

在自訂嵌入中,Python 標準程式庫會複製到原生映像檔的旁邊。移動原生映像檔時,標準程式庫資料夾需要保留在其旁邊。

與我們聯繫