Go Wiki: ターゲット固有のコード
パフォーマンスや互換性の理由から、特定の GOARCH と GOOS ターゲット向けにカスタムコードを作成する必要がある場合があります。このページでは、その場合に採用すべきベストプラクティスを紹介します。2019年4月現在、これは暗号パッケージの必須ポリシーです。
-
タグ付きファイル内のコードを最小限に抑える。 可能な限り多くのコードがすべてのターゲットでビルドされるようにする必要があります。特に、汎用 Go 実装は、最適化された実装を持つターゲットでもビルドする必要があります。これは、最適化されたコードを汎用 Go に対してテストするために重要であり、ビルドの失敗に迅速に気付くことができます。リンカは最終的なバイナリから未使用のコードを削除します。
-
`poly1305_amd64.go` のように、タグにちなんでファイルに名前を付ける。 ファイルが `_$GOARCH.go` で終わる場合、それはビルドタグとしてカウントされることに注意してください。 `_noasm.go` も良いサフィックスです。
-
タグ付きファイルにエクスポートされた関数を含めない。 エクスポートされた関数は、すべてのパブリック API とそのドキュメントを定義し、これはすべてのターゲットで同じである必要があります。エクスポートされた関数を各ターゲット固有のファイルで繰り返すと、同期がとれなくなる可能性があります。ミッドスタックインライナーは、おそらくパフォーマンスコストの一部を処理します。
-
利用可能なすべての実装をテストする。 最適化された実装を持つターゲットで `go test` を実行すると、汎用コードと最適化されたコードの両方がテストされます。このためには、サブテストを使用できます。理想的には、ベンチマークも同様です。
-
比較テストを作成する。 ランダムまたはエッジ入力に対して2つの実装を実行し、結果を比較するテストが必要です。#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 の一部です。