Go Wiki:目標特定程式碼

有時候,為了效能或相容性的考量,需要為特定的 GOARCH 和 GOOS 目標撰寫自訂程式碼。此頁面說明在這種情況下採用的一些最佳實務。自 2019 年 4 月起,這是加密套件的必要政策。

  1. 將標記檔案中的程式碼減至最少。應盡可能建置所有目標的程式碼。特別是,通用的 Go 實作也必須建置具有最佳化實作的目標。這對於針對通用的 Go 測試最佳化程式碼至關重要,而且可以更快地發現一些建置失敗。連結器會從最終二進位檔中移除未使用的程式碼。

  2. 檔案名稱應以標籤命名,例如 poly1305_amd64.go。請記住,如果檔案名稱以 _$GOARCH.go 結尾,則視為建置標籤。_noasm.go 也是一個不錯的字尾。

  3. 標籤檔案中不得有外傳函式。外傳函式會定義公開 API 及其文件,而這些內容在所有目標中都必須相同。如果在每個目標特定檔案中重複外傳函式,很可能會導致這些函式不同步。中階堆疊內嵌程式可能會處理部分效能成本。

  4. 測試所有可用的實作。在具有最佳化實作的目標上執行 go test 應該會測試一般程式碼和最佳化程式碼。你可以使用子測試來執行此操作。理想情況下,基準測試也應該包含在內。

  5. 撰寫比較測試。應該有一個測試會針對隨機或邊緣輸入執行兩個實作,並比較結果。隨著 #19109 的進展,這些測試應該成為模糊測試。

提示:你可以透過執行例如 GOARCH=arm64 go test -c 來輕鬆測試你的程式碼和測試是否可針對目標編譯。

範例

poly1305.go

package poly1305

// Sum generates an authenticator for m using a one-time key and puts the
// 16-byte result into out. Authenticating two different messages with the same
// key allows an attacker to forge messages at will.
func Sum(out *[16]byte, m []byte, key *[32]byte) {
    sum(out, m, key)
}

func sumGeneric(out *[16]byte, m []byte, key *[32]byte) {
    // ...
}

poly1305_amd64.go

//go:build !purego

package poly1305

//go:noescape
func sum(out *[16]byte, m []byte, key *[32]byte)

poly1305_amd64.s

//go:build !purego

// func sum(out *[16]byte, m []byte, key *[32]byte)
TEXT ·sum(SB), $0-128
    // ...

poly1305_noasm.go

//go:build !amd64 || purego

package poly1305

func sum(out *[16]byte, m []byte, key *[32]byte) {
    sumGeneric(out, m, key)
}

poly1305_test.go

package poly1305

import "testing"

func testSum(t *testing.T, sum func(tag *[16]byte, msg []byte, key *[32]byte)) {
    // ...
}

func TestSum(t *testing.T) {
    t.Run("Generic", func(t *testing.T) { testSum(t, sumGeneric) })
    t.Run("Native", func(t *testing.T) { testSum(t, sum) })
}

// TestSumCompare checks the output of sum against sumGeneric.
func TestSumCompare(t *testing.T) {
    // ...
}

如需更完整的範例,請參閱 x/crypto/poly1305x/crypto/salsa20/salsa 套件。


此內容是 Go Wiki 的一部分。