Skip to content

Instantly share code, notes, and snippets.

@maxcnunes
Last active March 9, 2019 13:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxcnunes/ed0cda578edc449748d99ee28c586dec to your computer and use it in GitHub Desktop.
Save maxcnunes/ed0cda578edc449748d99ee28c586dec to your computer and use it in GitHub Desktop.
Performance over different approaches to get a mask format from a HTTP status code (e.g. 201=2xx, 204=2xx, 405=4xx, 500=5xx)
package util_test
import (
"fmt"
"math"
"regexp"
"strconv"
"testing"
)
var re = regexp.MustCompile(`\d{2}$`)
// Get a masked value from a http status code (e.g. 201=2xx, 204=2xx, 405=4xx, 500=5xx)
func BenchmarkStatusBucket(b *testing.B) {
statusCode := 201
b.Run("if-statements", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := getStatusIfStatemets(statusCode)
_ = s
}
})
b.Run("Itoa+concat", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := getStatusItoaWithConcat(statusCode)
_ = s
}
})
b.Run("Itoa+[]byte", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := getStatusItoaWithByteArray(statusCode)
_ = s
}
})
b.Run("Itoa+Sprintf", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := getStatusItoaWithSprintf(statusCode)
_ = s
}
})
b.Run("regex+concat", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := getStatusRegexWithConcat(statusCode)
_ = s
}
})
b.Run("math+Sprintf", func(b *testing.B) {
for i := 0; i < b.N; i++ {
s := getStatusMathWithSprintf(statusCode)
_ = s
}
})
}
var (
status1xx = "1xx"
status2xx = "2xx"
status3xx = "3xx"
status4xx = "4xx"
status5xx = "5xx"
)
func getStatusIfStatemets(statusCode int) string {
if statusCode >= 100 && statusCode < 200 {
return status1xx
} else if statusCode < 300 {
return status2xx
} else if statusCode < 400 {
return status3xx
} else if statusCode < 500 {
return status4xx
}
return status5xx
}
func getStatusItoaWithConcat(statusCode int) string {
return string(strconv.Itoa(statusCode)[0]) + "xx"
}
func getStatusItoaWithByteArray(statusCode int) string {
b := make([]byte, 3)
b[0] = strconv.Itoa(statusCode)[0]
b[1] = 'x'
b[2] = 'x'
return string(b[:])
}
func getStatusItoaWithSprintf(statusCode int) string {
return fmt.Sprintf("%dxx", strconv.Itoa(statusCode)[0])
}
func getStatusRegexWithConcat(statusCode int) string {
return re.ReplaceAllString(strconv.FormatInt(int64(statusCode), 10), `XX`)
}
func getStatusMathWithSprintf(statusCode int) string {
return fmt.Sprintf("%.0fxx", math.Floor(float64(statusCode/100)))
}
@maxcnunes
Copy link
Author

maxcnunes commented Mar 9, 2019

go version go1.12 darwin/amd64

Benchmark test

go test -bench=.  -memprofile=mem.out -benchmem -benchtime=5s
goos: darwin
goarch: amd64
BenchmarkStatusBucket/if-statements-8           10000000000              0.67 ns/op            0 B/op        0 allocs/op
BenchmarkStatusBucket/Itoa+concat-8             100000000               52.5 ns/op             3 B/op        1 allocs/op
BenchmarkStatusBucket/Itoa+[]byte-8             100000000               53.7 ns/op             6 B/op        2 allocs/op
BenchmarkStatusBucket/Itoa+Sprintf-8            50000000               135 ns/op               8 B/op        2 allocs/op
BenchmarkStatusBucket/regex+concat-8            20000000               426 ns/op              32 B/op        4 allocs/op
BenchmarkStatusBucket/math+Sprintf-8            20000000               323 ns/op              16 B/op        2 allocs/op

Memory profiling alloc_space

go tool pprof util.test mem.out
File: util.test
Type: alloc_space
Time: Mar 9, 2019 at 9:40am (-03)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top 20
Showing nodes accounting for 2311.54MB, 99.49% of 2323.32MB total
Dropped 8 nodes (cum <= 11.62MB)
      flat  flat%   sum%        cum   cum%
  755.01MB 32.50% 32.50%   755.01MB 32.50%  strconv.formatBits
  518.01MB 22.30% 54.79%   519.01MB 22.34%  fmt.Sprintf
  498.51MB 21.46% 76.25%   509.30MB 21.92%  regexp.(*Regexp).replaceAll
     312MB 13.43% 89.68%   622.01MB 26.77%  github.com/InVisionApp/api/util_test.getStatusItoaWithByteArray
  146.50MB  6.31% 95.98%   655.80MB 28.23%  regexp.(*Regexp).ReplaceAllString
   81.50MB  3.51% 99.49%      316MB 13.60%  github.com/InVisionApp/api/util_test.getStatusMathWithSprintf
         0     0% 99.49%      323MB 13.90%  github.com/InVisionApp/api/util_test.BenchmarkStatusBucket.func2
         0     0% 99.49%   622.01MB 26.77%  github.com/InVisionApp/api/util_test.BenchmarkStatusBucket.func3
         0     0% 99.49%   406.51MB 17.50%  github.com/InVisionApp/api/util_test.BenchmarkStatusBucket.func4
         0     0% 99.49%   655.80MB 28.23%  github.com/InVisionApp/api/util_test.BenchmarkStatusBucket.func5
         0     0% 99.49%      316MB 13.60%  github.com/InVisionApp/api/util_test.BenchmarkStatusBucket.func6
         0     0% 99.49%      323MB 13.90%  github.com/InVisionApp/api/util_test.getStatusItoaWithConcat
         0     0% 99.49%   406.51MB 17.50%  github.com/InVisionApp/api/util_test.getStatusItoaWithSprintf
         0     0% 99.49%   655.80MB 28.23%  github.com/InVisionApp/api/util_test.getStatusRegexWithConcat
         0     0% 99.49%   755.01MB 32.50%  strconv.FormatInt
         0     0% 99.49%   755.01MB 32.50%  strconv.Itoa
         0     0% 99.49%  2323.32MB   100%  testing.(*B).launch
         0     0% 99.49%  2323.32MB   100%  testing.(*B).runN

This report shows the if statement is the fastest and with less allocations and the regex approach is the opposite of that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment