Files
avo/examples/md5x16/md5x16_test.go

137 lines
2.7 KiB
Go
Raw Permalink Normal View History

all: AVX-512 (#217) Extends avo to support most AVX-512 instruction sets. The instruction type is extended to support suffixes. The K family of opmask registers is added to the register package, and the operand package is updated to support the new operand types. Move instruction deduction in `Load` and `Store` is extended to support KMOV* and VMOV* forms. Internal code generation packages were overhauled. Instruction database loading required various messy changes to account for the additional complexities of the AVX-512 instruction sets. The internal/api package was added to introduce a separation between instruction forms in the database, and the functions avo provides to create them. This was required since with instruction suffixes there is no longer a one-to-one mapping between instruction constructors and opcodes. AVX-512 bloated generated source code size substantially, initially increasing compilation and CI test times to an unacceptable level. Two changes were made to address this: 1. Instruction constructors in the `x86` package moved to an optab-based approach. This compiles substantially faster than the verbose code generation we had before. 2. The most verbose code-generated tests are moved under build tags and limited to a stress test mode. Stress test builds are run on schedule but not in regular CI. An example of AVX-512 accelerated 16-lane MD5 is provided to demonstrate and test the new functionality. Updates #20 #163 #229 Co-authored-by: Vaughn Iverson <vsivsi@yahoo.com>
2021-11-12 18:35:36 -08:00
package md5x16
import (
"crypto/md5"
"encoding/hex"
"math/rand"
"testing"
"testing/quick"
"golang.org/x/sys/cpu"
)
func RequireISA(t *testing.T) {
t.Helper()
if !cpu.X86.HasAVX512F {
t.Skip("requires AVX512F instruction set")
}
}
func TestVectors(t *testing.T) {
RequireISA(t)
cases := []struct {
Data string
HexDigest string
}{
{"", "d41d8cd98f00b204e9800998ecf8427e"},
{"The quick brown fox jumps over the lazy dog", "9e107d9d372bb6826bd81d3542a419d6"},
{"The quick brown fox jumps over the lazy dog.", "e4d909c290d0fb1ca068ffaddf22cbd0"},
}
for _, c := range cases {
digest := Single(t, []byte(c.Data))
got := hex.EncodeToString(digest[:])
if got != c.HexDigest {
t.Errorf("Sum(%#v) = %s; expect %s", c.Data, got, c.HexDigest)
}
}
}
func TestCmp(t *testing.T) {
RequireISA(t)
sum := func(data []byte) [Size]byte { return Single(t, data) }
if err := quick.CheckEqual(sum, md5.Sum, nil); err != nil {
t.Fatal(err)
}
}
func TestLengths(t *testing.T) {
RequireISA(t)
const max = BlockSize << 6
data := make([]byte, max)
rand.Read(data)
for n := 0; n <= max; n++ {
got := Single(t, data[:n])
expect := md5.Sum(data[:n])
if got != expect {
t.Fatalf("failed on length %d", n)
}
}
}
// Single hashes a single data buffer in all 16 lanes and returns the result,
// after asserting that all lanes are the same.
func Single(t *testing.T, d []byte) [Size]byte {
// Place the same data in every lane.
var data [Lanes][]byte
for l := range data {
data[l] = d
}
if err := Validate(data); err != nil {
t.Fatal(err)
}
// Hash and check the lanes are the same.
digest := Sum(data)
for l := range data {
if digest[0] != digest[l] {
t.Logf("lane %02d: %x", 0, digest[0])
t.Logf("lane %02d: %x", l, digest[l])
t.Fatal("lane mismatch")
}
}
return digest[0]
}
func TestActiveLanes(t *testing.T) {
RequireISA(t)
const trials = 1 << 10
const maxlen = BlockSize << 6
for trial := 0; trial < trials; trial++ {
// Pick active lanes.
lanes := 1 + rand.Intn(Lanes-1)
active := rand.Perm(Lanes)[:lanes]
// Fill active lanes with random data.
n := rand.Intn(maxlen)
buffer := make([]byte, lanes*n)
rand.Read(buffer)
var data [Lanes][]byte
for i, l := range active {
data[l] = buffer[i*n : (i+1)*n]
}
// Hash.
digest := Sum(data)
// Verify correct result in active lanes.
for _, l := range active {
expect := md5.Sum(data[l])
if digest[l] != expect {
t.Fatalf("lane %02d: mismatch", l)
}
}
// Verify other lanes are zero.
isactive := map[int]bool{}
for _, l := range active {
isactive[l] = true
}
for l := 0; l < Lanes; l++ {
if !isactive[l] {
var zero [Size]byte
if digest[l] != zero {
t.Fatalf("inactive lane %d is non-zero", l)
}
}
}
}
}