├── .github
├── FUNDING.yml
└── workflows
│ └── audit.yml
├── LICENSE
├── README.md
├── README.win.md
├── _gophers_race.jpg
├── base64
└── base64_test.go
├── between
└── between_test.go
├── caseinsensitivecompare
└── caseinsensitivecompare_test.go
├── concat
└── concat_test.go
├── contains
└── contains_test.go
├── embed
├── embed_test.go
└── example.txt
├── floodfill
└── floodfill_test.go
├── foreach
└── foreach_test.go
├── go.mod
├── go.sum
├── hash
└── hash_test.go
├── index
└── index_test.go
├── json
└── json_test.go
├── math
└── math_test.go
├── parse
└── parse_test.go
├── random
└── random_test.go
├── regexp
└── regexp_test.go
├── split
└── split.go
├── template
└── template_test.go
├── updateBenchLogs.cmd
└── updateBenchLogs.sh
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: simonwaldherr
2 | patreon: simonwaldherr
3 | open_collective: # Replace with a single Open Collective username
4 | ko_fi: # Replace with a single Ko-fi username
5 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
6 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
7 | liberapay: SimonWaldherr
8 | issuehunt: # Replace with a single IssueHunt username
9 | otechie: # Replace with a single Otechie username
10 | custom: ['https://flattr.com/@SimonWaldherr']
--------------------------------------------------------------------------------
/.github/workflows/audit.yml:
--------------------------------------------------------------------------------
1 | name: Audit
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 |
11 | audit:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 |
16 | - name: Set up Go
17 | uses: actions/setup-go@v3
18 | with:
19 | go-version: 1.18
20 |
21 | - name: get dependencies
22 | run: go get -t github.com/SimonWaldherr/golang-benchmarks/...
23 |
24 | - name: Run tests
25 | run: go test -bench -benchmem ./...
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Simon Waldherr
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Go Benchmarks
2 |
3 | [](https://github.com/SimonWaldherr/golang-benchmarks/actions/workflows/audit.yml)
4 | [](https://zenodo.org/badge/latestdoi/154216722)
5 | [](https://goreportcard.com/report/github.com/SimonWaldherr/golang-benchmarks)
6 | [](https://opensource.org/licenses/MIT)
7 |
8 | In programming in general, and in Golang in particular, many roads lead to Rome.
9 | From time to time I ask myself which of these ways is the fastest.
10 | In Golang there is a wonderful solution, with `go test -bench` you can measure the speed very easily and quickly.
11 | In order for you to benefit from it too, I will publish such benchmarks in this repository in the future.
12 |
13 | ## ToC
14 |
15 | * [base64](https://github.com/SimonWaldherr/golang-benchmarks#base64)
16 | * [between](https://github.com/SimonWaldherr/golang-benchmarks#between)
17 | * [caseinsensitivecompare](https://github.com/SimonWaldherr/golang-benchmarks#caseinsensitivecompare)
18 | * [concat](https://github.com/SimonWaldherr/golang-benchmarks#concat)
19 | * [contains](https://github.com/SimonWaldherr/golang-benchmarks#contains)
20 | * [embed](https://github.com/SimonWaldherr/golang-benchmarks#embed)
21 | * [floodfill](https://github.com/SimonWaldherr/golang-benchmarks#floodfill)
22 | * [foreach](https://github.com/SimonWaldherr/golang-benchmarks#foreach)
23 | * [hash](https://github.com/SimonWaldherr/golang-benchmarks#hash)
24 | * [index](https://github.com/SimonWaldherr/golang-benchmarks#index)
25 | * [json](https://github.com/SimonWaldherr/golang-benchmarks#json)
26 | * [math](https://github.com/SimonWaldherr/golang-benchmarks#math)
27 | * [parse](https://github.com/SimonWaldherr/golang-benchmarks#parse)
28 | * [random](https://github.com/SimonWaldherr/golang-benchmarks#random)
29 | * [regexp](https://github.com/SimonWaldherr/golang-benchmarks#regexp)
30 | * [template](https://github.com/SimonWaldherr/golang-benchmarks#template)
31 | * [trim](https://github.com/SimonWaldherr/golang-benchmarks#trim)
32 |
33 | ## Golang?
34 |
35 | I published another repository where I show some Golang examples.
36 | If you\'re interested in new programming languages, you should definitely take a look at Golang:
37 |
38 | * [Golang examples](https://github.com/SimonWaldherr/golang-examples)
39 | * [tour.golang.org](https://tour.golang.org/)
40 | * [Go by example](https://gobyexample.com/)
41 | * [Golang Book](http://www.golang-book.com/)
42 | * [Go-Learn](https://github.com/skippednote/Go-Learn)
43 |
44 | ## Is it any good?
45 |
46 | [Yes](https://news.ycombinator.com/item?id=3067434)
47 |
48 | ## Benchmark Results
49 |
50 | Golang Version: [go version go1.24.3 darwin/arm64](https://tip.golang.org/doc/go1.24)
51 | Hardware Spec: [Apple MacBook Pro 16-Inch M2 Max 2023](https://support.apple.com/kb/SP890) [(?)](https://everymac.com/systems/apple/macbook_pro/specs/macbook-pro-m2-max-12-core-cpu-30-core-gpu-16-2023-specs.html) [(buy)](https://amzn.to/3K80lP4)
52 |
53 | ### base64
54 |
55 | ```go
56 | // Package base64 benchmarks some base64 functions.
57 | // On all tested systems it's faster to decode a
58 | // base64 encoded string instead of a check via regex.
59 | package base64
60 |
61 | import (
62 | "encoding/base64"
63 | "regexp"
64 | "testing"
65 | )
66 |
67 | func base64decode(s string) bool {
68 | _, err := base64.StdEncoding.DecodeString(s)
69 | return err == nil
70 | }
71 |
72 | func base64regex(s string) bool {
73 | matched, _ := regexp.MatchString(`^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$`, s)
74 | return matched
75 | }
76 |
77 | func BenchmarkBase64decode(b *testing.B) {
78 | isNotBase64 := `Invalid string`
79 | isBase64 := `VmFsaWQgc3RyaW5nCg==`
80 |
81 | for n := 0; n < b.N; n++ {
82 | base64decode(isNotBase64)
83 | base64decode(isBase64)
84 | }
85 | }
86 |
87 | func BenchmarkBase64regex(b *testing.B) {
88 | isNotBase64 := `Invalid string`
89 | isBase64 := `VmFsaWQgc3RyaW5nCg==`
90 |
91 | for n := 0; n < b.N; n++ {
92 | base64regex(isNotBase64)
93 | base64regex(isBase64)
94 | }
95 | }
96 | ```
97 |
98 | ```
99 | $ go test -bench . -benchmem
100 | goos: darwin
101 | goarch: arm64
102 | cpu: Apple M2 Max
103 | BenchmarkBase64decode-12 23044992 51.78 ns/op 32 B/op 2 allocs/op
104 | BenchmarkBase64regex-12 137590 8650 ns/op 21906 B/op 198 allocs/op
105 | PASS
106 | ok _/Users/simonwaldherr/git/golang-benchmarks/base64 3.640s
107 | ```
108 |
109 | ### between
110 |
111 | ```go
112 | // Package between compares the performance of checking
113 | // if a number is between two other numbers via regex
114 | // and by parsing the number as integers.
115 | package between
116 |
117 | import (
118 | "regexp"
119 | "simonwaldherr.de/go/golibs/as"
120 | "simonwaldherr.de/go/ranger"
121 | "testing"
122 | )
123 |
124 | func BenchmarkNumberRegEx(b *testing.B) {
125 | re := ranger.Compile(89, 1001)
126 | re = "^(" + re + ")$"
127 | b.ResetTimer()
128 |
129 | for n := 0; n < b.N; n++ {
130 | matched, err := regexp.MatchString(re, "404")
131 | if !matched || err != nil {
132 | b.Log("Error in Benchmark")
133 | }
134 |
135 | matched, err = regexp.MatchString(re, "2000")
136 | if matched || err != nil {
137 | b.Log("Error in Benchmark")
138 | }
139 | }
140 | }
141 |
142 | func BenchmarkFulltextRegEx(b *testing.B) {
143 | re := ranger.Compile(89, 1001)
144 | re = " (" + re + ") "
145 | b.ResetTimer()
146 |
147 | for n := 0; n < b.N; n++ {
148 | matched, err := regexp.MatchString(re, "lorem ipsum 404 dolor sit")
149 | if !matched || err != nil {
150 | b.Log("Error in Benchmark")
151 | }
152 |
153 | matched, err = regexp.MatchString(re, "lorem ipsum 2000 dolor sit")
154 | if matched || err != nil {
155 | b.Log("Error in Benchmark")
156 | }
157 | }
158 | }
159 |
160 | func BenchmarkNumberParse(b *testing.B) {
161 | for n := 0; n < b.N; n++ {
162 | i1 := as.Int("404")
163 | i2 := as.Int("2000")
164 |
165 | if i1 < 89 || i1 > 1001 {
166 | b.Log("Error in Benchmark")
167 | }
168 |
169 | if !(i2 < 89 || i2 > 1001) {
170 | b.Log("Error in Benchmark")
171 | }
172 | }
173 | }
174 |
175 | func BenchmarkFulltextParse(b *testing.B) {
176 | re := regexp.MustCompile("[0-9]+")
177 | b.ResetTimer()
178 |
179 | for n := 0; n < b.N; n++ {
180 | i1 := as.Int(re.FindString("lorem ipsum 404 dolor sit"))
181 | i2 := as.Int(re.FindString("lorem ipsum 2000 dolor sit"))
182 |
183 | if i1 < 89 || i1 > 1001 {
184 | b.Log("Error in Benchmark")
185 | }
186 |
187 | if !(i2 < 89 || i2 > 1001) {
188 | b.Log("Error in Benchmark")
189 | }
190 | }
191 | }
192 | ```
193 |
194 | ```
195 | $ go test -bench . -benchmem
196 | goos: darwin
197 | goarch: arm64
198 | cpu: Apple M2 Max
199 | BenchmarkNumberRegEx-12 201549 5985 ns/op 16870 B/op 142 allocs/op
200 | BenchmarkFulltextRegEx-12 240966 4835 ns/op 12075 B/op 104 allocs/op
201 | BenchmarkNumberParse-12 33684958 35.81 ns/op 0 B/op 0 allocs/op
202 | BenchmarkFulltextParse-12 2592841 462.5 ns/op 32 B/op 2 allocs/op
203 | PASS
204 | ok _/Users/simonwaldherr/git/golang-benchmarks/between 6.503s
205 | ```
206 |
207 | ### caseinsensitivecompare
208 |
209 | ```go
210 | package trim
211 |
212 | import (
213 | "strings"
214 | "testing"
215 | )
216 |
217 | func BenchmarkEqualFold(b *testing.B) {
218 | for n := 0; n < b.N; n++ {
219 | _ = strings.EqualFold("abc", "ABC")
220 | _ = strings.EqualFold("ABC", "ABC")
221 | _ = strings.EqualFold("1aBcD", "1AbCd")
222 | }
223 | }
224 |
225 | func BenchmarkToUpper(b *testing.B) {
226 | for n := 0; n < b.N; n++ {
227 | _ = strings.ToUpper("abc") == strings.ToUpper("ABC")
228 | _ = strings.ToUpper("ABC") == strings.ToUpper("ABC")
229 | _ = strings.ToUpper("1aBcD") == strings.ToUpper("1AbCd")
230 | }
231 | }
232 |
233 | func BenchmarkToLower(b *testing.B) {
234 | for n := 0; n < b.N; n++ {
235 | _ = strings.ToLower("abc") == strings.ToLower("ABC")
236 | _ = strings.ToLower("ABC") == strings.ToLower("ABC")
237 | _ = strings.ToLower("1aBcD") == strings.ToLower("1AbCd")
238 | }
239 | }
240 | ```
241 |
242 | ```
243 | $ go test -bench . -benchmem
244 | goos: darwin
245 | goarch: arm64
246 | cpu: Apple M2 Max
247 | BenchmarkEqualFold-12 83668860 14.13 ns/op 0 B/op 0 allocs/op
248 | BenchmarkToUpper-12 10808018 112.7 ns/op 24 B/op 3 allocs/op
249 | BenchmarkToLower-12 8450619 136.3 ns/op 40 B/op 5 allocs/op
250 | PASS
251 | ok _/Users/simonwaldherr/git/golang-benchmarks/caseinsensitivecompare 4.712s
252 | ```
253 |
254 | ### concat
255 |
256 | ```go
257 | // Package concat benchmarks the performance of
258 | // various string concatenation methods.
259 | // Instead of just concatenating a string to another string
260 | // it is also possible (and much faster) to use
261 | // a bytes buffer.
262 | package concat
263 |
264 | import (
265 | "bytes"
266 | "strings"
267 | "testing"
268 | )
269 |
270 | func BenchmarkConcatString(b *testing.B) {
271 | var str string
272 | for n := 0; n < b.N; n++ {
273 | str += "x"
274 | }
275 | }
276 |
277 | func BenchmarkConcatBuffer(b *testing.B) {
278 | var buffer bytes.Buffer
279 | for n := 0; n < b.N; n++ {
280 | buffer.WriteString("x")
281 |
282 | }
283 | }
284 |
285 | func BenchmarkConcatBuilder(b *testing.B) {
286 | var builder strings.Builder
287 | for n := 0; n < b.N; n++ {
288 | builder.WriteString("x")
289 | }
290 | }
291 |
292 | func BenchmarkConcat(b *testing.B) {
293 | b.Run("String", func(b *testing.B) {
294 | var str string
295 | for n := 0; n < b.N; n++ {
296 | str += "x"
297 | }
298 | })
299 |
300 | b.Run("Buffer", func(b *testing.B) {
301 | var buffer bytes.Buffer
302 | for n := 0; n < b.N; n++ {
303 | buffer.WriteString("x")
304 | }
305 | })
306 |
307 | b.Run("Builder", func(b *testing.B) {
308 | var builder strings.Builder
309 | for n := 0; n < b.N; n++ {
310 | builder.WriteString("x")
311 | }
312 | })
313 | }
314 | ```
315 |
316 | ```
317 | $ go test -bench . -benchmem
318 | goos: darwin
319 | goarch: arm64
320 | cpu: Apple M2 Max
321 | BenchmarkConcatString-12 1000000 28215 ns/op 503995 B/op 1 allocs/op
322 | BenchmarkConcatBuffer-12 321619281 3.862 ns/op 3 B/op 0 allocs/op
323 | BenchmarkConcatBuilder-12 566795916 2.274 ns/op 5 B/op 0 allocs/op
324 | BenchmarkConcat/String-12 1000000 29430 ns/op 503995 B/op 1 allocs/op
325 | BenchmarkConcat/Buffer-12 320330152 3.772 ns/op 3 B/op 0 allocs/op
326 | BenchmarkConcat/Builder-12 567004694 2.231 ns/op 5 B/op 0 allocs/op
327 | PASS
328 | ok _/Users/simonwaldherr/git/golang-benchmarks/concat 64.044s
329 | ```
330 |
331 | ### contains
332 |
333 | ```go
334 | // Package contains tests various ways of checking
335 | // if a string is contained in another string.
336 | package contains
337 |
338 | import (
339 | "bytes"
340 | "regexp"
341 | "strings"
342 | "testing"
343 | )
344 |
345 | // strings.Contains
346 | func contains() bool {
347 | return strings.Contains("Lorem Ipsum", "em Ip")
348 | }
349 |
350 | func containsNot() bool {
351 | return strings.Contains("Lorem Ipsum", "Dolor")
352 | }
353 |
354 | func TestContains(t *testing.T) {
355 | if contains() == false {
356 | t.Error("ERROR: contains")
357 | }
358 | if containsNot() == true {
359 | t.Error("ERROR: contains not")
360 | }
361 | }
362 |
363 | func BenchmarkContains(b *testing.B) {
364 | for n := 0; n < b.N; n++ {
365 | contains()
366 | }
367 | }
368 |
369 | func BenchmarkContainsNot(b *testing.B) {
370 | for n := 0; n < b.N; n++ {
371 | containsNot()
372 | }
373 | }
374 |
375 | // bytes.Contains
376 | func containsBytes() bool {
377 | return bytes.Contains([]byte("Lorem Ipsum"), []byte("em Ip"))
378 | }
379 |
380 | func containsBytesNot() bool {
381 | return bytes.Contains([]byte("Lorem Ipsum"), []byte("Dolor"))
382 | }
383 |
384 | func TestContainsBytes(t *testing.T) {
385 | if containsBytes() == false {
386 | t.Error("ERROR: bytes contains")
387 | }
388 | if containsBytesNot() == true {
389 | t.Error("ERROR: bytes contains not")
390 | }
391 | }
392 |
393 | func BenchmarkContainsBytes(b *testing.B) {
394 | for n := 0; n < b.N; n++ {
395 | containsBytes()
396 | }
397 | }
398 |
399 | func BenchmarkContainsBytesNot(b *testing.B) {
400 | for n := 0; n < b.N; n++ {
401 | containsBytesNot()
402 | }
403 | }
404 |
405 | // regexp.MustCompile + regexp.MatchString
406 | func compileMatch(re *regexp.Regexp) bool {
407 | matched := re.MatchString("Lorem Ipsum")
408 | return matched
409 | }
410 |
411 | func compileMatchNot(re *regexp.Regexp) bool {
412 | matched := re.MatchString("Lorem Ipsum")
413 | return matched
414 | }
415 |
416 | func TestCompileMatch(t *testing.T) {
417 | re1 := regexp.MustCompile("em Ip")
418 | re2 := regexp.MustCompile("Dolor")
419 | if compileMatch(re1) == false {
420 | t.Error("ERROR: compile match")
421 | }
422 | if compileMatchNot(re2) == true {
423 | t.Error("ERROR: compile match not")
424 | }
425 | }
426 |
427 | func BenchmarkCompileMatch(b *testing.B) {
428 | re := regexp.MustCompile("em Ip")
429 | for n := 0; n < b.N; n++ {
430 | compileMatch(re)
431 | }
432 | }
433 |
434 | func BenchmarkCompileMatchNot(b *testing.B) {
435 | re := regexp.MustCompile("Dolor")
436 | for n := 0; n < b.N; n++ {
437 | compileMatchNot(re)
438 | }
439 | }
440 |
441 | // regexp.MatchString
442 | func match() bool {
443 | matched, _ := regexp.MatchString("em Ip", "Lorem Ipsum")
444 | return matched
445 | }
446 |
447 | func matchNot() bool {
448 | matched, _ := regexp.MatchString("Dolor", "Lorem Ipsum")
449 | return matched
450 | }
451 |
452 | func TestMatch(t *testing.T) {
453 | if match() == false {
454 | t.Error("ERROR: match")
455 | }
456 | if matchNot() == true {
457 | t.Error("ERROR: match not")
458 | }
459 | }
460 |
461 | func BenchmarkMatch(b *testing.B) {
462 | for n := 0; n < b.N; n++ {
463 | match()
464 | }
465 | }
466 |
467 | func BenchmarkMatchNot(b *testing.B) {
468 | for n := 0; n < b.N; n++ {
469 | matchNot()
470 | }
471 | }
472 |
473 | // BenchmarkContainsMethods benchmarks different methods to check substring presence.
474 | func BenchmarkContainsMethods(b *testing.B) {
475 | b.Run("Strings.Contains", func(b *testing.B) {
476 | str := "Lorem Ipsum"
477 | substr := "em Ip"
478 | for n := 0; n < b.N; n++ {
479 | _ = strings.Contains(str, substr)
480 | _ = strings.Contains(str, "Dolor")
481 | }
482 | })
483 |
484 | b.Run("Bytes.Contains", func(b *testing.B) {
485 | str := []byte("Lorem Ipsum")
486 | substr := []byte("em Ip")
487 | for n := 0; n < b.N; n++ {
488 | _ = bytes.Contains(str, substr)
489 | _ = bytes.Contains(str, []byte("Dolor"))
490 | }
491 | })
492 |
493 | b.Run("RegexMatchString", func(b *testing.B) {
494 | re := regexp.MustCompile(`em Ip`)
495 | for n := 0; n < b.N; n++ {
496 | _ = re.MatchString("Lorem Ipsum")
497 | _ = re.MatchString("Dolor")
498 | }
499 | })
500 |
501 | b.Run("RegexMatch", func(b *testing.B) {
502 | for n := 0; n < b.N; n++ {
503 | _, _ = regexp.MatchString(`em Ip`, "Lorem Ipsum")
504 | _, _ = regexp.MatchString(`em Ip`, "Dolor")
505 | }
506 | })
507 | }
508 | ```
509 |
510 | ```
511 | $ go test -bench . -benchmem
512 | goos: darwin
513 | goarch: arm64
514 | cpu: Apple M2 Max
515 | BenchmarkContains-12 242450262 4.748 ns/op 0 B/op 0 allocs/op
516 | BenchmarkContainsNot-12 203046486 5.942 ns/op 0 B/op 0 allocs/op
517 | BenchmarkContainsBytes-12 225166587 5.339 ns/op 0 B/op 0 allocs/op
518 | BenchmarkContainsBytesNot-12 183250030 6.531 ns/op 0 B/op 0 allocs/op
519 | BenchmarkCompileMatch-12 25628521 47.09 ns/op 0 B/op 0 allocs/op
520 | BenchmarkCompileMatchNot-12 50846470 23.77 ns/op 0 B/op 0 allocs/op
521 | BenchmarkMatch-12 1872439 644.3 ns/op 1397 B/op 17 allocs/op
522 | BenchmarkMatchNot-12 1981207 608.1 ns/op 1397 B/op 17 allocs/op
523 | BenchmarkContainsMethods/Strings.Contains-12 100000000 10.40 ns/op 0 B/op 0 allocs/op
524 | BenchmarkContainsMethods/Bytes.Contains-12 100000000 10.98 ns/op 0 B/op 0 allocs/op
525 | BenchmarkContainsMethods/RegexMatchString-12 17969552 66.61 ns/op 0 B/op 0 allocs/op
526 | BenchmarkContainsMethods/RegexMatch-12 885897 1257 ns/op 2795 B/op 34 allocs/op
527 | PASS
528 | ok _/Users/simonwaldherr/git/golang-benchmarks/contains 18.077s
529 | ```
530 |
531 | ### embed
532 |
533 | ```go
534 | package embed
535 |
536 | import (
537 | _ "embed"
538 | "io/ioutil"
539 | "os"
540 | "testing"
541 | )
542 |
543 | //go:embed example.txt
544 | var embeddedFile []byte
545 |
546 | func BenchmarkEmbed(b *testing.B) {
547 | for i := 0; i < b.N; i++ {
548 | // Access the embedded file
549 | _ = embeddedFile
550 | }
551 | }
552 |
553 | func BenchmarkReadFile(b *testing.B) {
554 | for i := 0; i < b.N; i++ {
555 | // Read the file from disk
556 | data, err := os.ReadFile("example.txt")
557 | if err != nil {
558 | b.Fatalf("failed to read file: %v", err)
559 | }
560 | _ = data
561 | }
562 | }
563 |
564 | func BenchmarkIoutilReadFile(b *testing.B) {
565 | for i := 0; i < b.N; i++ {
566 | // Read the file using ioutil
567 | data, err := ioutil.ReadFile("example.txt")
568 | if err != nil {
569 | b.Fatalf("failed to read file: %v", err)
570 | }
571 | _ = data
572 | }
573 | }
574 | ```
575 |
576 | ```
577 | $ go test -bench . -benchmem
578 | goos: darwin
579 | goarch: arm64
580 | cpu: Apple M2 Max
581 | BenchmarkEmbed-12 1000000000 0.2964 ns/op 0 B/op 0 allocs/op
582 | BenchmarkReadFile-12 152505 7331 ns/op 840 B/op 5 allocs/op
583 | BenchmarkIoutilReadFile-12 164889 7278 ns/op 840 B/op 5 allocs/op
584 | PASS
585 | ok _/Users/simonwaldherr/git/golang-benchmarks/embed 2.986s
586 | ```
587 |
588 | ### floodfill
589 |
590 | ```go
591 | // Package floodfill benchmarks various flood fill implementations.
592 | package main
593 |
594 | import (
595 | "testing"
596 | )
597 |
598 | // Rekursive Implementierung
599 | func floodFillRecursive(image [][]int, sr int, sc int, newColor int) [][]int {
600 | oldColor := image[sr][sc]
601 | if oldColor == newColor {
602 | return image
603 | }
604 | floodFillRecurse(image, sr, sc, oldColor, newColor)
605 | return image
606 | }
607 |
608 | func floodFillRecurse(image [][]int, sr int, sc int, oldColor int, newColor int) {
609 | if sr < 0 || sr >= len(image) || sc < 0 || sc >= len(image[0]) || image[sr][sc] != oldColor {
610 | return
611 | }
612 | image[sr][sc] = newColor
613 |
614 | floodFillRecurse(image, sr+1, sc, oldColor, newColor)
615 | floodFillRecurse(image, sr-1, sc, oldColor, newColor)
616 | floodFillRecurse(image, sr, sc+1, oldColor, newColor)
617 | floodFillRecurse(image, sr, sc-1, oldColor, newColor)
618 | }
619 |
620 | // Iterative Implementierung mit Stack (DFS)
621 | func floodFillDFS(image [][]int, sr int, sc int, newColor int) [][]int {
622 | oldColor := image[sr][sc]
623 | if oldColor == newColor {
624 | return image
625 | }
626 |
627 | stack := [][]int{{sr, sc}}
628 | for len(stack) > 0 {
629 | current := stack[len(stack)-1]
630 | stack = stack[:len(stack)-1]
631 |
632 | r, c := current[0], current[1]
633 | if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
634 | continue
635 | }
636 |
637 | image[r][c] = newColor
638 |
639 | stack = append(stack, []int{r + 1, c})
640 | stack = append(stack, []int{r - 1, c})
641 | stack = append(stack, []int{r, c + 1})
642 | stack = append(stack, []int{r, c - 1})
643 | }
644 | return image
645 | }
646 |
647 | // Iterative Implementierung mit Queue (BFS)
648 | func floodFillBFS(image [][]int, sr int, sc int, newColor int) [][]int {
649 | oldColor := image[sr][sc]
650 | if oldColor == newColor {
651 | return image
652 | }
653 |
654 | queue := [][]int{{sr, sc}}
655 | for len(queue) > 0 {
656 | current := queue[0]
657 | queue = queue[1:]
658 |
659 | r, c := current[0], current[1]
660 | if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
661 | continue
662 | }
663 |
664 | image[r][c] = newColor
665 |
666 | queue = append(queue, []int{r + 1, c})
667 | queue = append(queue, []int{r - 1, c})
668 | queue = append(queue, []int{r, c + 1})
669 | queue = append(queue, []int{r, c - 1})
670 | }
671 | return image
672 | }
673 |
674 | // Iterative Implementierung mit Stack (4-Wege-Verbindung)
675 | func floodFillStack4Way(image [][]int, sr int, sc int, newColor int) [][]int {
676 | oldColor := image[sr][sc]
677 | if oldColor == newColor {
678 | return image
679 | }
680 |
681 | stack := [][]int{{sr, sc}}
682 | directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
683 | for len(stack) > 0 {
684 | current := stack[len(stack)-1]
685 | stack = stack[:len(stack)-1]
686 |
687 | r, c := current[0], current[1]
688 | if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
689 | continue
690 | }
691 |
692 | image[r][c] = newColor
693 |
694 | for _, dir := range directions {
695 | stack = append(stack, []int{r + dir[0], c + dir[1]})
696 | }
697 | }
698 | return image
699 | }
700 |
701 | // Komplexes Beispielbild
702 | var complexImage = [][]int{
703 | {0, 0, 0, 0, 0, 0},
704 | {0, 1, 1, 0, 1, 0},
705 | {0, 1, 0, 0, 1, 0},
706 | {0, 1, 1, 1, 1, 0},
707 | {0, 0, 0, 0, 0, 0},
708 | {0, 1, 1, 0, 1, 1},
709 | }
710 |
711 | // Benchmark für die rekursive Implementierung
712 | func BenchmarkFloodFillRecursive(b *testing.B) {
713 | for i := 0; i < b.N; i++ {
714 | image := make([][]int, len(complexImage))
715 | for j := range complexImage {
716 | image[j] = make([]int, len(complexImage[j]))
717 | copy(image[j], complexImage[j])
718 | }
719 | floodFillRecursive(image, 1, 1, 2)
720 | }
721 | }
722 |
723 | // Benchmark für die iterative Implementierung mit Stack (DFS)
724 | func BenchmarkFloodFillDFS(b *testing.B) {
725 | for i := 0; i < b.N; i++ {
726 | image := make([][]int, len(complexImage))
727 | for j := range complexImage {
728 | image[j] = make([]int, len(complexImage[j]))
729 | copy(image[j], complexImage[j])
730 | }
731 | floodFillDFS(image, 1, 1, 2)
732 | }
733 | }
734 |
735 | // Benchmark für die iterative Implementierung mit Queue (BFS)
736 | func BenchmarkFloodFillBFS(b *testing.B) {
737 | for i := 0; i < b.N; i++ {
738 | image := make([][]int, len(complexImage))
739 | for j := range complexImage {
740 | image[j] = make([]int, len(complexImage[j]))
741 | copy(image[j], complexImage[j])
742 | }
743 | floodFillBFS(image, 1, 1, 2)
744 | }
745 | }
746 |
747 | // Benchmark für die iterative Implementierung mit Stack (4-Wege-Verbindung)
748 | func BenchmarkFloodFillStack4Way(b *testing.B) {
749 | for i := 0; i < b.N; i++ {
750 | image := make([][]int, len(complexImage))
751 | for j := range complexImage {
752 | image[j] = make([]int, len(complexImage[j]))
753 | copy(image[j], complexImage[j])
754 | }
755 | floodFillStack4Way(image, 1, 1, 2)
756 | }
757 | }
758 | ```
759 |
760 | ```
761 | $ go test -bench . -benchmem
762 | goos: darwin
763 | goarch: arm64
764 | cpu: Apple M2 Max
765 | BenchmarkFloodFillRecursive-12 5962818 181.5 ns/op 432 B/op 7 allocs/op
766 | BenchmarkFloodFillDFS-12 1496862 795.0 ns/op 1744 B/op 48 allocs/op
767 | BenchmarkFloodFillBFS-12 1000000 1003 ns/op 2728 B/op 54 allocs/op
768 | BenchmarkFloodFillStack4Way-12 1456789 823.9 ns/op 1744 B/op 48 allocs/op
769 | PASS
770 | ok _/Users/simonwaldherr/git/golang-benchmarks/floodfill 6.503s
771 | ```
772 |
773 | ### foreach
774 |
775 | ```go
776 | // Package foreach benchmarks ranging over slices and maps.
777 | package foreach
778 |
779 | import (
780 | "testing"
781 | )
782 |
783 | var amap map[int]string
784 | var aslice []string
785 |
786 | func init() {
787 | amap = map[int]string{
788 | 0: "lorem",
789 | 1: "ipsum",
790 | 2: "dolor",
791 | 3: "sit",
792 | 4: "amet",
793 | }
794 |
795 | aslice = []string{
796 | "lorem",
797 | "ipsum",
798 | "dolor",
799 | "sit",
800 | "amet",
801 | }
802 | }
803 |
804 | func forMap() {
805 | for i := 0; i < len(amap); i++ {
806 | _ = amap[i]
807 | }
808 | }
809 |
810 | func rangeMap() {
811 | for _, v := range amap {
812 | _ = v
813 | }
814 | }
815 |
816 | func rangeSlice() {
817 | for _, v := range aslice {
818 | _ = v
819 | }
820 | }
821 |
822 | func rangeSliceKey() {
823 | for k := range aslice {
824 | _ = aslice[k]
825 | }
826 | }
827 |
828 | func BenchmarkForMap(b *testing.B) {
829 | for n := 0; n < b.N; n++ {
830 | forMap()
831 | }
832 | }
833 |
834 | func BenchmarkRangeMap(b *testing.B) {
835 | for n := 0; n < b.N; n++ {
836 | rangeMap()
837 | }
838 | }
839 |
840 | func BenchmarkRangeSlice(b *testing.B) {
841 | for n := 0; n < b.N; n++ {
842 | rangeSlice()
843 | }
844 | }
845 |
846 | func BenchmarkRangeSliceKey(b *testing.B) {
847 | for n := 0; n < b.N; n++ {
848 | rangeSliceKey()
849 | }
850 | }
851 | ```
852 |
853 | ```
854 | $ go test -bench . -benchmem
855 | goos: darwin
856 | goarch: arm64
857 | cpu: Apple M2 Max
858 | BenchmarkForMap-12 64878922 18.27 ns/op 0 B/op 0 allocs/op
859 | BenchmarkRangeMap-12 29856315 40.45 ns/op 0 B/op 0 allocs/op
860 | BenchmarkRangeSlice-12 458876170 2.602 ns/op 0 B/op 0 allocs/op
861 | BenchmarkRangeSliceKey-12 463405524 2.598 ns/op 0 B/op 0 allocs/op
862 | PASS
863 | ok _/Users/simonwaldherr/git/golang-benchmarks/foreach 6.249s
864 | ```
865 |
866 | ### hash
867 |
868 | ```go
869 | // Package hash benchmarks various hashing algorithms.
870 | // Especially with hashing algorithms, faster is not always better.
871 | // One should always decide on the basis of the respective requirements.
872 | package hash
873 |
874 | import (
875 | "crypto/md5"
876 | "crypto/sha1"
877 | "crypto/sha256"
878 | "crypto/sha512"
879 | "hash"
880 | "hash/adler32"
881 | "hash/crc32"
882 | "hash/crc64"
883 | "hash/fnv"
884 | "math/rand"
885 | "testing"
886 |
887 | "github.com/jzelinskie/whirlpool"
888 | "github.com/reusee/mmh3"
889 | "github.com/zeebo/blake3"
890 | "golang.org/x/crypto/bcrypt"
891 | "golang.org/x/crypto/blake2b"
892 | "golang.org/x/crypto/md4"
893 | "golang.org/x/crypto/ripemd160"
894 | "golang.org/x/crypto/sha3"
895 | )
896 |
897 | func benchmarkHashAlgo(b *testing.B, h hash.Hash) {
898 | data := make([]byte, 2048)
899 | rand.Read(data)
900 |
901 | b.ResetTimer()
902 | for n := 0; n < b.N; n++ {
903 | h.Reset()
904 | h.Write(data)
905 | _ = h.Sum(nil)
906 | }
907 | }
908 |
909 | func benchmarkBCryptHashAlgo(b *testing.B, cost int) {
910 | data := make([]byte, 2048)
911 | rand.Read(data)
912 |
913 | b.ResetTimer()
914 | for n := 0; n < b.N; n++ {
915 | bcrypt.GenerateFromPassword(data, cost)
916 | }
917 | }
918 |
919 | func BenchmarkAdler32(b *testing.B) {
920 | benchmarkHashAlgo(b, adler32.New())
921 | }
922 |
923 | func BenchmarkBCryptCost4(b *testing.B) {
924 | benchmarkBCryptHashAlgo(b, 4)
925 | }
926 |
927 | func BenchmarkBCryptCost10(b *testing.B) {
928 | benchmarkBCryptHashAlgo(b, 10)
929 | }
930 |
931 | func BenchmarkBCryptCost16(b *testing.B) {
932 | benchmarkBCryptHashAlgo(b, 16)
933 | }
934 |
935 | /*
936 | func BenchmarkBCryptCost22(b *testing.B) {
937 | benchmarkBCryptHashAlgo(b, 22)
938 | }
939 |
940 | func BenchmarkBCryptCost28(b *testing.B) {
941 | benchmarkBCryptHashAlgo(b, 28)
942 | }
943 |
944 | func BenchmarkBCryptCost31(b *testing.B) {
945 | benchmarkBCryptHashAlgo(b, 31)
946 | }
947 | */
948 |
949 | func BenchmarkBlake2b256(b *testing.B) {
950 | h, err := blake2b.New256(nil)
951 | if err != nil {
952 | b.Fatal(err)
953 | }
954 | benchmarkHashAlgo(b, h)
955 | }
956 |
957 | func BenchmarkBlake2b512(b *testing.B) {
958 | h, err := blake2b.New512(nil)
959 | if err != nil {
960 | b.Fatal(err)
961 | }
962 | benchmarkHashAlgo(b, h)
963 | }
964 |
965 | func BenchmarkBlake3256(b *testing.B) {
966 | benchmarkHashAlgo(b, blake3.New())
967 | }
968 |
969 | func BenchmarkMMH3(b *testing.B) {
970 | benchmarkHashAlgo(b, mmh3.New128())
971 | }
972 |
973 | func BenchmarkCRC32(b *testing.B) {
974 | benchmarkHashAlgo(b, crc32.NewIEEE())
975 | }
976 |
977 | func BenchmarkCRC64ISO(b *testing.B) {
978 | benchmarkHashAlgo(b, crc64.New(crc64.MakeTable(crc64.ISO)))
979 | }
980 |
981 | func BenchmarkCRC64ECMA(b *testing.B) {
982 | benchmarkHashAlgo(b, crc64.New(crc64.MakeTable(crc64.ECMA)))
983 | }
984 |
985 | func BenchmarkFnv32(b *testing.B) {
986 | benchmarkHashAlgo(b, fnv.New32())
987 | }
988 |
989 | func BenchmarkFnv32a(b *testing.B) {
990 | benchmarkHashAlgo(b, fnv.New32a())
991 | }
992 |
993 | func BenchmarkFnv64(b *testing.B) {
994 | benchmarkHashAlgo(b, fnv.New64())
995 | }
996 |
997 | func BenchmarkFnv64a(b *testing.B) {
998 | benchmarkHashAlgo(b, fnv.New64a())
999 | }
1000 |
1001 | func BenchmarkFnv128(b *testing.B) {
1002 | benchmarkHashAlgo(b, fnv.New128())
1003 | }
1004 |
1005 | func BenchmarkFnv128a(b *testing.B) {
1006 | benchmarkHashAlgo(b, fnv.New128a())
1007 | }
1008 |
1009 | func BenchmarkMD4(b *testing.B) {
1010 | benchmarkHashAlgo(b, md4.New())
1011 | }
1012 |
1013 | func BenchmarkMD5(b *testing.B) {
1014 | benchmarkHashAlgo(b, md5.New())
1015 | }
1016 |
1017 | func BenchmarkSHA1(b *testing.B) {
1018 | benchmarkHashAlgo(b, sha1.New())
1019 | }
1020 |
1021 | func BenchmarkSHA224(b *testing.B) {
1022 | benchmarkHashAlgo(b, sha256.New224())
1023 | }
1024 |
1025 | func BenchmarkSHA256(b *testing.B) {
1026 | benchmarkHashAlgo(b, sha256.New())
1027 | }
1028 |
1029 | func BenchmarkSHA384(b *testing.B) {
1030 | benchmarkHashAlgo(b, sha512.New384())
1031 | }
1032 |
1033 | func BenchmarkSHA512(b *testing.B) {
1034 | benchmarkHashAlgo(b, sha512.New())
1035 | }
1036 |
1037 | func BenchmarkSHA3256(b *testing.B) {
1038 | benchmarkHashAlgo(b, sha3.New256())
1039 | }
1040 |
1041 | func BenchmarkSHA3512(b *testing.B) {
1042 | benchmarkHashAlgo(b, sha3.New512())
1043 | }
1044 |
1045 | func BenchmarkRIPEMD160(b *testing.B) {
1046 | benchmarkHashAlgo(b, ripemd160.New())
1047 | }
1048 |
1049 | func BenchmarkWhirlpool(b *testing.B) {
1050 | benchmarkHashAlgo(b, whirlpool.New())
1051 | }
1052 |
1053 | func BenchmarkSHA256Parallel(b *testing.B) {
1054 | b.RunParallel(func(pb *testing.PB) {
1055 | data := make([]byte, 2048)
1056 | rand.Read(data)
1057 | for pb.Next() {
1058 | h := sha256.New()
1059 | h.Write(data)
1060 | h.Sum(nil)
1061 | }
1062 | })
1063 | }
1064 | ```
1065 |
1066 | ```
1067 | $ go test -bench . -benchmem
1068 | goos: darwin
1069 | goarch: arm64
1070 | cpu: Apple M2 Max
1071 | BenchmarkAdler32-12 1755874 664.9 ns/op 8 B/op 1 allocs/op
1072 | BenchmarkBCryptCost4-12 620728717 1.929 ns/op 0 B/op 0 allocs/op
1073 | BenchmarkBCryptCost10-12 623147460 1.932 ns/op 0 B/op 0 allocs/op
1074 | BenchmarkBCryptCost16-12 618433970 1.928 ns/op 0 B/op 0 allocs/op
1075 | BenchmarkBlake2b256-12 476428 2537 ns/op 32 B/op 1 allocs/op
1076 | BenchmarkBlake2b512-12 473736 2544 ns/op 64 B/op 1 allocs/op
1077 | BenchmarkBlake3256-12 410496 2841 ns/op 64 B/op 2 allocs/op
1078 | BenchmarkMMH3-12 3808770 315.9 ns/op 16 B/op 1 allocs/op
1079 | BenchmarkCRC32-12 5186767 232.1 ns/op 8 B/op 1 allocs/op
1080 | BenchmarkCRC64ISO-12 1000000 1094 ns/op 8 B/op 1 allocs/op
1081 | BenchmarkCRC64ECMA-12 1000000 1089 ns/op 8 B/op 1 allocs/op
1082 | BenchmarkFnv32-12 512662 2344 ns/op 8 B/op 1 allocs/op
1083 | BenchmarkFnv32a-12 514120 2330 ns/op 8 B/op 1 allocs/op
1084 | BenchmarkFnv64-12 513177 2334 ns/op 8 B/op 1 allocs/op
1085 | BenchmarkFnv64a-12 516385 2338 ns/op 8 B/op 1 allocs/op
1086 | BenchmarkFnv128-12 309386 3863 ns/op 24 B/op 2 allocs/op
1087 | BenchmarkFnv128a-12 486774 2497 ns/op 24 B/op 2 allocs/op
1088 | BenchmarkMD4-12 411661 2907 ns/op 24 B/op 2 allocs/op
1089 | BenchmarkMD5-12 406767 2935 ns/op 16 B/op 1 allocs/op
1090 | BenchmarkSHA1-12 1450699 826.7 ns/op 24 B/op 1 allocs/op
1091 | BenchmarkSHA224-12 1452280 830.0 ns/op 32 B/op 1 allocs/op
1092 | BenchmarkSHA256-12 1447124 827.3 ns/op 32 B/op 1 allocs/op
1093 | BenchmarkSHA384-12 842685 1432 ns/op 48 B/op 1 allocs/op
1094 | BenchmarkSHA512-12 842818 1429 ns/op 64 B/op 1 allocs/op
1095 | BenchmarkSHA3256-12 231009 5184 ns/op 480 B/op 2 allocs/op
1096 | BenchmarkSHA3512-12 132553 9054 ns/op 512 B/op 2 allocs/op
1097 | BenchmarkRIPEMD160-12 199648 6015 ns/op 24 B/op 1 allocs/op
1098 | BenchmarkWhirlpool-12 45268 26407 ns/op 64 B/op 1 allocs/op
1099 | BenchmarkSHA256Parallel-12 12267210 94.14 ns/op 32 B/op 1 allocs/op
1100 | PASS
1101 | ok _/Users/simonwaldherr/git/golang-benchmarks/hash 39.960s
1102 | ```
1103 |
1104 | ### index
1105 |
1106 | ```go
1107 | // Package index benchmarks access on maps with various data types as keys.
1108 | package index
1109 |
1110 | import (
1111 | "math/rand"
1112 | "strconv"
1113 | "testing"
1114 | )
1115 |
1116 | var NumItems int = 1000000
1117 |
1118 | var ms map[string]string
1119 | var ks []string
1120 |
1121 | var mi map[int]string
1122 | var ki []int
1123 |
1124 | func initMapStringIndex() {
1125 | ms = make(map[string]string)
1126 | ks = make([]string, 0)
1127 |
1128 | for i := 0; i < NumItems; i++ {
1129 | key := strconv.Itoa(rand.Intn(NumItems))
1130 | ms[key] = "value" + strconv.Itoa(i)
1131 | ks = append(ks, key)
1132 | }
1133 | }
1134 |
1135 | func initMapIntIndex() {
1136 | mi = make(map[int]string)
1137 | ki = make([]int, 0)
1138 |
1139 | for i := 0; i < NumItems; i++ {
1140 | key := rand.Intn(NumItems)
1141 | mi[key] = "value" + strconv.Itoa(i)
1142 | ki = append(ki, key)
1143 | }
1144 | }
1145 |
1146 | func init() {
1147 | initMapStringIndex()
1148 | initMapIntIndex()
1149 | }
1150 |
1151 | func BenchmarkMapStringKeys(b *testing.B) {
1152 | i := 0
1153 |
1154 | for n := 0; n < b.N; n++ {
1155 | if _, ok := ms[ks[i]]; ok {
1156 | }
1157 |
1158 | i++
1159 | if i >= NumItems {
1160 | i = 0
1161 | }
1162 | }
1163 | }
1164 |
1165 | func BenchmarkMapIntKeys(b *testing.B) {
1166 | i := 0
1167 |
1168 | for n := 0; n < b.N; n++ {
1169 | if _, ok := mi[ki[i]]; ok {
1170 | }
1171 |
1172 | i++
1173 | if i >= NumItems {
1174 | i = 0
1175 | }
1176 | }
1177 | }
1178 |
1179 | func BenchmarkMapStringIndex(b *testing.B) {
1180 | i := 0
1181 |
1182 | for n := 0; n < b.N; n++ {
1183 | _ = ms[ks[i]]
1184 |
1185 | i++
1186 | if i >= NumItems {
1187 | i = 0
1188 | }
1189 | }
1190 | }
1191 |
1192 | func BenchmarkMapIntIndex(b *testing.B) {
1193 | i := 0
1194 |
1195 | for n := 0; n < b.N; n++ {
1196 | _ = mi[ki[i]]
1197 |
1198 | i++
1199 | if i >= NumItems {
1200 | i = 0
1201 | }
1202 | }
1203 | }
1204 | ```
1205 |
1206 | ```
1207 | $ go test -bench . -benchmem
1208 | goos: darwin
1209 | goarch: arm64
1210 | cpu: Apple M2 Max
1211 | BenchmarkMapStringKeys-12 26338965 41.49 ns/op 0 B/op 0 allocs/op
1212 | BenchmarkMapIntKeys-12 77105655 15.35 ns/op 0 B/op 0 allocs/op
1213 | BenchmarkMapStringIndex-12 26142556 41.40 ns/op 0 B/op 0 allocs/op
1214 | BenchmarkMapIntIndex-12 64173840 15.64 ns/op 0 B/op 0 allocs/op
1215 | PASS
1216 | ok _/Users/simonwaldherr/git/golang-benchmarks/index 6.309s
1217 | ```
1218 |
1219 | ### json
1220 |
1221 | ```go
1222 | package json
1223 |
1224 | import (
1225 | "encoding/json"
1226 | "math"
1227 | "math/big"
1228 | "testing"
1229 | "time"
1230 | )
1231 |
1232 | type Data struct {
1233 | String string
1234 | Time time.Time
1235 | Int int
1236 | Int8 int8
1237 | Int16 int16
1238 | Int32 int32
1239 | Int64 int64
1240 | Boolean bool
1241 | Float32 float32
1242 | Float64 float64
1243 | BigInt big.Int
1244 | BigFloat big.Float
1245 | }
1246 |
1247 | func BenchmarkJsonMarshal(b *testing.B) {
1248 | for n := 0; n < b.N; n++ {
1249 | var d = Data{
1250 | String: "",
1251 | Time: time.Now(),
1252 | Int: math.MaxInt32,
1253 | Int8: math.MaxInt8,
1254 | Int16: math.MaxInt16,
1255 | Int32: math.MaxInt32,
1256 | Int64: math.MaxInt64,
1257 | Boolean: false,
1258 | Float32: math.MaxFloat32,
1259 | Float64: math.MaxFloat64,
1260 | BigInt: *big.NewInt(math.MaxInt64),
1261 | BigFloat: *big.NewFloat(math.MaxFloat64),
1262 | }
1263 |
1264 | _, err := json.Marshal(d)
1265 | if err != nil {
1266 | b.Error(err)
1267 | b.Fail()
1268 | return
1269 | }
1270 | }
1271 | }
1272 |
1273 | func BenchmarkJsonUnmarshal(b *testing.B) {
1274 | str := `
1275 | {
1276 | "String": "",
1277 | "Time": "2019-10-30T16:41:29.853426+07:00",
1278 | "Int": 2147483647,
1279 | "Int8": 127,
1280 | "Int16": 32767,
1281 | "Int32": 2147483647,
1282 | "Int64": 9223372036854775807,
1283 | "Boolean": false,
1284 | "Float32": 3.4028235e+38,
1285 | "Float64": 1.7976931348623157e+308,
1286 | "BigInt": 9999999999999999999,
1287 | "BigFloat": "2.7976931348623157e+308"
1288 | }
1289 | `
1290 |
1291 | for n := 0; n < b.N; n++ {
1292 | var d Data
1293 | err := json.Unmarshal([]byte(str), &d)
1294 | if err != nil {
1295 | b.Error(err)
1296 | b.Fail()
1297 | return
1298 | }
1299 | }
1300 | }
1301 | ```
1302 |
1303 | ```
1304 | $ go test -bench . -benchmem
1305 | goos: darwin
1306 | goarch: arm64
1307 | cpu: Apple M2 Max
1308 | BenchmarkJsonMarshal-12 1711849 683.5 ns/op 480 B/op 5 allocs/op
1309 | BenchmarkJsonUnmarshal-12 348568 3445 ns/op 1816 B/op 27 allocs/op
1310 | PASS
1311 | ok _/Users/simonwaldherr/git/golang-benchmarks/json 3.287s
1312 | ```
1313 |
1314 | ### math
1315 |
1316 | ```go
1317 | // Package math compares the speed of various mathematical operations.
1318 | package math
1319 |
1320 | import (
1321 | "sync"
1322 | "sync/atomic"
1323 | "testing"
1324 | )
1325 |
1326 | func BenchmarkMathInt8(b *testing.B) {
1327 | var intVal int8
1328 | for n := 0; n < b.N; n++ {
1329 | intVal = intVal + 2
1330 | }
1331 | }
1332 |
1333 | func BenchmarkMathInt32(b *testing.B) {
1334 | var intVal int32
1335 | for n := 0; n < b.N; n++ {
1336 | intVal = intVal + 2
1337 | }
1338 | }
1339 |
1340 | func BenchmarkMathInt64(b *testing.B) {
1341 | var intVal int64
1342 | for n := 0; n < b.N; n++ {
1343 | intVal = intVal + 2
1344 | }
1345 | }
1346 |
1347 | func BenchmarkMathAtomicInt32(b *testing.B) {
1348 | var intVal int32
1349 | for n := 0; n < b.N; n++ {
1350 | atomic.AddInt32(&intVal, 2)
1351 | }
1352 | }
1353 |
1354 | func BenchmarkMathAtomicInt64(b *testing.B) {
1355 | var intVal int64
1356 | for n := 0; n < b.N; n++ {
1357 | atomic.AddInt64(&intVal, 2)
1358 | }
1359 | }
1360 |
1361 | type IntMutex struct {
1362 | v int64
1363 | mux sync.Mutex
1364 | }
1365 |
1366 | func BenchmarkMathMutexInt(b *testing.B) {
1367 | var m IntMutex
1368 | for n := 0; n < b.N; n++ {
1369 | m.mux.Lock()
1370 | m.v = m.v + 2
1371 | m.mux.Unlock()
1372 | }
1373 | }
1374 |
1375 | func BenchmarkMathFloat32(b *testing.B) {
1376 | var floatVal float32
1377 | for n := 0; n < b.N; n++ {
1378 | floatVal = floatVal + 2
1379 | }
1380 | }
1381 |
1382 | func BenchmarkMathFloat64(b *testing.B) {
1383 | var floatVal float64
1384 | for n := 0; n < b.N; n++ {
1385 | floatVal = floatVal + 2
1386 | }
1387 | }
1388 | ```
1389 |
1390 | ```
1391 | $ go test -bench . -benchmem
1392 | goos: darwin
1393 | goarch: arm64
1394 | cpu: Apple M2 Max
1395 | BenchmarkMathInt8-12 1000000000 0.2976 ns/op 0 B/op 0 allocs/op
1396 | BenchmarkMathInt32-12 1000000000 0.2881 ns/op 0 B/op 0 allocs/op
1397 | BenchmarkMathInt64-12 1000000000 0.2874 ns/op 0 B/op 0 allocs/op
1398 | BenchmarkMathAtomicInt32-12 305561536 3.922 ns/op 0 B/op 0 allocs/op
1399 | BenchmarkMathAtomicInt64-12 305254834 3.925 ns/op 0 B/op 0 allocs/op
1400 | BenchmarkMathMutexInt-12 154337096 7.757 ns/op 0 B/op 0 allocs/op
1401 | BenchmarkMathFloat32-12 1000000000 0.2912 ns/op 0 B/op 0 allocs/op
1402 | BenchmarkMathFloat64-12 1000000000 0.2878 ns/op 0 B/op 0 allocs/op
1403 | PASS
1404 | ok _/Users/simonwaldherr/git/golang-benchmarks/math 6.962s
1405 | ```
1406 |
1407 | ### parse
1408 |
1409 | ```go
1410 | // Package parse benchmarks parsing.
1411 | package parse
1412 |
1413 | import (
1414 | "strconv"
1415 | "testing"
1416 | )
1417 |
1418 | func BenchmarkParseBool(b *testing.B) {
1419 | for n := 0; n < b.N; n++ {
1420 | _, err := strconv.ParseBool("true")
1421 | if err != nil {
1422 | panic(err)
1423 | }
1424 | }
1425 | }
1426 |
1427 | func BenchmarkParseInt(b *testing.B) {
1428 | for n := 0; n < b.N; n++ {
1429 | _, err := strconv.ParseInt("1337", 10, 64)
1430 | if err != nil {
1431 | panic(err)
1432 | }
1433 | }
1434 | }
1435 |
1436 | func BenchmarkParseFloat(b *testing.B) {
1437 | for n := 0; n < b.N; n++ {
1438 | _, err := strconv.ParseFloat("3.141592653589793238462643383", 64)
1439 | if err != nil {
1440 | panic(err)
1441 | }
1442 | }
1443 | }
1444 | ```
1445 |
1446 | ```
1447 | $ go test -bench . -benchmem
1448 | goos: darwin
1449 | goarch: arm64
1450 | cpu: Apple M2 Max
1451 | BenchmarkParseBool-12 1000000000 0.2964 ns/op 0 B/op 0 allocs/op
1452 | BenchmarkParseInt-12 123106347 9.746 ns/op 0 B/op 0 allocs/op
1453 | BenchmarkParseFloat-12 20415453 58.61 ns/op 0 B/op 0 allocs/op
1454 | PASS
1455 | ok _/Users/simonwaldherr/git/golang-benchmarks/parse 3.952s
1456 | ```
1457 |
1458 | ### random
1459 |
1460 | ```go
1461 | // Package random compares math/rand with crypto/rand.
1462 | // math/rand is much faster than crypto/rand, but it
1463 | // returns only a pseudo random number.
1464 | package random
1465 |
1466 | import (
1467 | crand "crypto/rand"
1468 | "encoding/base64"
1469 | "math/big"
1470 | mrand "math/rand"
1471 | "testing"
1472 | )
1473 |
1474 | func BenchmarkMathRand(b *testing.B) {
1475 | for n := 0; n < b.N; n++ {
1476 | mrand.Int63n(0xFFFF)
1477 | }
1478 | }
1479 |
1480 | func BenchmarkCryptoRand(b *testing.B) {
1481 | for n := 0; n < b.N; n++ {
1482 | _, err := crand.Int(crand.Reader, big.NewInt(0xFFFF))
1483 | if err != nil {
1484 | panic(err)
1485 | }
1486 | }
1487 | }
1488 |
1489 | func BenchmarkCryptoRandString(b *testing.B) {
1490 | for n := 0; n < b.N; n++ {
1491 | _, err := GenerateRandomString(32)
1492 | if err != nil {
1493 | panic(err)
1494 | }
1495 | }
1496 | }
1497 |
1498 | func BenchmarkCryptoRandBytes(b *testing.B) {
1499 | for n := 0; n < b.N; n++ {
1500 | _, err := GenerateRandomBytes(32)
1501 | if err != nil {
1502 | panic(err)
1503 | }
1504 | }
1505 | }
1506 |
1507 | func GenerateRandomBytes(n int) ([]byte, error) {
1508 | b := make([]byte, n)
1509 | _, err := mrand.Read(b)
1510 | if err != nil {
1511 | return nil, err
1512 | }
1513 |
1514 | return b, nil
1515 | }
1516 |
1517 | func GenerateRandomString(s int) (string, error) {
1518 | b, err := GenerateRandomBytes(s)
1519 | return base64.URLEncoding.EncodeToString(b), err
1520 | }
1521 | ```
1522 |
1523 | ```
1524 | $ go test -bench . -benchmem
1525 | goos: darwin
1526 | goarch: arm64
1527 | cpu: Apple M2 Max
1528 | BenchmarkMathRand-12 175220616 6.685 ns/op 0 B/op 0 allocs/op
1529 | BenchmarkCryptoRand-12 10221548 117.6 ns/op 48 B/op 3 allocs/op
1530 | BenchmarkCryptoRandString-12 11165036 107.0 ns/op 128 B/op 3 allocs/op
1531 | BenchmarkCryptoRandBytes-12 20872166 57.37 ns/op 32 B/op 1 allocs/op
1532 | PASS
1533 | ok _/Users/simonwaldherr/git/golang-benchmarks/random 5.914s
1534 | ```
1535 |
1536 | ### regexp
1537 |
1538 | ```go
1539 | // Package regexp benchmarks the performance of a pre-compiled regexp match
1540 | // a non-pre-compiled match and JIT-cached-compilation via golibs: https://simonwaldherr.de/go/golibs
1541 | package regexp
1542 |
1543 | import (
1544 | "regexp"
1545 | "testing"
1546 |
1547 | "simonwaldherr.de/go/golibs/regex"
1548 | )
1549 |
1550 | var regexpStr string = `^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,9}$`
1551 |
1552 | func BenchmarkMatchString(b *testing.B) {
1553 | for n := 0; n < b.N; n++ {
1554 | _, err := regexp.MatchString(regexpStr, "john.doe@example.tld")
1555 | if err != nil {
1556 | panic(err)
1557 | }
1558 | }
1559 | }
1560 |
1561 | func BenchmarkMatchStringCompiled(b *testing.B) {
1562 | r, err := regexp.Compile(regexpStr)
1563 | if err != nil {
1564 | panic(err)
1565 | }
1566 |
1567 | b.ResetTimer()
1568 | for n := 0; n < b.N; n++ {
1569 | r.MatchString("john.doe@example.tld")
1570 | }
1571 | }
1572 |
1573 | func BenchmarkMatchStringGolibs(b *testing.B) {
1574 | for n := 0; n < b.N; n++ {
1575 | _, err := regex.MatchString("john.doe@example.tld", regexpStr)
1576 | if err != nil {
1577 | panic(err)
1578 | }
1579 | }
1580 | }
1581 | ```
1582 |
1583 | ```
1584 | $ go test -bench . -benchmem
1585 | goos: darwin
1586 | goarch: arm64
1587 | cpu: Apple M2 Max
1588 | BenchmarkMatchString-12 323158 3722 ns/op 10217 B/op 86 allocs/op
1589 | BenchmarkMatchStringCompiled-12 3796606 316.2 ns/op 0 B/op 0 allocs/op
1590 | BenchmarkMatchStringGolibs-12 3715104 323.4 ns/op 0 B/op 0 allocs/op
1591 | PASS
1592 | ok _/Users/simonwaldherr/git/golang-benchmarks/regexp 5.285s
1593 | ```
1594 |
1595 | ### template
1596 |
1597 | ```go
1598 | // Package template benchmarks the performance of different templating methods
1599 | package template
1600 |
1601 | import (
1602 | "bytes"
1603 | htmltemplate "html/template"
1604 | "regexp"
1605 | "testing"
1606 | texttemplate "text/template"
1607 | )
1608 |
1609 | // Define a struct to hold the data for the templates
1610 | type Data struct {
1611 | Name string
1612 | Address string
1613 | }
1614 |
1615 | // Prepare the templates and data
1616 | var (
1617 | data = Data{Name: "John Doe", Address: "123 Elm St"}
1618 |
1619 | textTplString = "Name: {{.Name}}, Address: {{.Address}}"
1620 | htmlTplString = "
Name: {{.Name}}
Address: {{.Address}}
"
1621 | regExpString = "Name: {{NAME}}, Address: {{ADDRESS}}"
1622 | )
1623 |
1624 | // Benchmark for text/template
1625 | func BenchmarkTextTemplate(b *testing.B) {
1626 | tpl, _ := texttemplate.New("text").Parse(textTplString)
1627 | b.ResetTimer()
1628 | for i := 0; i < b.N; i++ {
1629 | var buf bytes.Buffer
1630 | tpl.Execute(&buf, data)
1631 | }
1632 | }
1633 |
1634 | // Benchmark for html/template
1635 | func BenchmarkHTMLTemplate(b *testing.B) {
1636 | tpl, _ := htmltemplate.New("html").Parse(htmlTplString)
1637 | b.ResetTimer()
1638 | for i := 0; i < b.N; i++ {
1639 | var buf bytes.Buffer
1640 | tpl.Execute(&buf, data)
1641 | }
1642 | }
1643 |
1644 | // Benchmark for replacing placeholders using regexp
1645 | func BenchmarkRegExp(b *testing.B) {
1646 | rName := regexp.MustCompile(`{{NAME}}`)
1647 | rAddress := regexp.MustCompile(`{{ADDRESS}}`)
1648 | b.ResetTimer()
1649 | for i := 0; i < b.N; i++ {
1650 | result := rName.ReplaceAllString(regExpString, data.Name)
1651 | result = rAddress.ReplaceAllString(result, data.Address)
1652 | }
1653 | }
1654 | ```
1655 |
1656 | ```
1657 | $ go test -bench . -benchmem
1658 | goos: darwin
1659 | goarch: arm64
1660 | cpu: Apple M2 Max
1661 | BenchmarkTextTemplate-12 3574970 320.5 ns/op 272 B/op 5 allocs/op
1662 | BenchmarkHTMLTemplate-12 1000000 1076 ns/op 592 B/op 19 allocs/op
1663 | BenchmarkRegExp-12 2987456 401.8 ns/op 298 B/op 9 allocs/op
1664 | PASS
1665 | ok _/Users/simonwaldherr/git/golang-benchmarks/template 4.359s
1666 | ```
1667 |
1668 | ### trim
1669 |
1670 | ```go
1671 | ```
1672 |
1673 | ```
1674 | $ go test -bench . -benchmem
1675 | FAIL _/Users/simonwaldherr/git/golang-benchmarks/trim [setup failed]
1676 | ```
1677 |
1678 |
--------------------------------------------------------------------------------
/README.win.md:
--------------------------------------------------------------------------------
1 | # Go Benchmarks
2 |
3 | In programming in general, and in Golang in particular, many roads lead to Rome.
4 | From time to time I ask myself which of these ways is the fastest.
5 | In Golang there is a wonderful solution, with \`go test -bench\` you can measure the speed very easily and quickly.
6 | In order for you to benefit from it too, I will publish such benchmarks in this repository in the future.
7 |
8 | ## ToC
9 |
10 | * [base64](https://github.com/SimonWaldherr/golang-benchmarks#base64)
11 | * [between](https://github.com/SimonWaldherr/golang-benchmarks#between)
12 | * [concat](https://github.com/SimonWaldherr/golang-benchmarks#concat)
13 | * [contains](https://github.com/SimonWaldherr/golang-benchmarks#contains)
14 | * [foreach](https://github.com/SimonWaldherr/golang-benchmarks#foreach)
15 | * [hash](https://github.com/SimonWaldherr/golang-benchmarks#hash)
16 | * [index](https://github.com/SimonWaldherr/golang-benchmarks#index)
17 | * [json](https://github.com/SimonWaldherr/golang-benchmarks#json)
18 | * [math](https://github.com/SimonWaldherr/golang-benchmarks#math)
19 | * [parse](https://github.com/SimonWaldherr/golang-benchmarks#parse)
20 | * [random](https://github.com/SimonWaldherr/golang-benchmarks#random)
21 | * [regexp](https://github.com/SimonWaldherr/golang-benchmarks#regexp)
22 |
23 | ## Golang?
24 |
25 | I published another repository where I show some Golang examples.
26 | If you\'re interested in new programming languages, you should definitely take a look at Golang:
27 |
28 | * [Golang examples](https://github.com/SimonWaldherr/golang-examples)
29 | * [tour.golang.org](https://tour.golang.org/)
30 | * [Go by example](https://gobyexample.com/)
31 | * [Golang Book](http://www.golang-book.com/)
32 | * [Go-Learn](https://github.com/skippednote/Go-Learn)
33 |
34 | ## Is it any good?
35 |
36 | [Yes](https://news.ycombinator.com/item?id=3067434)
37 |
38 | ## Benchmark Results
39 |
40 | Golang Version: go version devel +a6755fc0de Sat Nov 7 07:33:23 2020 +0000 windows/amd64
41 |
42 | ### base64
43 |
44 | ```go
45 | // Package base64 benchmarks some base64 functions.
46 | // On all tested systems it's faster to decode a
47 | // base64 encoded string instead of a check via regex.
48 | package base64
49 |
50 | import (
51 | "encoding/base64"
52 | "regexp"
53 | "testing"
54 | )
55 |
56 | func base64decode(s string) bool {
57 | _, err := base64.StdEncoding.DecodeString(s)
58 | return err == nil
59 | }
60 |
61 | func base64regex(s string) bool {
62 | matched, _ := regexp.MatchString(`^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$`, s)
63 | return matched
64 | }
65 |
66 | func BenchmarkBase64decode(b *testing.B) {
67 | isNotBase64 := `Invalid string`
68 | isBase64 := `VmFsaWQgc3RyaW5nCg==`
69 |
70 | for n := 0; n < b.N; n++ {
71 | base64decode(isNotBase64)
72 | base64decode(isBase64)
73 | }
74 | }
75 |
76 | func BenchmarkBase64regex(b *testing.B) {
77 | isNotBase64 := `Invalid string`
78 | isBase64 := `VmFsaWQgc3RyaW5nCg==`
79 |
80 | for n := 0; n < b.N; n++ {
81 | base64regex(isNotBase64)
82 | base64regex(isBase64)
83 | }
84 | }
85 | ```
86 |
87 | ```
88 | $ go test -bench . -benchmem
89 | goos: windows
90 | goarch: amd64
91 | pkg: github.com/SimonWaldherr/golang-benchmarks/base64
92 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
93 | BenchmarkBase64decode-8 5367226 245.6 ns/op 32 B/op 2 allocs/op
94 | BenchmarkBase64regex-8 30693 37875 ns/op 21444 B/op 198 allocs/op
95 | PASS
96 | ok github.com/SimonWaldherr/golang-benchmarks/base64 3.170s
97 | ```
98 |
99 | ### between
100 |
101 | ```go
102 | // Package between compares the performance of checking
103 | // if a number is between two other numbers via regex
104 | // and by parsing the number as integers.
105 | package between
106 |
107 | import (
108 | "regexp"
109 | "simonwaldherr.de/go/golibs/as"
110 | "simonwaldherr.de/go/ranger"
111 | "testing"
112 | )
113 |
114 | func BenchmarkNumberRegEx(b *testing.B) {
115 | re := ranger.Compile(89, 1001)
116 | re = "^(" + re + ")$"
117 | b.ResetTimer()
118 |
119 | for n := 0; n < b.N; n++ {
120 | matched, err := regexp.MatchString(re, "404")
121 | if !matched || err != nil {
122 | b.Log("Error in Benchmark")
123 | }
124 |
125 | matched, err = regexp.MatchString(re, "2000")
126 | if matched || err != nil {
127 | b.Log("Error in Benchmark")
128 | }
129 | }
130 | }
131 |
132 | func BenchmarkFulltextRegEx(b *testing.B) {
133 | re := ranger.Compile(89, 1001)
134 | re = " (" + re + ") "
135 | b.ResetTimer()
136 |
137 | for n := 0; n < b.N; n++ {
138 | matched, err := regexp.MatchString(re, "lorem ipsum 404 dolor sit")
139 | if !matched || err != nil {
140 | b.Log("Error in Benchmark")
141 | }
142 |
143 | matched, err = regexp.MatchString(re, "lorem ipsum 2000 dolor sit")
144 | if matched || err != nil {
145 | b.Log("Error in Benchmark")
146 | }
147 | }
148 | }
149 |
150 | func BenchmarkNumberParse(b *testing.B) {
151 | for n := 0; n < b.N; n++ {
152 | i1 := as.Int("404")
153 | i2 := as.Int("2000")
154 |
155 | if i1 < 89 || i1 > 1001 {
156 | b.Log("Error in Benchmark")
157 | }
158 |
159 | if !(i2 < 89 || i2 > 1001) {
160 | b.Log("Error in Benchmark")
161 | }
162 | }
163 | }
164 |
165 | func BenchmarkFulltextParse(b *testing.B) {
166 | re := regexp.MustCompile("[0-9]+")
167 | b.ResetTimer()
168 |
169 | for n := 0; n < b.N; n++ {
170 | i1 := as.Int(re.FindString("lorem ipsum 404 dolor sit"))
171 | i2 := as.Int(re.FindString("lorem ipsum 2000 dolor sit"))
172 |
173 | if i1 < 89 || i1 > 1001 {
174 | b.Log("Error in Benchmark")
175 | }
176 |
177 | if !(i2 < 89 || i2 > 1001) {
178 | b.Log("Error in Benchmark")
179 | }
180 | }
181 | }
182 | ```
183 |
184 | ```
185 | $ go test -bench . -benchmem
186 | goos: windows
187 | goarch: amd64
188 | pkg: github.com/SimonWaldherr/golang-benchmarks/between
189 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
190 | BenchmarkNumberRegEx-8 45007 26404 ns/op 16139 B/op 142 allocs/op
191 | BenchmarkFulltextRegEx-8 58605 21743 ns/op 11640 B/op 104 allocs/op
192 | BenchmarkNumberParse-8 12766242 95.73 ns/op 0 B/op 0 allocs/op
193 | BenchmarkFulltextParse-8 800138 1296 ns/op 32 B/op 2 allocs/op
194 | PASS
195 | ok github.com/SimonWaldherr/golang-benchmarks/between 5.382s
196 | ```
197 |
198 | ### concat
199 |
200 | ```go
201 | // Package concat benchmarks the performance of
202 | // various string concatenation methods.
203 | // Instead of just concatenating a string to another string
204 | // it is also possible (and much faster) to use
205 | // a bytes buffer.
206 | package concat
207 |
208 | import (
209 | "bytes"
210 | "strings"
211 | "testing"
212 | )
213 |
214 | func BenchmarkConcatString(b *testing.B) {
215 | var str string
216 | for n := 0; n < b.N; n++ {
217 | str += "x"
218 | }
219 | }
220 |
221 | func BenchmarkConcatBuffer(b *testing.B) {
222 | var buffer bytes.Buffer
223 | for n := 0; n < b.N; n++ {
224 | buffer.WriteString("x")
225 |
226 | }
227 | }
228 |
229 | func BenchmarkConcatBuilder(b *testing.B) {
230 | var builder strings.Builder
231 | for n := 0; n < b.N; n++ {
232 | builder.WriteString("x")
233 | }
234 | }
235 | ```
236 |
237 | ```
238 | $ go test -bench . -benchmem
239 | goos: windows
240 | goarch: amd64
241 | pkg: github.com/SimonWaldherr/golang-benchmarks/concat
242 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
243 | BenchmarkConcatString-8 654996 64654 ns/op 331436 B/op 1 allocs/op
244 | BenchmarkConcatBuffer-8 96764048 12.40 ns/op 2 B/op 0 allocs/op
245 | BenchmarkConcatBuilder-8 290770929 4.316 ns/op 5 B/op 0 allocs/op
246 | PASS
247 | ok github.com/SimonWaldherr/golang-benchmarks/concat 45.428s
248 | ```
249 |
250 | ### contains
251 |
252 | ```go
253 | // Package contains tests various ways of checking
254 | // if a string is contained in another string.
255 | package contains
256 |
257 | import (
258 | "bytes"
259 | "regexp"
260 | "strings"
261 | "testing"
262 | )
263 |
264 | // strings.Contains
265 | func contains() bool {
266 | return strings.Contains("Lorem Ipsum", "em Ip")
267 | }
268 |
269 | func containsNot() bool {
270 | return strings.Contains("Lorem Ipsum", "Dolor")
271 | }
272 |
273 | func TestContains(t *testing.T) {
274 | if contains() == false {
275 | t.Error("ERROR: contains")
276 | }
277 | if containsNot() == true {
278 | t.Error("ERROR: contains not")
279 | }
280 | }
281 |
282 | func BenchmarkContains(b *testing.B) {
283 | for n := 0; n < b.N; n++ {
284 | contains()
285 | }
286 | }
287 |
288 | func BenchmarkContainsNot(b *testing.B) {
289 | for n := 0; n < b.N; n++ {
290 | containsNot()
291 | }
292 | }
293 |
294 | // bytes.Contains
295 | func containsBytes() bool {
296 | return bytes.Contains([]byte("Lorem Ipsum"), []byte("em Ip"))
297 | }
298 |
299 | func containsBytesNot() bool {
300 | return bytes.Contains([]byte("Lorem Ipsum"), []byte("Dolor"))
301 | }
302 |
303 | func TestContainsBytes(t *testing.T) {
304 | if containsBytes() == false {
305 | t.Error("ERROR: bytes contains")
306 | }
307 | if containsBytesNot() == true {
308 | t.Error("ERROR: bytes contains not")
309 | }
310 | }
311 |
312 | func BenchmarkContainsBytes(b *testing.B) {
313 | for n := 0; n < b.N; n++ {
314 | containsBytes()
315 | }
316 | }
317 |
318 | func BenchmarkContainsBytesNot(b *testing.B) {
319 | for n := 0; n < b.N; n++ {
320 | containsBytesNot()
321 | }
322 | }
323 |
324 | // regexp.MustCompile + regexp.MatchString
325 | func compileMatch(re *regexp.Regexp) bool {
326 | matched := re.MatchString("Lorem Ipsum")
327 | return matched
328 | }
329 |
330 | func compileMatchNot(re *regexp.Regexp) bool {
331 | matched := re.MatchString("Lorem Ipsum")
332 | return matched
333 | }
334 |
335 | func TestCompileMatch(t *testing.T) {
336 | re1 := regexp.MustCompile("em Ip")
337 | re2 := regexp.MustCompile("Dolor")
338 | if compileMatch(re1) == false {
339 | t.Error("ERROR: compile match")
340 | }
341 | if compileMatchNot(re2) == true {
342 | t.Error("ERROR: compile match not")
343 | }
344 | }
345 |
346 | func BenchmarkCompileMatch(b *testing.B) {
347 | re := regexp.MustCompile("em Ip")
348 | for n := 0; n < b.N; n++ {
349 | compileMatch(re)
350 | }
351 | }
352 |
353 | func BenchmarkCompileMatchNot(b *testing.B) {
354 | re := regexp.MustCompile("Dolor")
355 | for n := 0; n < b.N; n++ {
356 | compileMatchNot(re)
357 | }
358 | }
359 |
360 | // regexp.MatchString
361 | func match() bool {
362 | matched, _ := regexp.MatchString("em Ip", "Lorem Ipsum")
363 | return matched
364 | }
365 |
366 | func matchNot() bool {
367 | matched, _ := regexp.MatchString("Dolor", "Lorem Ipsum")
368 | return matched
369 | }
370 |
371 | func TestMatch(t *testing.T) {
372 | if match() == false {
373 | t.Error("ERROR: match")
374 | }
375 | if matchNot() == true {
376 | t.Error("ERROR: match not")
377 | }
378 | }
379 |
380 | func BenchmarkMatch(b *testing.B) {
381 | for n := 0; n < b.N; n++ {
382 | match()
383 | }
384 | }
385 |
386 | func BenchmarkMatchNot(b *testing.B) {
387 | for n := 0; n < b.N; n++ {
388 | matchNot()
389 | }
390 | }
391 | ```
392 |
393 | ```
394 | $ go test -bench . -benchmem
395 | goos: windows
396 | goarch: amd64
397 | pkg: github.com/SimonWaldherr/golang-benchmarks/contains
398 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
399 | BenchmarkContains-8 100000000 10.06 ns/op 0 B/op 0 allocs/op
400 | BenchmarkContainsNot-8 124361080 9.478 ns/op 0 B/op 0 allocs/op
401 | BenchmarkContainsBytes-8 111913422 10.52 ns/op 0 B/op 0 allocs/op
402 | BenchmarkContainsBytesNot-8 113837294 11.18 ns/op 0 B/op 0 allocs/op
403 | BenchmarkCompileMatch-8 6798404 166.2 ns/op 0 B/op 0 allocs/op
404 | BenchmarkCompileMatchNot-8 18461878 65.58 ns/op 0 B/op 0 allocs/op
405 | BenchmarkMatch-8 387135 2618 ns/op 1367 B/op 17 allocs/op
406 | BenchmarkMatchNot-8 480118 2550 ns/op 1368 B/op 17 allocs/op
407 | PASS
408 | ok github.com/SimonWaldherr/golang-benchmarks/contains 12.542s
409 | ```
410 |
411 | ### foreach
412 |
413 | ```go
414 | // Package foreach benchmarks ranging over slices and maps.
415 | package foreach
416 |
417 | import (
418 | "testing"
419 | )
420 |
421 | var amap map[int]string
422 | var aslice []string
423 |
424 | func init() {
425 | amap = map[int]string{
426 | 0: "lorem",
427 | 1: "ipsum",
428 | 2: "dolor",
429 | 3: "sit",
430 | 4: "amet",
431 | }
432 |
433 | aslice = []string{
434 | "lorem",
435 | "ipsum",
436 | "dolor",
437 | "sit",
438 | "amet",
439 | }
440 | }
441 |
442 | func forMap() {
443 | for i := 0; i < len(amap); i++ {
444 | _ = amap[i]
445 | }
446 | }
447 |
448 | func rangeMap() {
449 | for _, v := range amap {
450 | _ = v
451 | }
452 | }
453 |
454 | func rangeSlice() {
455 | for _, v := range aslice {
456 | _ = v
457 | }
458 | }
459 |
460 | func rangeSliceKey() {
461 | for k := range aslice {
462 | _ = aslice[k]
463 | }
464 | }
465 |
466 | func BenchmarkForMap(b *testing.B) {
467 | for n := 0; n < b.N; n++ {
468 | forMap()
469 | }
470 | }
471 |
472 | func BenchmarkRangeMap(b *testing.B) {
473 | for n := 0; n < b.N; n++ {
474 | rangeMap()
475 | }
476 | }
477 |
478 | func BenchmarkRangeSlice(b *testing.B) {
479 | for n := 0; n < b.N; n++ {
480 | rangeSlice()
481 | }
482 | }
483 |
484 | func BenchmarkRangeSliceKey(b *testing.B) {
485 | for n := 0; n < b.N; n++ {
486 | rangeSliceKey()
487 | }
488 | }
489 | ```
490 |
491 | ```
492 | $ go test -bench . -benchmem
493 | goos: windows
494 | goarch: amd64
495 | pkg: github.com/SimonWaldherr/golang-benchmarks/foreach
496 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
497 | BenchmarkForMap-8 41428023 30.76 ns/op 0 B/op 0 allocs/op
498 | BenchmarkRangeMap-8 13371783 95.48 ns/op 0 B/op 0 allocs/op
499 | BenchmarkRangeSlice-8 312989208 3.537 ns/op 0 B/op 0 allocs/op
500 | BenchmarkRangeSliceKey-8 307276144 4.235 ns/op 0 B/op 0 allocs/op
501 | PASS
502 | ok github.com/SimonWaldherr/golang-benchmarks/foreach 5.941s
503 | ```
504 |
505 | ### hash
506 |
507 | ```go
508 | // Package hash benchmarks various hashing algorithms.
509 | // Especially with hashing algorithms, faster is not always better.
510 | // One should always decide on the basis of the respective requirements.
511 | package hash
512 |
513 | import (
514 | "crypto/md5"
515 | "crypto/sha1"
516 | "crypto/sha256"
517 | "crypto/sha512"
518 | "hash"
519 | "hash/adler32"
520 | "hash/crc32"
521 | "hash/fnv"
522 | "math/rand"
523 | "testing"
524 |
525 | "github.com/jzelinskie/whirlpool"
526 | "github.com/reusee/mmh3"
527 | "github.com/zeebo/blake3"
528 | "golang.org/x/crypto/blake2b"
529 | "golang.org/x/crypto/sha3"
530 | )
531 |
532 | func benchmarkHashAlgo(b *testing.B, h hash.Hash) {
533 | data := make([]byte, 2048)
534 | rand.Read(data)
535 |
536 | b.ResetTimer()
537 | for n := 0; n < b.N; n++ {
538 | h.Write(data)
539 | h.Sum(nil)
540 | }
541 | }
542 |
543 | func BenchmarkAdler32(b *testing.B) {
544 | benchmarkHashAlgo(b, adler32.New())
545 | }
546 |
547 | func BenchmarkBlake2b256(b *testing.B) {
548 | h, err := blake2b.New256(nil)
549 | if err != nil {
550 | b.Fatal(err)
551 | }
552 | benchmarkHashAlgo(b, h)
553 | }
554 |
555 | func BenchmarkBlake2b512(b *testing.B) {
556 | h, err := blake2b.New512(nil)
557 | if err != nil {
558 | b.Fatal(err)
559 | }
560 | benchmarkHashAlgo(b, h)
561 | }
562 |
563 | func BenchmarkBlake3256(b *testing.B) {
564 | benchmarkHashAlgo(b, blake3.New())
565 | }
566 |
567 | func BenchmarkMMH3(b *testing.B) {
568 | benchmarkHashAlgo(b, mmh3.New128())
569 | }
570 |
571 | func BenchmarkCRC32(b *testing.B) {
572 | benchmarkHashAlgo(b, crc32.NewIEEE())
573 | }
574 |
575 | func BenchmarkFnv128(b *testing.B) {
576 | benchmarkHashAlgo(b, fnv.New128())
577 | }
578 |
579 | func BenchmarkMD5(b *testing.B) {
580 | benchmarkHashAlgo(b, md5.New())
581 | }
582 |
583 | func BenchmarkSHA1(b *testing.B) {
584 | benchmarkHashAlgo(b, sha1.New())
585 | }
586 |
587 | func BenchmarkSHA256(b *testing.B) {
588 | benchmarkHashAlgo(b, sha256.New())
589 | }
590 |
591 | func BenchmarkSHA512(b *testing.B) {
592 | benchmarkHashAlgo(b, sha512.New())
593 | }
594 |
595 | func BenchmarkSHA3256(b *testing.B) {
596 | benchmarkHashAlgo(b, sha3.New256())
597 | }
598 |
599 | func BenchmarkSHA3512(b *testing.B) {
600 | benchmarkHashAlgo(b, sha3.New512())
601 | }
602 |
603 | func BenchmarkWhirlpool(b *testing.B) {
604 | benchmarkHashAlgo(b, whirlpool.New())
605 | }
606 | ```
607 |
608 | ```
609 | $ go test -bench . -benchmem
610 | goos: windows
611 | goarch: amd64
612 | pkg: github.com/SimonWaldherr/golang-benchmarks/hash
613 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
614 | BenchmarkAdler32-8 1286498 863.3 ns/op 8 B/op 1 allocs/op
615 | BenchmarkBlake2b256-8 532658 2478 ns/op 32 B/op 1 allocs/op
616 | BenchmarkBlake2b512-8 480070 2402 ns/op 64 B/op 1 allocs/op
617 | BenchmarkBlake3256-8 255342 4927 ns/op 64 B/op 2 allocs/op
618 | BenchmarkMMH3-8 3088189 428.1 ns/op 16 B/op 1 allocs/op
619 | BenchmarkCRC32-8 5754514 213.8 ns/op 8 B/op 1 allocs/op
620 | BenchmarkFnv128-8 144576 7388 ns/op 16 B/op 1 allocs/op
621 | BenchmarkMD5-8 307717 3447 ns/op 16 B/op 1 allocs/op
622 | BenchmarkSHA1-8 464949 2606 ns/op 24 B/op 1 allocs/op
623 | BenchmarkSHA256-8 166674 6193 ns/op 32 B/op 1 allocs/op
624 | BenchmarkSHA512-8 206931 6695 ns/op 64 B/op 1 allocs/op
625 | BenchmarkSHA3256-8 139539 7776 ns/op 512 B/op 3 allocs/op
626 | BenchmarkSHA3512-8 69549 14832 ns/op 576 B/op 3 allocs/op
627 | BenchmarkWhirlpool-8 17632 65506 ns/op 64 B/op 1 allocs/op
628 | PASS
629 | ok github.com/SimonWaldherr/golang-benchmarks/hash 20.398s
630 | ```
631 |
632 | ### index
633 |
634 | ```go
635 | // Package index benchmarks access on maps with various data types as keys.
636 | package index
637 |
638 | import (
639 | "math/rand"
640 | "strconv"
641 | "testing"
642 | )
643 |
644 | var NumItems int = 1000000
645 |
646 | var ms map[string]string
647 | var ks []string
648 |
649 | var mi map[int]string
650 | var ki []int
651 |
652 | func initMapStringIndex() {
653 | ms = make(map[string]string)
654 | ks = make([]string, 0)
655 |
656 | for i := 0; i < NumItems; i++ {
657 | key := strconv.Itoa(rand.Intn(NumItems))
658 | ms[key] = "value" + strconv.Itoa(i)
659 | ks = append(ks, key)
660 | }
661 | }
662 |
663 | func initMapIntIndex() {
664 | mi = make(map[int]string)
665 | ki = make([]int, 0)
666 |
667 | for i := 0; i < NumItems; i++ {
668 | key := rand.Intn(NumItems)
669 | mi[key] = "value" + strconv.Itoa(i)
670 | ki = append(ki, key)
671 | }
672 | }
673 |
674 | func init() {
675 | initMapStringIndex()
676 | initMapIntIndex()
677 | }
678 |
679 | func BenchmarkMapStringKeys(b *testing.B) {
680 | i := 0
681 |
682 | for n := 0; n < b.N; n++ {
683 | if _, ok := ms[ks[i]]; ok {
684 | }
685 |
686 | i++
687 | if i >= NumItems {
688 | i = 0
689 | }
690 | }
691 | }
692 |
693 | func BenchmarkMapIntKeys(b *testing.B) {
694 | i := 0
695 |
696 | for n := 0; n < b.N; n++ {
697 | if _, ok := mi[ki[i]]; ok {
698 | }
699 |
700 | i++
701 | if i >= NumItems {
702 | i = 0
703 | }
704 | }
705 | }
706 | ```
707 |
708 | ```
709 | $ go test -bench . -benchmem
710 | goos: windows
711 | goarch: amd64
712 | pkg: github.com/SimonWaldherr/golang-benchmarks/index
713 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
714 | BenchmarkMapStringKeys-8 8609346 142.5 ns/op 0 B/op 0 allocs/op
715 | BenchmarkMapIntKeys-8 9610347 124.4 ns/op 0 B/op 0 allocs/op
716 | PASS
717 | ok github.com/SimonWaldherr/golang-benchmarks/index 4.545s
718 | ```
719 |
720 | ### json
721 |
722 | ```go
723 | package json
724 |
725 | import (
726 | "encoding/json"
727 | "math"
728 | "math/big"
729 | "testing"
730 | "time"
731 | )
732 |
733 | type Data struct {
734 | String string
735 | Time time.Time
736 | Int int
737 | Int8 int8
738 | Int16 int16
739 | Int32 int32
740 | Int64 int64
741 | Boolean bool
742 | Float32 float32
743 | Float64 float64
744 | BigInt big.Int
745 | BigFloat big.Float
746 | }
747 |
748 | func BenchmarkJsonMarshal(b *testing.B) {
749 | for n := 0; n < b.N; n++ {
750 | var d = Data{
751 | String: "",
752 | Time: time.Now(),
753 | Int: math.MaxInt32,
754 | Int8: math.MaxInt8,
755 | Int16: math.MaxInt16,
756 | Int32: math.MaxInt32,
757 | Int64: math.MaxInt64,
758 | Boolean: false,
759 | Float32: math.MaxFloat32,
760 | Float64: math.MaxFloat64,
761 | BigInt: *big.NewInt(math.MaxInt64),
762 | BigFloat: *big.NewFloat(math.MaxFloat64),
763 | }
764 |
765 | _, err := json.Marshal(d)
766 | if err != nil {
767 | b.Error(err)
768 | b.Fail()
769 | return
770 | }
771 | }
772 | }
773 |
774 | func BenchmarkJsonUnmarshal(b *testing.B) {
775 | str := `
776 | {
777 | "String": "",
778 | "Time": "2019-10-30T16:41:29.853426+07:00",
779 | "Int": 2147483647,
780 | "Int8": 127,
781 | "Int16": 32767,
782 | "Int32": 2147483647,
783 | "Int64": 9223372036854775807,
784 | "Boolean": false,
785 | "Float32": 3.4028235e+38,
786 | "Float64": 1.7976931348623157e+308,
787 | "BigInt": 9999999999999999999,
788 | "BigFloat": "2.7976931348623157e+308"
789 | }
790 | `
791 |
792 | for n := 0; n < b.N; n++ {
793 | var d Data
794 | err := json.Unmarshal([]byte(str), &d)
795 | if err != nil {
796 | b.Error(err)
797 | b.Fail()
798 | return
799 | }
800 | }
801 | }
802 | ```
803 |
804 | ```
805 | $ go test -bench . -benchmem
806 | goos: windows
807 | goarch: amd64
808 | pkg: github.com/SimonWaldherr/golang-benchmarks/json
809 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
810 | BenchmarkJsonMarshal-8 411422 2951 ns/op 480 B/op 5 allocs/op
811 | BenchmarkJsonUnmarshal-8 96012 13334 ns/op 2120 B/op 38 allocs/op
812 | PASS
813 | ok github.com/SimonWaldherr/golang-benchmarks/json 3.390s
814 | ```
815 |
816 | ### math
817 |
818 | ```go
819 | // Package math compares the speed of various mathematical operations.
820 | package math
821 |
822 | import (
823 | "sync"
824 | "sync/atomic"
825 | "testing"
826 | )
827 |
828 | func BenchmarkMathInt8(b *testing.B) {
829 | var intVal int8
830 | for n := 0; n < b.N; n++ {
831 | intVal = intVal + 2
832 | }
833 | }
834 |
835 | func BenchmarkMathInt32(b *testing.B) {
836 | var intVal int32
837 | for n := 0; n < b.N; n++ {
838 | intVal = intVal + 2
839 | }
840 | }
841 |
842 | func BenchmarkMathInt64(b *testing.B) {
843 | var intVal int64
844 | for n := 0; n < b.N; n++ {
845 | intVal = intVal + 2
846 | }
847 | }
848 |
849 | func BenchmarkMathAtomicInt32(b *testing.B) {
850 | var intVal int32
851 | for n := 0; n < b.N; n++ {
852 | atomic.AddInt32(&intVal, 2)
853 | }
854 | }
855 |
856 | func BenchmarkMathAtomicInt64(b *testing.B) {
857 | var intVal int64
858 | for n := 0; n < b.N; n++ {
859 | atomic.AddInt64(&intVal, 2)
860 | }
861 | }
862 |
863 | type IntMutex struct {
864 | v int64
865 | mux sync.Mutex
866 | }
867 |
868 | func BenchmarkMathMutexInt(b *testing.B) {
869 | var m IntMutex
870 | for n := 0; n < b.N; n++ {
871 | m.mux.Lock()
872 | m.v = m.v + 2
873 | m.mux.Unlock()
874 | }
875 | }
876 |
877 | func BenchmarkMathFloat32(b *testing.B) {
878 | var floatVal float32
879 | for n := 0; n < b.N; n++ {
880 | floatVal = floatVal + 2
881 | }
882 | }
883 |
884 | func BenchmarkMathFloat64(b *testing.B) {
885 | var floatVal float64
886 | for n := 0; n < b.N; n++ {
887 | floatVal = floatVal + 2
888 | }
889 | }
890 | ```
891 |
892 | ```
893 | $ go test -bench . -benchmem
894 | goos: windows
895 | goarch: amd64
896 | pkg: github.com/SimonWaldherr/golang-benchmarks/math
897 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
898 | BenchmarkMathInt8-8 1000000000 0.3302 ns/op 0 B/op 0 allocs/op
899 | BenchmarkMathInt32-8 1000000000 0.3290 ns/op 0 B/op 0 allocs/op
900 | BenchmarkMathInt64-8 1000000000 0.3397 ns/op 0 B/op 0 allocs/op
901 | BenchmarkMathAtomicInt32-8 131834130 9.002 ns/op 0 B/op 0 allocs/op
902 | BenchmarkMathAtomicInt64-8 138517504 9.437 ns/op 0 B/op 0 allocs/op
903 | BenchmarkMathMutexInt-8 63160886 18.73 ns/op 0 B/op 0 allocs/op
904 | BenchmarkMathFloat32-8 1000000000 0.3210 ns/op 0 B/op 0 allocs/op
905 | BenchmarkMathFloat64-8 1000000000 0.3560 ns/op 0 B/op 0 allocs/op
906 | PASS
907 | ok github.com/SimonWaldherr/golang-benchmarks/math 7.463s
908 | ```
909 |
910 | ### parse
911 |
912 | ```go
913 | // Package parse benchmarks parsing.
914 | package parse
915 |
916 | import (
917 | "strconv"
918 | "testing"
919 | )
920 |
921 | func BenchmarkParseBool(b *testing.B) {
922 | for n := 0; n < b.N; n++ {
923 | _, err := strconv.ParseBool("true")
924 | if err != nil {
925 | panic(err)
926 | }
927 | }
928 | }
929 |
930 | func BenchmarkParseInt(b *testing.B) {
931 | for n := 0; n < b.N; n++ {
932 | _, err := strconv.ParseInt("1337", 10, 64)
933 | if err != nil {
934 | panic(err)
935 | }
936 | }
937 | }
938 |
939 | func BenchmarkParseFloat(b *testing.B) {
940 | for n := 0; n < b.N; n++ {
941 | _, err := strconv.ParseFloat("3.141592653589793238462643383", 64)
942 | if err != nil {
943 | panic(err)
944 | }
945 | }
946 | }
947 | ```
948 |
949 | ```
950 | $ go test -bench . -benchmem
951 | goos: windows
952 | goarch: amd64
953 | pkg: github.com/SimonWaldherr/golang-benchmarks/parse
954 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
955 | BenchmarkParseBool-8 1000000000 0.6245 ns/op 0 B/op 0 allocs/op
956 | BenchmarkParseInt-8 54551900 18.86 ns/op 0 B/op 0 allocs/op
957 | BenchmarkParseFloat-8 10073257 122.2 ns/op 0 B/op 0 allocs/op
958 | PASS
959 | ok github.com/SimonWaldherr/golang-benchmarks/parse 3.160s
960 | ```
961 |
962 | ### random
963 |
964 | ```go
965 | // Package random compares math/rand with crypto/rand.
966 | // math/rand is much faster than crypto/rand, but it
967 | // returns only a pseudo random number.
968 | package random
969 |
970 | import (
971 | crand "crypto/rand"
972 | "encoding/base64"
973 | "math/big"
974 | mrand "math/rand"
975 | "testing"
976 | )
977 |
978 | func BenchmarkMathRand(b *testing.B) {
979 | for n := 0; n < b.N; n++ {
980 | mrand.Int63n(0xFFFF)
981 | }
982 | }
983 |
984 | func BenchmarkCryptoRand(b *testing.B) {
985 | for n := 0; n < b.N; n++ {
986 | _, err := crand.Int(crand.Reader, big.NewInt(0xFFFF))
987 | if err != nil {
988 | panic(err)
989 | }
990 | }
991 | }
992 |
993 | func BenchmarkCryptoRandString(b *testing.B) {
994 | for n := 0; n < b.N; n++ {
995 | _, err := GenerateRandomString(32)
996 | if err != nil {
997 | panic(err)
998 | }
999 | }
1000 | }
1001 |
1002 | func GenerateRandomBytes(n int) ([]byte, error) {
1003 | b := make([]byte, n)
1004 | _, err := mrand.Read(b)
1005 | if err != nil {
1006 | return nil, err
1007 | }
1008 |
1009 | return b, nil
1010 | }
1011 |
1012 | func GenerateRandomString(s int) (string, error) {
1013 | b, err := GenerateRandomBytes(s)
1014 | return base64.URLEncoding.EncodeToString(b), err
1015 | }
1016 | ```
1017 |
1018 | ```
1019 | $ go test -bench . -benchmem
1020 | goos: windows
1021 | goarch: amd64
1022 | pkg: github.com/SimonWaldherr/golang-benchmarks/random
1023 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
1024 | BenchmarkMathRand-8 51076435 22.53 ns/op 0 B/op 0 allocs/op
1025 | BenchmarkCryptoRand-8 3178332 374.1 ns/op 56 B/op 4 allocs/op
1026 | BenchmarkCryptoRandString-8 4236312 288.6 ns/op 128 B/op 3 allocs/op
1027 | PASS
1028 | ok github.com/SimonWaldherr/golang-benchmarks/random 4.331s
1029 | ```
1030 |
1031 | ### regexp
1032 |
1033 | ```go
1034 | // Package regexp benchmarks the performance of a pre-compiled regexp match
1035 | // a non-pre-compiled match and JIT-cached-compilation via golibs: https://simonwaldherr.de/go/golibs
1036 | package regexp
1037 |
1038 | import (
1039 | "regexp"
1040 | "testing"
1041 |
1042 | "simonwaldherr.de/go/golibs/regex"
1043 | )
1044 |
1045 | var regexpStr string = `^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,9}$`
1046 |
1047 | func BenchmarkMatchString(b *testing.B) {
1048 | for n := 0; n < b.N; n++ {
1049 | _, err := regexp.MatchString(regexpStr, "john.doe@example.tld")
1050 | if err != nil {
1051 | panic(err)
1052 | }
1053 | }
1054 | }
1055 |
1056 | func BenchmarkMatchStringCompiled(b *testing.B) {
1057 | r, err := regexp.Compile(regexpStr)
1058 | if err != nil {
1059 | panic(err)
1060 | }
1061 |
1062 | b.ResetTimer()
1063 | for n := 0; n < b.N; n++ {
1064 | r.MatchString("john.doe@example.tld")
1065 | }
1066 | }
1067 |
1068 | func BenchmarkMatchStringGolibs(b *testing.B) {
1069 | for n := 0; n < b.N; n++ {
1070 | _, err := regex.MatchString("john.doe@example.tld", regexpStr)
1071 | if err != nil {
1072 | panic(err)
1073 | }
1074 | }
1075 | }
1076 | ```
1077 |
1078 | ```
1079 | $ go test -bench . -benchmem
1080 | goos: windows
1081 | goarch: amd64
1082 | pkg: github.com/SimonWaldherr/golang-benchmarks/regexp
1083 | cpu: AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
1084 | BenchmarkMatchString-8 71940 15527 ns/op 9969 B/op 86 allocs/op
1085 | BenchmarkMatchStringCompiled-8 1611652 803.6 ns/op 0 B/op 0 allocs/op
1086 | BenchmarkMatchStringGolibs-8 1000000 1011 ns/op 0 B/op 0 allocs/op
1087 | PASS
1088 | ok github.com/SimonWaldherr/golang-benchmarks/regexp 4.432s
1089 | ```
1090 |
1091 |
--------------------------------------------------------------------------------
/_gophers_race.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimonWaldherr/golang-benchmarks/96d27bb724d05ee920b4e4cf1be9493346673258/_gophers_race.jpg
--------------------------------------------------------------------------------
/base64/base64_test.go:
--------------------------------------------------------------------------------
1 | // Package base64 benchmarks some base64 functions.
2 | // On all tested systems it's faster to decode a
3 | // base64 encoded string instead of a check via regex.
4 | package base64
5 |
6 | import (
7 | "encoding/base64"
8 | "regexp"
9 | "testing"
10 | )
11 |
12 | func base64decode(s string) bool {
13 | _, err := base64.StdEncoding.DecodeString(s)
14 | return err == nil
15 | }
16 |
17 | func base64regex(s string) bool {
18 | matched, _ := regexp.MatchString(`^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$`, s)
19 | return matched
20 | }
21 |
22 | func BenchmarkBase64decode(b *testing.B) {
23 | isNotBase64 := `Invalid string`
24 | isBase64 := `VmFsaWQgc3RyaW5nCg==`
25 |
26 | for n := 0; n < b.N; n++ {
27 | base64decode(isNotBase64)
28 | base64decode(isBase64)
29 | }
30 | }
31 |
32 | func BenchmarkBase64regex(b *testing.B) {
33 | isNotBase64 := `Invalid string`
34 | isBase64 := `VmFsaWQgc3RyaW5nCg==`
35 |
36 | for n := 0; n < b.N; n++ {
37 | base64regex(isNotBase64)
38 | base64regex(isBase64)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/between/between_test.go:
--------------------------------------------------------------------------------
1 | // Package between compares the performance of checking
2 | // if a number is between two other numbers via regex
3 | // and by parsing the number as integers.
4 | package between
5 |
6 | import (
7 | "regexp"
8 | "simonwaldherr.de/go/golibs/as"
9 | "simonwaldherr.de/go/ranger"
10 | "testing"
11 | )
12 |
13 | func BenchmarkNumberRegEx(b *testing.B) {
14 | re := ranger.Compile(89, 1001)
15 | re = "^(" + re + ")$"
16 | b.ResetTimer()
17 |
18 | for n := 0; n < b.N; n++ {
19 | matched, err := regexp.MatchString(re, "404")
20 | if !matched || err != nil {
21 | b.Log("Error in Benchmark")
22 | }
23 |
24 | matched, err = regexp.MatchString(re, "2000")
25 | if matched || err != nil {
26 | b.Log("Error in Benchmark")
27 | }
28 | }
29 | }
30 |
31 | func BenchmarkFulltextRegEx(b *testing.B) {
32 | re := ranger.Compile(89, 1001)
33 | re = " (" + re + ") "
34 | b.ResetTimer()
35 |
36 | for n := 0; n < b.N; n++ {
37 | matched, err := regexp.MatchString(re, "lorem ipsum 404 dolor sit")
38 | if !matched || err != nil {
39 | b.Log("Error in Benchmark")
40 | }
41 |
42 | matched, err = regexp.MatchString(re, "lorem ipsum 2000 dolor sit")
43 | if matched || err != nil {
44 | b.Log("Error in Benchmark")
45 | }
46 | }
47 | }
48 |
49 | func BenchmarkNumberParse(b *testing.B) {
50 | for n := 0; n < b.N; n++ {
51 | i1 := as.Int("404")
52 | i2 := as.Int("2000")
53 |
54 | if i1 < 89 || i1 > 1001 {
55 | b.Log("Error in Benchmark")
56 | }
57 |
58 | if !(i2 < 89 || i2 > 1001) {
59 | b.Log("Error in Benchmark")
60 | }
61 | }
62 | }
63 |
64 | func BenchmarkFulltextParse(b *testing.B) {
65 | re := regexp.MustCompile("[0-9]+")
66 | b.ResetTimer()
67 |
68 | for n := 0; n < b.N; n++ {
69 | i1 := as.Int(re.FindString("lorem ipsum 404 dolor sit"))
70 | i2 := as.Int(re.FindString("lorem ipsum 2000 dolor sit"))
71 |
72 | if i1 < 89 || i1 > 1001 {
73 | b.Log("Error in Benchmark")
74 | }
75 |
76 | if !(i2 < 89 || i2 > 1001) {
77 | b.Log("Error in Benchmark")
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/caseinsensitivecompare/caseinsensitivecompare_test.go:
--------------------------------------------------------------------------------
1 | package trim
2 |
3 | import (
4 | "strings"
5 | "testing"
6 | )
7 |
8 | func BenchmarkEqualFold(b *testing.B) {
9 | for n := 0; n < b.N; n++ {
10 | _ = strings.EqualFold("abc", "ABC")
11 | _ = strings.EqualFold("ABC", "ABC")
12 | _ = strings.EqualFold("1aBcD", "1AbCd")
13 | }
14 | }
15 |
16 | func BenchmarkToUpper(b *testing.B) {
17 | for n := 0; n < b.N; n++ {
18 | _ = strings.ToUpper("abc") == strings.ToUpper("ABC")
19 | _ = strings.ToUpper("ABC") == strings.ToUpper("ABC")
20 | _ = strings.ToUpper("1aBcD") == strings.ToUpper("1AbCd")
21 | }
22 | }
23 |
24 | func BenchmarkToLower(b *testing.B) {
25 | for n := 0; n < b.N; n++ {
26 | _ = strings.ToLower("abc") == strings.ToLower("ABC")
27 | _ = strings.ToLower("ABC") == strings.ToLower("ABC")
28 | _ = strings.ToLower("1aBcD") == strings.ToLower("1AbCd")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/concat/concat_test.go:
--------------------------------------------------------------------------------
1 | // Package concat benchmarks the performance of
2 | // various string concatenation methods.
3 | // Instead of just concatenating a string to another string
4 | // it is also possible (and much faster) to use
5 | // a bytes buffer.
6 | package concat
7 |
8 | import (
9 | "bytes"
10 | "strings"
11 | "testing"
12 | )
13 |
14 | func BenchmarkConcatString(b *testing.B) {
15 | var str string
16 | for n := 0; n < b.N; n++ {
17 | str += "x"
18 | }
19 | }
20 |
21 | func BenchmarkConcatBuffer(b *testing.B) {
22 | var buffer bytes.Buffer
23 | for n := 0; n < b.N; n++ {
24 | buffer.WriteString("x")
25 |
26 | }
27 | }
28 |
29 | func BenchmarkConcatBuilder(b *testing.B) {
30 | var builder strings.Builder
31 | for n := 0; n < b.N; n++ {
32 | builder.WriteString("x")
33 | }
34 | }
35 |
36 | func BenchmarkConcat(b *testing.B) {
37 | b.Run("String", func(b *testing.B) {
38 | var str string
39 | for n := 0; n < b.N; n++ {
40 | str += "x"
41 | }
42 | })
43 |
44 | b.Run("Buffer", func(b *testing.B) {
45 | var buffer bytes.Buffer
46 | for n := 0; n < b.N; n++ {
47 | buffer.WriteString("x")
48 | }
49 | })
50 |
51 | b.Run("Builder", func(b *testing.B) {
52 | var builder strings.Builder
53 | for n := 0; n < b.N; n++ {
54 | builder.WriteString("x")
55 | }
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/contains/contains_test.go:
--------------------------------------------------------------------------------
1 | // Package contains tests various ways of checking
2 | // if a string is contained in another string.
3 | package contains
4 |
5 | import (
6 | "bytes"
7 | "regexp"
8 | "strings"
9 | "testing"
10 | )
11 |
12 | // strings.Contains
13 | func contains() bool {
14 | return strings.Contains("Lorem Ipsum", "em Ip")
15 | }
16 |
17 | func containsNot() bool {
18 | return strings.Contains("Lorem Ipsum", "Dolor")
19 | }
20 |
21 | func TestContains(t *testing.T) {
22 | if contains() == false {
23 | t.Error("ERROR: contains")
24 | }
25 | if containsNot() == true {
26 | t.Error("ERROR: contains not")
27 | }
28 | }
29 |
30 | func BenchmarkContains(b *testing.B) {
31 | for n := 0; n < b.N; n++ {
32 | contains()
33 | }
34 | }
35 |
36 | func BenchmarkContainsNot(b *testing.B) {
37 | for n := 0; n < b.N; n++ {
38 | containsNot()
39 | }
40 | }
41 |
42 | // bytes.Contains
43 | func containsBytes() bool {
44 | return bytes.Contains([]byte("Lorem Ipsum"), []byte("em Ip"))
45 | }
46 |
47 | func containsBytesNot() bool {
48 | return bytes.Contains([]byte("Lorem Ipsum"), []byte("Dolor"))
49 | }
50 |
51 | func TestContainsBytes(t *testing.T) {
52 | if containsBytes() == false {
53 | t.Error("ERROR: bytes contains")
54 | }
55 | if containsBytesNot() == true {
56 | t.Error("ERROR: bytes contains not")
57 | }
58 | }
59 |
60 | func BenchmarkContainsBytes(b *testing.B) {
61 | for n := 0; n < b.N; n++ {
62 | containsBytes()
63 | }
64 | }
65 |
66 | func BenchmarkContainsBytesNot(b *testing.B) {
67 | for n := 0; n < b.N; n++ {
68 | containsBytesNot()
69 | }
70 | }
71 |
72 | // regexp.MustCompile + regexp.MatchString
73 | func compileMatch(re *regexp.Regexp) bool {
74 | matched := re.MatchString("Lorem Ipsum")
75 | return matched
76 | }
77 |
78 | func compileMatchNot(re *regexp.Regexp) bool {
79 | matched := re.MatchString("Lorem Ipsum")
80 | return matched
81 | }
82 |
83 | func TestCompileMatch(t *testing.T) {
84 | re1 := regexp.MustCompile("em Ip")
85 | re2 := regexp.MustCompile("Dolor")
86 | if compileMatch(re1) == false {
87 | t.Error("ERROR: compile match")
88 | }
89 | if compileMatchNot(re2) == true {
90 | t.Error("ERROR: compile match not")
91 | }
92 | }
93 |
94 | func BenchmarkCompileMatch(b *testing.B) {
95 | re := regexp.MustCompile("em Ip")
96 | for n := 0; n < b.N; n++ {
97 | compileMatch(re)
98 | }
99 | }
100 |
101 | func BenchmarkCompileMatchNot(b *testing.B) {
102 | re := regexp.MustCompile("Dolor")
103 | for n := 0; n < b.N; n++ {
104 | compileMatchNot(re)
105 | }
106 | }
107 |
108 | // regexp.MatchString
109 | func match() bool {
110 | matched, _ := regexp.MatchString("em Ip", "Lorem Ipsum")
111 | return matched
112 | }
113 |
114 | func matchNot() bool {
115 | matched, _ := regexp.MatchString("Dolor", "Lorem Ipsum")
116 | return matched
117 | }
118 |
119 | func TestMatch(t *testing.T) {
120 | if match() == false {
121 | t.Error("ERROR: match")
122 | }
123 | if matchNot() == true {
124 | t.Error("ERROR: match not")
125 | }
126 | }
127 |
128 | func BenchmarkMatch(b *testing.B) {
129 | for n := 0; n < b.N; n++ {
130 | match()
131 | }
132 | }
133 |
134 | func BenchmarkMatchNot(b *testing.B) {
135 | for n := 0; n < b.N; n++ {
136 | matchNot()
137 | }
138 | }
139 |
140 | // BenchmarkContainsMethods benchmarks different methods to check substring presence.
141 | func BenchmarkContainsMethods(b *testing.B) {
142 | b.Run("Strings.Contains", func(b *testing.B) {
143 | str := "Lorem Ipsum"
144 | substr := "em Ip"
145 | for n := 0; n < b.N; n++ {
146 | _ = strings.Contains(str, substr)
147 | _ = strings.Contains(str, "Dolor")
148 | }
149 | })
150 |
151 | b.Run("Bytes.Contains", func(b *testing.B) {
152 | str := []byte("Lorem Ipsum")
153 | substr := []byte("em Ip")
154 | for n := 0; n < b.N; n++ {
155 | _ = bytes.Contains(str, substr)
156 | _ = bytes.Contains(str, []byte("Dolor"))
157 | }
158 | })
159 |
160 | b.Run("RegexMatchString", func(b *testing.B) {
161 | re := regexp.MustCompile(`em Ip`)
162 | for n := 0; n < b.N; n++ {
163 | _ = re.MatchString("Lorem Ipsum")
164 | _ = re.MatchString("Dolor")
165 | }
166 | })
167 |
168 | b.Run("RegexMatch", func(b *testing.B) {
169 | for n := 0; n < b.N; n++ {
170 | _, _ = regexp.MatchString(`em Ip`, "Lorem Ipsum")
171 | _, _ = regexp.MatchString(`em Ip`, "Dolor")
172 | }
173 | })
174 | }
175 |
--------------------------------------------------------------------------------
/embed/embed_test.go:
--------------------------------------------------------------------------------
1 | package embed
2 |
3 | import (
4 | _ "embed"
5 | "io/ioutil"
6 | "os"
7 | "testing"
8 | )
9 |
10 | //go:embed example.txt
11 | var embeddedFile []byte
12 |
13 | func BenchmarkEmbed(b *testing.B) {
14 | for i := 0; i < b.N; i++ {
15 | // Access the embedded file
16 | _ = embeddedFile
17 | }
18 | }
19 |
20 | func BenchmarkReadFile(b *testing.B) {
21 | for i := 0; i < b.N; i++ {
22 | // Read the file from disk
23 | data, err := os.ReadFile("example.txt")
24 | if err != nil {
25 | b.Fatalf("failed to read file: %v", err)
26 | }
27 | _ = data
28 | }
29 | }
30 |
31 | func BenchmarkIoutilReadFile(b *testing.B) {
32 | for i := 0; i < b.N; i++ {
33 | // Read the file using ioutil
34 | data, err := ioutil.ReadFile("example.txt")
35 | if err != nil {
36 | b.Fatalf("failed to read file: %v", err)
37 | }
38 | _ = data
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/embed/example.txt:
--------------------------------------------------------------------------------
1 | Lorem Ipsum
2 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
--------------------------------------------------------------------------------
/floodfill/floodfill_test.go:
--------------------------------------------------------------------------------
1 | // Package floodfill benchmarks various flood fill implementations.
2 | package main
3 |
4 | import (
5 | "testing"
6 | )
7 |
8 | // Rekursive Implementierung
9 | func floodFillRecursive(image [][]int, sr int, sc int, newColor int) [][]int {
10 | oldColor := image[sr][sc]
11 | if oldColor == newColor {
12 | return image
13 | }
14 | floodFillRecurse(image, sr, sc, oldColor, newColor)
15 | return image
16 | }
17 |
18 | func floodFillRecurse(image [][]int, sr int, sc int, oldColor int, newColor int) {
19 | if sr < 0 || sr >= len(image) || sc < 0 || sc >= len(image[0]) || image[sr][sc] != oldColor {
20 | return
21 | }
22 | image[sr][sc] = newColor
23 |
24 | floodFillRecurse(image, sr+1, sc, oldColor, newColor)
25 | floodFillRecurse(image, sr-1, sc, oldColor, newColor)
26 | floodFillRecurse(image, sr, sc+1, oldColor, newColor)
27 | floodFillRecurse(image, sr, sc-1, oldColor, newColor)
28 | }
29 |
30 | // Iterative Implementierung mit Stack (DFS)
31 | func floodFillDFS(image [][]int, sr int, sc int, newColor int) [][]int {
32 | oldColor := image[sr][sc]
33 | if oldColor == newColor {
34 | return image
35 | }
36 |
37 | stack := [][]int{{sr, sc}}
38 | for len(stack) > 0 {
39 | current := stack[len(stack)-1]
40 | stack = stack[:len(stack)-1]
41 |
42 | r, c := current[0], current[1]
43 | if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
44 | continue
45 | }
46 |
47 | image[r][c] = newColor
48 |
49 | stack = append(stack, []int{r + 1, c})
50 | stack = append(stack, []int{r - 1, c})
51 | stack = append(stack, []int{r, c + 1})
52 | stack = append(stack, []int{r, c - 1})
53 | }
54 | return image
55 | }
56 |
57 | // Iterative Implementierung mit Queue (BFS)
58 | func floodFillBFS(image [][]int, sr int, sc int, newColor int) [][]int {
59 | oldColor := image[sr][sc]
60 | if oldColor == newColor {
61 | return image
62 | }
63 |
64 | queue := [][]int{{sr, sc}}
65 | for len(queue) > 0 {
66 | current := queue[0]
67 | queue = queue[1:]
68 |
69 | r, c := current[0], current[1]
70 | if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
71 | continue
72 | }
73 |
74 | image[r][c] = newColor
75 |
76 | queue = append(queue, []int{r + 1, c})
77 | queue = append(queue, []int{r - 1, c})
78 | queue = append(queue, []int{r, c + 1})
79 | queue = append(queue, []int{r, c - 1})
80 | }
81 | return image
82 | }
83 |
84 | // Iterative Implementierung mit Stack (4-Wege-Verbindung)
85 | func floodFillStack4Way(image [][]int, sr int, sc int, newColor int) [][]int {
86 | oldColor := image[sr][sc]
87 | if oldColor == newColor {
88 | return image
89 | }
90 |
91 | stack := [][]int{{sr, sc}}
92 | directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
93 | for len(stack) > 0 {
94 | current := stack[len(stack)-1]
95 | stack = stack[:len(stack)-1]
96 |
97 | r, c := current[0], current[1]
98 | if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
99 | continue
100 | }
101 |
102 | image[r][c] = newColor
103 |
104 | for _, dir := range directions {
105 | stack = append(stack, []int{r + dir[0], c + dir[1]})
106 | }
107 | }
108 | return image
109 | }
110 |
111 | // Komplexes Beispielbild
112 | var complexImage = [][]int{
113 | {0, 0, 0, 0, 0, 0},
114 | {0, 1, 1, 0, 1, 0},
115 | {0, 1, 0, 0, 1, 0},
116 | {0, 1, 1, 1, 1, 0},
117 | {0, 0, 0, 0, 0, 0},
118 | {0, 1, 1, 0, 1, 1},
119 | }
120 |
121 | // Benchmark für die rekursive Implementierung
122 | func BenchmarkFloodFillRecursive(b *testing.B) {
123 | for i := 0; i < b.N; i++ {
124 | image := make([][]int, len(complexImage))
125 | for j := range complexImage {
126 | image[j] = make([]int, len(complexImage[j]))
127 | copy(image[j], complexImage[j])
128 | }
129 | floodFillRecursive(image, 1, 1, 2)
130 | }
131 | }
132 |
133 | // Benchmark für die iterative Implementierung mit Stack (DFS)
134 | func BenchmarkFloodFillDFS(b *testing.B) {
135 | for i := 0; i < b.N; i++ {
136 | image := make([][]int, len(complexImage))
137 | for j := range complexImage {
138 | image[j] = make([]int, len(complexImage[j]))
139 | copy(image[j], complexImage[j])
140 | }
141 | floodFillDFS(image, 1, 1, 2)
142 | }
143 | }
144 |
145 | // Benchmark für die iterative Implementierung mit Queue (BFS)
146 | func BenchmarkFloodFillBFS(b *testing.B) {
147 | for i := 0; i < b.N; i++ {
148 | image := make([][]int, len(complexImage))
149 | for j := range complexImage {
150 | image[j] = make([]int, len(complexImage[j]))
151 | copy(image[j], complexImage[j])
152 | }
153 | floodFillBFS(image, 1, 1, 2)
154 | }
155 | }
156 |
157 | // Benchmark für die iterative Implementierung mit Stack (4-Wege-Verbindung)
158 | func BenchmarkFloodFillStack4Way(b *testing.B) {
159 | for i := 0; i < b.N; i++ {
160 | image := make([][]int, len(complexImage))
161 | for j := range complexImage {
162 | image[j] = make([]int, len(complexImage[j]))
163 | copy(image[j], complexImage[j])
164 | }
165 | floodFillStack4Way(image, 1, 1, 2)
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/foreach/foreach_test.go:
--------------------------------------------------------------------------------
1 | // Package foreach benchmarks ranging over slices and maps.
2 | package foreach
3 |
4 | import (
5 | "testing"
6 | )
7 |
8 | var amap map[int]string
9 | var aslice []string
10 |
11 | func init() {
12 | amap = map[int]string{
13 | 0: "lorem",
14 | 1: "ipsum",
15 | 2: "dolor",
16 | 3: "sit",
17 | 4: "amet",
18 | }
19 |
20 | aslice = []string{
21 | "lorem",
22 | "ipsum",
23 | "dolor",
24 | "sit",
25 | "amet",
26 | }
27 | }
28 |
29 | func forMap() {
30 | for i := 0; i < len(amap); i++ {
31 | _ = amap[i]
32 | }
33 | }
34 |
35 | func rangeMap() {
36 | for _, v := range amap {
37 | _ = v
38 | }
39 | }
40 |
41 | func rangeSlice() {
42 | for _, v := range aslice {
43 | _ = v
44 | }
45 | }
46 |
47 | func rangeSliceKey() {
48 | for k := range aslice {
49 | _ = aslice[k]
50 | }
51 | }
52 |
53 | func BenchmarkForMap(b *testing.B) {
54 | for n := 0; n < b.N; n++ {
55 | forMap()
56 | }
57 | }
58 |
59 | func BenchmarkRangeMap(b *testing.B) {
60 | for n := 0; n < b.N; n++ {
61 | rangeMap()
62 | }
63 | }
64 |
65 | func BenchmarkRangeSlice(b *testing.B) {
66 | for n := 0; n < b.N; n++ {
67 | rangeSlice()
68 | }
69 | }
70 |
71 | func BenchmarkRangeSliceKey(b *testing.B) {
72 | for n := 0; n < b.N; n++ {
73 | rangeSliceKey()
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/SimonWaldherr/golang-benchmarks
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004
7 | github.com/reusee/mmh3 v0.0.0-20140820141314-64b85163255b
8 | github.com/zeebo/blake3 v0.2.3
9 | golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8
10 | )
11 |
12 | require (
13 | github.com/klauspost/cpuid/v2 v2.0.12 // indirect
14 | golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
15 | simonwaldherr.de/go/golibs v0.14.0 // indirect
16 | simonwaldherr.de/go/ranger v0.1.4 // indirect
17 | )
18 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 h1:G+9t9cEtnC9jFiTxyptEKuNIAbiN5ZCQzX2a74lj3xg=
2 | github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004/go.mod h1:KmHnJWQrgEvbuy0vcvj00gtMqbvNn1L+3YUZLK/B92c=
3 | github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
4 | github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
5 | github.com/reusee/mmh3 v0.0.0-20140820141314-64b85163255b h1:GQkEnyBFqzQXb3RFqGt5z2QcBZJVQxgzXKF/sPCFh7w=
6 | github.com/reusee/mmh3 v0.0.0-20140820141314-64b85163255b/go.mod h1:ADBBIMrt68BC/v967NyoiPZMwPVq44r8QJ5oRyXJHJs=
7 | github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
8 | github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
9 | github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
10 | github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
11 | golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
12 | golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
13 | golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
14 | golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
15 | simonwaldherr.de/go/golibs v0.14.0 h1:8sTp6ubcnjv8IgrpoQw9wJbbPXCONSnUJwQ+RYxLFtM=
16 | simonwaldherr.de/go/golibs v0.14.0/go.mod h1:sW7x4PAds/r6rLt/THqsZhpFxoq2n6cBb5QtlZ44eVk=
17 | simonwaldherr.de/go/ranger v0.1.4 h1:XYeTZYa5sShWbYmzo6aC96orKaSUpwh68CcMY7PDUac=
18 | simonwaldherr.de/go/ranger v0.1.4/go.mod h1:M12Ibcx9G5mzh/8dXpMhRUmFJJoNXvaiPiJax/d0v8U=
19 |
--------------------------------------------------------------------------------
/hash/hash_test.go:
--------------------------------------------------------------------------------
1 | // Package hash benchmarks various hashing algorithms.
2 | // Especially with hashing algorithms, faster is not always better.
3 | // One should always decide on the basis of the respective requirements.
4 | package hash
5 |
6 | import (
7 | "crypto/md5"
8 | "crypto/sha1"
9 | "crypto/sha256"
10 | "crypto/sha512"
11 | "hash"
12 | "hash/adler32"
13 | "hash/crc32"
14 | "hash/crc64"
15 | "hash/fnv"
16 | "math/rand"
17 | "testing"
18 |
19 | "github.com/jzelinskie/whirlpool"
20 | "github.com/reusee/mmh3"
21 | "github.com/zeebo/blake3"
22 | "golang.org/x/crypto/bcrypt"
23 | "golang.org/x/crypto/blake2b"
24 | "golang.org/x/crypto/md4"
25 | "golang.org/x/crypto/ripemd160"
26 | "golang.org/x/crypto/sha3"
27 | )
28 |
29 | func benchmarkHashAlgo(b *testing.B, h hash.Hash) {
30 | data := make([]byte, 2048)
31 | rand.Read(data)
32 |
33 | b.ResetTimer()
34 | for n := 0; n < b.N; n++ {
35 | h.Reset()
36 | h.Write(data)
37 | _ = h.Sum(nil)
38 | }
39 | }
40 |
41 | func benchmarkBCryptHashAlgo(b *testing.B, cost int) {
42 | data := make([]byte, 2048)
43 | rand.Read(data)
44 |
45 | b.ResetTimer()
46 | for n := 0; n < b.N; n++ {
47 | bcrypt.GenerateFromPassword(data, cost)
48 | }
49 | }
50 |
51 | func BenchmarkAdler32(b *testing.B) {
52 | benchmarkHashAlgo(b, adler32.New())
53 | }
54 |
55 | func BenchmarkBCryptCost4(b *testing.B) {
56 | benchmarkBCryptHashAlgo(b, 4)
57 | }
58 |
59 | func BenchmarkBCryptCost10(b *testing.B) {
60 | benchmarkBCryptHashAlgo(b, 10)
61 | }
62 |
63 | func BenchmarkBCryptCost16(b *testing.B) {
64 | benchmarkBCryptHashAlgo(b, 16)
65 | }
66 |
67 | /*
68 | func BenchmarkBCryptCost22(b *testing.B) {
69 | benchmarkBCryptHashAlgo(b, 22)
70 | }
71 |
72 | func BenchmarkBCryptCost28(b *testing.B) {
73 | benchmarkBCryptHashAlgo(b, 28)
74 | }
75 |
76 | func BenchmarkBCryptCost31(b *testing.B) {
77 | benchmarkBCryptHashAlgo(b, 31)
78 | }
79 | */
80 |
81 | func BenchmarkBlake2b256(b *testing.B) {
82 | h, err := blake2b.New256(nil)
83 | if err != nil {
84 | b.Fatal(err)
85 | }
86 | benchmarkHashAlgo(b, h)
87 | }
88 |
89 | func BenchmarkBlake2b512(b *testing.B) {
90 | h, err := blake2b.New512(nil)
91 | if err != nil {
92 | b.Fatal(err)
93 | }
94 | benchmarkHashAlgo(b, h)
95 | }
96 |
97 | func BenchmarkBlake3256(b *testing.B) {
98 | benchmarkHashAlgo(b, blake3.New())
99 | }
100 |
101 | func BenchmarkMMH3(b *testing.B) {
102 | benchmarkHashAlgo(b, mmh3.New128())
103 | }
104 |
105 | func BenchmarkCRC32(b *testing.B) {
106 | benchmarkHashAlgo(b, crc32.NewIEEE())
107 | }
108 |
109 | func BenchmarkCRC64ISO(b *testing.B) {
110 | benchmarkHashAlgo(b, crc64.New(crc64.MakeTable(crc64.ISO)))
111 | }
112 |
113 | func BenchmarkCRC64ECMA(b *testing.B) {
114 | benchmarkHashAlgo(b, crc64.New(crc64.MakeTable(crc64.ECMA)))
115 | }
116 |
117 | func BenchmarkFnv32(b *testing.B) {
118 | benchmarkHashAlgo(b, fnv.New32())
119 | }
120 |
121 | func BenchmarkFnv32a(b *testing.B) {
122 | benchmarkHashAlgo(b, fnv.New32a())
123 | }
124 |
125 | func BenchmarkFnv64(b *testing.B) {
126 | benchmarkHashAlgo(b, fnv.New64())
127 | }
128 |
129 | func BenchmarkFnv64a(b *testing.B) {
130 | benchmarkHashAlgo(b, fnv.New64a())
131 | }
132 |
133 | func BenchmarkFnv128(b *testing.B) {
134 | benchmarkHashAlgo(b, fnv.New128())
135 | }
136 |
137 | func BenchmarkFnv128a(b *testing.B) {
138 | benchmarkHashAlgo(b, fnv.New128a())
139 | }
140 |
141 | func BenchmarkMD4(b *testing.B) {
142 | benchmarkHashAlgo(b, md4.New())
143 | }
144 |
145 | func BenchmarkMD5(b *testing.B) {
146 | benchmarkHashAlgo(b, md5.New())
147 | }
148 |
149 | func BenchmarkSHA1(b *testing.B) {
150 | benchmarkHashAlgo(b, sha1.New())
151 | }
152 |
153 | func BenchmarkSHA224(b *testing.B) {
154 | benchmarkHashAlgo(b, sha256.New224())
155 | }
156 |
157 | func BenchmarkSHA256(b *testing.B) {
158 | benchmarkHashAlgo(b, sha256.New())
159 | }
160 |
161 | func BenchmarkSHA384(b *testing.B) {
162 | benchmarkHashAlgo(b, sha512.New384())
163 | }
164 |
165 | func BenchmarkSHA512(b *testing.B) {
166 | benchmarkHashAlgo(b, sha512.New())
167 | }
168 |
169 | func BenchmarkSHA3256(b *testing.B) {
170 | benchmarkHashAlgo(b, sha3.New256())
171 | }
172 |
173 | func BenchmarkSHA3512(b *testing.B) {
174 | benchmarkHashAlgo(b, sha3.New512())
175 | }
176 |
177 | func BenchmarkRIPEMD160(b *testing.B) {
178 | benchmarkHashAlgo(b, ripemd160.New())
179 | }
180 |
181 | func BenchmarkWhirlpool(b *testing.B) {
182 | benchmarkHashAlgo(b, whirlpool.New())
183 | }
184 |
185 | func BenchmarkSHA256Parallel(b *testing.B) {
186 | b.RunParallel(func(pb *testing.PB) {
187 | data := make([]byte, 2048)
188 | rand.Read(data)
189 | for pb.Next() {
190 | h := sha256.New()
191 | h.Write(data)
192 | h.Sum(nil)
193 | }
194 | })
195 | }
196 |
--------------------------------------------------------------------------------
/index/index_test.go:
--------------------------------------------------------------------------------
1 | // Package index benchmarks access on maps with various data types as keys.
2 | package index
3 |
4 | import (
5 | "math/rand"
6 | "strconv"
7 | "testing"
8 | )
9 |
10 | var NumItems int = 1000000
11 |
12 | var ms map[string]string
13 | var ks []string
14 |
15 | var mi map[int]string
16 | var ki []int
17 |
18 | func initMapStringIndex() {
19 | ms = make(map[string]string)
20 | ks = make([]string, 0)
21 |
22 | for i := 0; i < NumItems; i++ {
23 | key := strconv.Itoa(rand.Intn(NumItems))
24 | ms[key] = "value" + strconv.Itoa(i)
25 | ks = append(ks, key)
26 | }
27 | }
28 |
29 | func initMapIntIndex() {
30 | mi = make(map[int]string)
31 | ki = make([]int, 0)
32 |
33 | for i := 0; i < NumItems; i++ {
34 | key := rand.Intn(NumItems)
35 | mi[key] = "value" + strconv.Itoa(i)
36 | ki = append(ki, key)
37 | }
38 | }
39 |
40 | func init() {
41 | initMapStringIndex()
42 | initMapIntIndex()
43 | }
44 |
45 | func BenchmarkMapStringKeys(b *testing.B) {
46 | i := 0
47 |
48 | for n := 0; n < b.N; n++ {
49 | if _, ok := ms[ks[i]]; ok {
50 | }
51 |
52 | i++
53 | if i >= NumItems {
54 | i = 0
55 | }
56 | }
57 | }
58 |
59 | func BenchmarkMapIntKeys(b *testing.B) {
60 | i := 0
61 |
62 | for n := 0; n < b.N; n++ {
63 | if _, ok := mi[ki[i]]; ok {
64 | }
65 |
66 | i++
67 | if i >= NumItems {
68 | i = 0
69 | }
70 | }
71 | }
72 |
73 | func BenchmarkMapStringIndex(b *testing.B) {
74 | i := 0
75 |
76 | for n := 0; n < b.N; n++ {
77 | _ = ms[ks[i]]
78 |
79 | i++
80 | if i >= NumItems {
81 | i = 0
82 | }
83 | }
84 | }
85 |
86 | func BenchmarkMapIntIndex(b *testing.B) {
87 | i := 0
88 |
89 | for n := 0; n < b.N; n++ {
90 | _ = mi[ki[i]]
91 |
92 | i++
93 | if i >= NumItems {
94 | i = 0
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/json/json_test.go:
--------------------------------------------------------------------------------
1 | package json
2 |
3 | import (
4 | "encoding/json"
5 | "math"
6 | "math/big"
7 | "testing"
8 | "time"
9 | )
10 |
11 | type Data struct {
12 | String string
13 | Time time.Time
14 | Int int
15 | Int8 int8
16 | Int16 int16
17 | Int32 int32
18 | Int64 int64
19 | Boolean bool
20 | Float32 float32
21 | Float64 float64
22 | BigInt big.Int
23 | BigFloat big.Float
24 | }
25 |
26 | func BenchmarkJsonMarshal(b *testing.B) {
27 | for n := 0; n < b.N; n++ {
28 | var d = Data{
29 | String: "",
30 | Time: time.Now(),
31 | Int: math.MaxInt32,
32 | Int8: math.MaxInt8,
33 | Int16: math.MaxInt16,
34 | Int32: math.MaxInt32,
35 | Int64: math.MaxInt64,
36 | Boolean: false,
37 | Float32: math.MaxFloat32,
38 | Float64: math.MaxFloat64,
39 | BigInt: *big.NewInt(math.MaxInt64),
40 | BigFloat: *big.NewFloat(math.MaxFloat64),
41 | }
42 |
43 | _, err := json.Marshal(d)
44 | if err != nil {
45 | b.Error(err)
46 | b.Fail()
47 | return
48 | }
49 | }
50 | }
51 |
52 | func BenchmarkJsonUnmarshal(b *testing.B) {
53 | str := `
54 | {
55 | "String": "",
56 | "Time": "2019-10-30T16:41:29.853426+07:00",
57 | "Int": 2147483647,
58 | "Int8": 127,
59 | "Int16": 32767,
60 | "Int32": 2147483647,
61 | "Int64": 9223372036854775807,
62 | "Boolean": false,
63 | "Float32": 3.4028235e+38,
64 | "Float64": 1.7976931348623157e+308,
65 | "BigInt": 9999999999999999999,
66 | "BigFloat": "2.7976931348623157e+308"
67 | }
68 | `
69 |
70 | for n := 0; n < b.N; n++ {
71 | var d Data
72 | err := json.Unmarshal([]byte(str), &d)
73 | if err != nil {
74 | b.Error(err)
75 | b.Fail()
76 | return
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/math/math_test.go:
--------------------------------------------------------------------------------
1 | // Package math compares the speed of various mathematical operations.
2 | package math
3 |
4 | import (
5 | "sync"
6 | "sync/atomic"
7 | "testing"
8 | )
9 |
10 | func BenchmarkMathInt8(b *testing.B) {
11 | var intVal int8
12 | for n := 0; n < b.N; n++ {
13 | intVal = intVal + 2
14 | }
15 | }
16 |
17 | func BenchmarkMathInt32(b *testing.B) {
18 | var intVal int32
19 | for n := 0; n < b.N; n++ {
20 | intVal = intVal + 2
21 | }
22 | }
23 |
24 | func BenchmarkMathInt64(b *testing.B) {
25 | var intVal int64
26 | for n := 0; n < b.N; n++ {
27 | intVal = intVal + 2
28 | }
29 | }
30 |
31 | func BenchmarkMathAtomicInt32(b *testing.B) {
32 | var intVal int32
33 | for n := 0; n < b.N; n++ {
34 | atomic.AddInt32(&intVal, 2)
35 | }
36 | }
37 |
38 | func BenchmarkMathAtomicInt64(b *testing.B) {
39 | var intVal int64
40 | for n := 0; n < b.N; n++ {
41 | atomic.AddInt64(&intVal, 2)
42 | }
43 | }
44 |
45 | type IntMutex struct {
46 | v int64
47 | mux sync.Mutex
48 | }
49 |
50 | func BenchmarkMathMutexInt(b *testing.B) {
51 | var m IntMutex
52 | for n := 0; n < b.N; n++ {
53 | m.mux.Lock()
54 | m.v = m.v + 2
55 | m.mux.Unlock()
56 | }
57 | }
58 |
59 | func BenchmarkMathFloat32(b *testing.B) {
60 | var floatVal float32
61 | for n := 0; n < b.N; n++ {
62 | floatVal = floatVal + 2
63 | }
64 | }
65 |
66 | func BenchmarkMathFloat64(b *testing.B) {
67 | var floatVal float64
68 | for n := 0; n < b.N; n++ {
69 | floatVal = floatVal + 2
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/parse/parse_test.go:
--------------------------------------------------------------------------------
1 | // Package parse benchmarks parsing.
2 | package parse
3 |
4 | import (
5 | "strconv"
6 | "testing"
7 | )
8 |
9 | func BenchmarkParseBool(b *testing.B) {
10 | for n := 0; n < b.N; n++ {
11 | _, err := strconv.ParseBool("true")
12 | if err != nil {
13 | panic(err)
14 | }
15 | }
16 | }
17 |
18 | func BenchmarkParseInt(b *testing.B) {
19 | for n := 0; n < b.N; n++ {
20 | _, err := strconv.ParseInt("1337", 10, 64)
21 | if err != nil {
22 | panic(err)
23 | }
24 | }
25 | }
26 |
27 | func BenchmarkParseFloat(b *testing.B) {
28 | for n := 0; n < b.N; n++ {
29 | _, err := strconv.ParseFloat("3.141592653589793238462643383", 64)
30 | if err != nil {
31 | panic(err)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/random/random_test.go:
--------------------------------------------------------------------------------
1 | // Package random compares math/rand with crypto/rand.
2 | // math/rand is much faster than crypto/rand, but it
3 | // returns only a pseudo random number.
4 | package random
5 |
6 | import (
7 | crand "crypto/rand"
8 | "encoding/base64"
9 | "math/big"
10 | mrand "math/rand"
11 | "testing"
12 | )
13 |
14 | func BenchmarkMathRand(b *testing.B) {
15 | for n := 0; n < b.N; n++ {
16 | mrand.Int63n(0xFFFF)
17 | }
18 | }
19 |
20 | func BenchmarkCryptoRand(b *testing.B) {
21 | for n := 0; n < b.N; n++ {
22 | _, err := crand.Int(crand.Reader, big.NewInt(0xFFFF))
23 | if err != nil {
24 | panic(err)
25 | }
26 | }
27 | }
28 |
29 | func BenchmarkCryptoRandString(b *testing.B) {
30 | for n := 0; n < b.N; n++ {
31 | _, err := GenerateRandomString(32)
32 | if err != nil {
33 | panic(err)
34 | }
35 | }
36 | }
37 |
38 | func BenchmarkCryptoRandBytes(b *testing.B) {
39 | for n := 0; n < b.N; n++ {
40 | _, err := GenerateRandomBytes(32)
41 | if err != nil {
42 | panic(err)
43 | }
44 | }
45 | }
46 |
47 | func GenerateRandomBytes(n int) ([]byte, error) {
48 | b := make([]byte, n)
49 | _, err := mrand.Read(b)
50 | if err != nil {
51 | return nil, err
52 | }
53 |
54 | return b, nil
55 | }
56 |
57 | func GenerateRandomString(s int) (string, error) {
58 | b, err := GenerateRandomBytes(s)
59 | return base64.URLEncoding.EncodeToString(b), err
60 | }
61 |
--------------------------------------------------------------------------------
/regexp/regexp_test.go:
--------------------------------------------------------------------------------
1 | // Package regexp benchmarks the performance of a pre-compiled regexp match
2 | // a non-pre-compiled match and JIT-cached-compilation via golibs: https://simonwaldherr.de/go/golibs
3 | package regexp
4 |
5 | import (
6 | "regexp"
7 | "testing"
8 |
9 | "simonwaldherr.de/go/golibs/regex"
10 | )
11 |
12 | var regexpStr string = `^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,9}$`
13 |
14 | func BenchmarkMatchString(b *testing.B) {
15 | for n := 0; n < b.N; n++ {
16 | _, err := regexp.MatchString(regexpStr, "john.doe@example.tld")
17 | if err != nil {
18 | panic(err)
19 | }
20 | }
21 | }
22 |
23 | func BenchmarkMatchStringCompiled(b *testing.B) {
24 | r, err := regexp.Compile(regexpStr)
25 | if err != nil {
26 | panic(err)
27 | }
28 |
29 | b.ResetTimer()
30 | for n := 0; n < b.N; n++ {
31 | r.MatchString("john.doe@example.tld")
32 | }
33 | }
34 |
35 | func BenchmarkMatchStringGolibs(b *testing.B) {
36 | for n := 0; n < b.N; n++ {
37 | _, err := regex.MatchString("john.doe@example.tld", regexpStr)
38 | if err != nil {
39 | panic(err)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/split/split.go:
--------------------------------------------------------------------------------
1 | // Package split benchmarks various string splitting methods.
2 | package split
3 |
4 | import (
5 | "strings"
6 | "testing"
7 | )
8 |
9 | func BenchmarkSplitMethods(b *testing.B) {
10 | b.Run("Strings.Split", func(b *testing.B) {
11 | str := "one,two,three,four,five"
12 | for n := 0; n < b.N; n++ {
13 | _ = strings.Split(str, ",")
14 | }
15 | })
16 |
17 | b.Run("Strings.SplitN", func(b *testing.B) {
18 | str := "one,two,three,four,five"
19 | for n := 0; n < b.N; n++ {
20 | _ = strings.SplitN(str, ",", 3)
21 | }
22 | })
23 |
24 | b.Run("Strings.FieldsFunc", func(b *testing.B) {
25 | str := "one,two,three,four,five"
26 | for n := 0; n < b.N; n++ {
27 | _ = strings.FieldsFunc(str, func(r rune) bool { return r == ',' })
28 | }
29 | })
30 | }
31 |
--------------------------------------------------------------------------------
/template/template_test.go:
--------------------------------------------------------------------------------
1 | // Package template benchmarks the performance of different templating methods
2 | package template
3 |
4 | import (
5 | "bytes"
6 | htmltemplate "html/template"
7 | "regexp"
8 | "testing"
9 | texttemplate "text/template"
10 | )
11 |
12 | // Define a struct to hold the data for the templates
13 | type Data struct {
14 | Name string
15 | Address string
16 | }
17 |
18 | // Prepare the templates and data
19 | var (
20 | data = Data{Name: "John Doe", Address: "123 Elm St"}
21 |
22 | textTplString = "Name: {{.Name}}, Address: {{.Address}}"
23 | htmlTplString = "Name: {{.Name}}
Address: {{.Address}}
"
24 | regExpString = "Name: {{NAME}}, Address: {{ADDRESS}}"
25 | )
26 |
27 | // Benchmark for text/template
28 | func BenchmarkTextTemplate(b *testing.B) {
29 | tpl, _ := texttemplate.New("text").Parse(textTplString)
30 | b.ResetTimer()
31 | for i := 0; i < b.N; i++ {
32 | var buf bytes.Buffer
33 | tpl.Execute(&buf, data)
34 | }
35 | }
36 |
37 | // Benchmark for html/template
38 | func BenchmarkHTMLTemplate(b *testing.B) {
39 | tpl, _ := htmltemplate.New("html").Parse(htmlTplString)
40 | b.ResetTimer()
41 | for i := 0; i < b.N; i++ {
42 | var buf bytes.Buffer
43 | tpl.Execute(&buf, data)
44 | }
45 | }
46 |
47 | // Benchmark for replacing placeholders using regexp
48 | func BenchmarkRegExp(b *testing.B) {
49 | rName := regexp.MustCompile(`{{NAME}}`)
50 | rAddress := regexp.MustCompile(`{{ADDRESS}}`)
51 | b.ResetTimer()
52 | for i := 0; i < b.N; i++ {
53 | result := rName.ReplaceAllString(regExpString, data.Name)
54 | result = rAddress.ReplaceAllString(result, data.Address)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/updateBenchLogs.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | setlocal
4 | set "benchs=base64 between concat contains foreach hash index json math parse random regexp"
5 |
6 | > README.md (
7 | echo.# Go Benchmarks
8 | echo.
9 | echo.In programming in general, and in Golang in particular, many roads lead to Rome.
10 | echo.From time to time I ask myself which of these ways is the fastest.
11 | echo.In Golang there is a wonderful solution, with \`go test -bench\` you can measure the speed very easily and quickly.
12 | echo.In order for you to benefit from it too, I will publish such benchmarks in this repository in the future.
13 | echo.
14 | echo.## ToC
15 | echo.
16 | )
17 |
18 | for %%i in (%benchs%) do (
19 | echo * [%%i](https://github.com/SimonWaldherr/golang-benchmarks#%%i^) >> README.md
20 | )
21 |
22 | >> README.md (
23 | echo.
24 | echo.## Golang?
25 | echo.
26 | echo.I published another repository where I show some Golang examples.
27 | echo.If you\'re interested in new programming languages, you should definitely take a look at Golang:
28 | echo.
29 | echo.* [Golang examples](https://github.com/SimonWaldherr/golang-examples^)
30 | echo.* [tour.golang.org](https://tour.golang.org/^)
31 | echo.* [Go by example](https://gobyexample.com/^)
32 | echo.* [Golang Book](http://www.golang-book.com/^)
33 | echo.* [Go-Learn](https://github.com/skippednote/Go-Learn^)
34 | echo.
35 | echo.## Is it any good?
36 | echo.
37 | echo.[Yes](https://news.ycombinator.com/item?id=3067434^)
38 | echo.
39 | echo.## Benchmark Results
40 | echo.
41 | )
42 |
43 | go fmt ./...
44 |
45 | for /f "delims=;" %%i in ('go version') do set GO_VERSION=%%i
46 |
47 | echo.Golang Version: %GO_VERSION% >> README.md
48 | echo. >> README.md
49 |
50 | for %%i in (%benchs%) do (
51 |
52 | @cd %%i
53 | @gofmt -s -w -l -e .
54 |
55 | echo.### %%i
56 | echo.
57 | echo.```go
58 | type *_test.go
59 | echo.```
60 | echo.
61 | echo.```
62 | echo.$ go test -bench . -benchmem
63 | go test -bench . -benchmem
64 | echo.```
65 | echo.
66 |
67 | @cd ..
68 | ) >> README.win.md
69 |
--------------------------------------------------------------------------------
/updateBenchLogs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | declare -a benchs=(base64 between caseinsensitivecompare concat contains embed floodfill foreach hash index json math parse random regexp template trim)
4 |
5 | cat > README.md <<- EOM
6 | # Go Benchmarks
7 |
8 | [](https://zenodo.org/badge/latestdoi/154216722)
9 | [](https://goreportcard.com/report/github.com/SimonWaldherr/golang-benchmarks)
10 | [](https://opensource.org/licenses/MIT)
11 |
12 | In programming in general, and in Golang in particular, many roads lead to Rome.
13 | From time to time I ask myself which of these ways is the fastest.
14 | In Golang there is a wonderful solution, with \`go test -bench\` you can measure the speed very easily and quickly.
15 | In order for you to benefit from it too, I will publish such benchmarks in this repository in the future.
16 |
17 | ## ToC
18 |
19 | EOM
20 |
21 | for i in "${benchs[@]}"
22 | do
23 | echo "* [$i](https://github.com/SimonWaldherr/golang-benchmarks#$i)" >> README.md
24 | done
25 |
26 | cat >> README.md <<- EOM
27 |
28 | ## Golang?
29 |
30 | I published another repository where I show some Golang examples.
31 | If you\'re interested in new programming languages, you should definitely take a look at Golang:
32 |
33 | * [Golang examples](https://github.com/SimonWaldherr/golang-examples)
34 | * [tour.golang.org](https://tour.golang.org/)
35 | * [Go by example](https://gobyexample.com/)
36 | * [Golang Book](http://www.golang-book.com/)
37 | * [Go-Learn](https://github.com/skippednote/Go-Learn)
38 |
39 | ## Is it any good?
40 |
41 | [Yes](https://news.ycombinator.com/item?id=3067434)
42 |
43 | ## Benchmark Results
44 |
45 | EOM
46 |
47 | go fmt ./...
48 |
49 | echo -n "Golang Version: " >> README.md
50 | go version >> README.md
51 | echo "" >> README.md
52 |
53 | for i in "${benchs[@]}"
54 | do
55 | cd $i
56 | gofmt -s -w -l -e .
57 | echo "### $i" >> ../README.md
58 | echo >> ../README.md
59 | echo "\`\`\`go" >> ../README.md
60 | cat *_test.go >> ../README.md
61 | echo "\`\`\`" >> ../README.md
62 | echo >> ../README.md
63 | echo "\`\`\`" >> ../README.md
64 | echo "$ go test -bench . -benchmem" >> ../README.md
65 | go test -bench . -benchmem >> ../README.md
66 | echo "\`\`\`" >> ../README.md
67 | echo >> ../README.md
68 | cd ..
69 | done
70 |
--------------------------------------------------------------------------------