-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
sha256.go
160 lines (134 loc) · 3.59 KB
/
sha256.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// 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 https://mozilla.org/MPL/2.0/.
package sha256
import (
"crypto/rand"
cryptoSha256 "crypto/sha256"
"encoding/hex"
"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.
selectMinio()
default:
// When set to anything else, such as "standard", use the default Go
// implementation. Make sure not to touch the minio
// implementation as it may be disabled for incompatibility reasons.
}
verifyCorrectness()
}
// 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
}
if selectedRate == 0 {
return
}
l.Infof("Single thread SHA256 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)
}
func verifyCorrectness() {
// The currently selected algo should in fact perform a SHA256 calculation.
// $ echo "Syncthing Magic Testing Value" | openssl dgst -sha256 -hex
correct := "87f6cfd24131724c6ec43495594c5c22abc7d2b86bcc134bc6f10b7ec3dda4ee"
input := "Syncthing Magic Testing Value\n"
h := New()
h.Write([]byte(input))
sum := hex.EncodeToString(h.Sum(nil))
if sum != correct {
panic("sha256 is broken")
}
arr := Sum256([]byte(input))
sum = hex.EncodeToString(arr[:])
if sum != correct {
panic("sha256 is broken")
}
}