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の一部です。