Go Wiki: 目標特定程式碼
有時,出於效能或相容性原因,需要為特定的 GOARCH 和 GOOS 目標編寫自定義程式碼。本頁介紹了在這種情況下應遵循的一些最佳實踐。自 2019 年 4 月起,這是加密包的強制性策略。
-
儘量減少在標記檔案中編寫程式碼。應儘可能多地為所有目標構建程式碼。特別是,通用 Go 實現也必須為具有最佳化實現的那些目標構建。這對於將最佳化程式碼與通用 Go 進行比較測試至關重要,並且可以更快地發現一些構建失敗。連結器將從最終二進位制檔案中刪除未使用的程式碼。
-
為檔案加上其目標標記命名,例如
poly1305_amd64.go。請記住,如果一個檔案以_$GOARCH.go結尾,那就算作一個構建標記。_noasm.go也是一個不錯的字尾。 -
在標記檔案中不要包含匯出的函式。匯出的函式定義了公共 API 及其文件,這些必須在所有目標上保持一致。如果在每個目標特定的檔案中重複匯出的函式,很容易導致它們不同步。中間堆疊內聯器可能會處理掉一些效能成本。
-
測試所有可用實現。在具有最佳化實現的特定目標上執行
go test應該同時測試通用程式碼和最佳化程式碼。您可以使用子測試來實現這一點。理想情況下,也包括基準測試。 -
編寫對比測試。應該有一個測試,針對隨機輸入或邊緣情況輸入執行兩個實現,並比較結果。隨著 #19109 的進展,這些應該成為模糊測試。
提示:您可以透過執行 e.g. 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 的一部分。