|
@@ -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)
|
|
|
+}
|