Go Wiki:目標特定程式碼
有時候,為了效能或相容性的考量,需要為特定的 GOARCH 和 GOOS 目標撰寫自訂程式碼。此頁面說明在這種情況下採用的一些最佳實務。自 2019 年 4 月起,這是加密套件的必要政策。
-
將標記檔案中的程式碼減至最少。應盡可能建置所有目標的程式碼。特別是,通用的 Go 實作也必須建置具有最佳化實作的目標。這對於針對通用的 Go 測試最佳化程式碼至關重要,而且可以更快地發現一些建置失敗。連結器會從最終二進位檔中移除未使用的程式碼。
-
檔案名稱應以標籤命名,例如
poly1305_amd64.go
。請記住,如果檔案名稱以_$GOARCH.go
結尾,則視為建置標籤。_noasm.go
也是一個不錯的字尾。 -
標籤檔案中不得有外傳函式。外傳函式會定義公開 API 及其文件,而這些內容在所有目標中都必須相同。如果在每個目標特定檔案中重複外傳函式,很可能會導致這些函式不同步。中階堆疊內嵌程式可能會處理部分效能成本。
-
測試所有可用的實作。在具有最佳化實作的目標上執行
go test
應該會測試一般程式碼和最佳化程式碼。你可以使用子測試來執行此操作。理想情況下,基準測試也應該包含在內。 -
撰寫比較測試。應該有一個測試會針對隨機或邊緣輸入執行兩個實作,並比較結果。隨著 #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/poly1305 和 x/crypto/salsa20/salsa 套件。
此內容是 Go Wiki 的一部分。