This adds autodetection of the fastest hashing library on startup, thus handling the performance regression. It also adds an environment variable to control the selection, STHASHING=standard (Go standard library version, avoids SIGILL crash when the minio library has bugs on odd CPUs), STHASHING=minio (to force using the minio version) or unset for the default autodetection. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3617
This commit is contained in:
committed by
Audrius Butkevicius
parent
5f01afb7ea
commit
d328e0fb75
136
lib/sha256/sha256.go
Normal file
136
lib/sha256/sha256.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (C) 2016 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package sha256
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
cryptoSha256 "crypto/sha256"
|
||||
"fmt"
|
||||
"hash"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
minioSha256 "github.com/minio/sha256-simd"
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
)
|
||||
|
||||
var l = logger.DefaultLogger.NewFacility("sha256", "SHA256 hashing package")
|
||||
|
||||
const (
|
||||
benchmarkingIterations = 3
|
||||
benchmarkingDuration = 150 * time.Millisecond
|
||||
defaultImpl = "crypto/sha256"
|
||||
minioImpl = "minio/sha256-simd"
|
||||
)
|
||||
|
||||
const (
|
||||
BlockSize = cryptoSha256.BlockSize
|
||||
Size = cryptoSha256.Size
|
||||
)
|
||||
|
||||
// May be switched out for another implementation
|
||||
var (
|
||||
New = cryptoSha256.New
|
||||
Sum256 = cryptoSha256.Sum256
|
||||
)
|
||||
|
||||
var (
|
||||
selectedImpl = defaultImpl
|
||||
cryptoPerf float64
|
||||
minioPerf float64
|
||||
)
|
||||
|
||||
func SelectAlgo() {
|
||||
switch os.Getenv("STHASHING") {
|
||||
case "":
|
||||
// When unset, probe for the fastest implementation.
|
||||
benchmark()
|
||||
if minioPerf > cryptoPerf {
|
||||
selectMinio()
|
||||
}
|
||||
|
||||
case "minio":
|
||||
// When set to "minio", use that. Benchmark anyway to be able to
|
||||
// present the difference.
|
||||
benchmark()
|
||||
selectMinio()
|
||||
|
||||
default:
|
||||
// When set to anything else, such as "standard", use the default Go
|
||||
// implementation. Benchmark that anyway, so we can report something
|
||||
// useful in Report(). Make sure not to touch the minio
|
||||
// implementation as it may be disabled for incompatibility reasons.
|
||||
cryptoPerf = cpuBenchOnce(benchmarkingIterations*benchmarkingDuration, cryptoSha256.New)
|
||||
}
|
||||
}
|
||||
|
||||
// Report prints a line with the measured hash performance rates for the
|
||||
// selected and alternate implementation.
|
||||
func Report() {
|
||||
var otherImpl string
|
||||
var selectedRate, otherRate float64
|
||||
|
||||
switch selectedImpl {
|
||||
case defaultImpl:
|
||||
selectedRate = cryptoPerf
|
||||
otherRate = minioPerf
|
||||
otherImpl = minioImpl
|
||||
|
||||
case minioImpl:
|
||||
selectedRate = minioPerf
|
||||
otherRate = cryptoPerf
|
||||
otherImpl = defaultImpl
|
||||
}
|
||||
|
||||
l.Infof("Single thread hash performance is %s using %s (%s using %s).", formatRate(selectedRate), selectedImpl, formatRate(otherRate), otherImpl)
|
||||
}
|
||||
|
||||
func selectMinio() {
|
||||
New = minioSha256.New
|
||||
Sum256 = minioSha256.Sum256
|
||||
selectedImpl = minioImpl
|
||||
}
|
||||
|
||||
func benchmark() {
|
||||
// Interleave the tests to achieve some sort of fairness if the CPU is
|
||||
// just in the process of spinning up to full speed.
|
||||
for i := 0; i < benchmarkingIterations; i++ {
|
||||
if perf := cpuBenchOnce(benchmarkingDuration, cryptoSha256.New); perf > cryptoPerf {
|
||||
cryptoPerf = perf
|
||||
}
|
||||
if perf := cpuBenchOnce(benchmarkingDuration, minioSha256.New); perf > minioPerf {
|
||||
minioPerf = perf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cpuBenchOnce(duration time.Duration, newFn func() hash.Hash) float64 {
|
||||
chunkSize := 100 * 1 << 10
|
||||
h := newFn()
|
||||
bs := make([]byte, chunkSize)
|
||||
rand.Reader.Read(bs)
|
||||
|
||||
t0 := time.Now()
|
||||
b := 0
|
||||
for time.Since(t0) < duration {
|
||||
h.Write(bs)
|
||||
b += chunkSize
|
||||
}
|
||||
h.Sum(nil)
|
||||
d := time.Since(t0)
|
||||
return float64(int(float64(b)/d.Seconds()/(1<<20)*100)) / 100
|
||||
}
|
||||
|
||||
func formatRate(rate float64) string {
|
||||
decimals := 0
|
||||
if rate < 1 {
|
||||
decimals = 2
|
||||
} else if rate < 10 {
|
||||
decimals = 1
|
||||
}
|
||||
return fmt.Sprintf("%.*f MB/s", decimals, rate)
|
||||
}
|
||||
Reference in New Issue
Block a user