├── .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 | [![test](https://github.com/SimonWaldherr/golang-benchmarks/actions/workflows/audit.yml/badge.svg?branch=master&event=push)](https://github.com/SimonWaldherr/golang-benchmarks/actions/workflows/audit.yml) 4 | [![DOI](https://zenodo.org/badge/154216722.svg)](https://zenodo.org/badge/latestdoi/154216722) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/SimonWaldherr/golang-benchmarks)](https://goreportcard.com/report/github.com/SimonWaldherr/golang-benchmarks) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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 | [![DOI](https://zenodo.org/badge/154216722.svg)](https://zenodo.org/badge/latestdoi/154216722) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/SimonWaldherr/golang-benchmarks)](https://goreportcard.com/report/github.com/SimonWaldherr/golang-benchmarks) 10 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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 | --------------------------------------------------------------------------------