Last active
April 19, 2022 17:30
-
-
Save ifraixedes/fd8b585c3da6dacb6385 to your computer and use it in GitHub Desktop.
Golang Barcelona - Introductory talk about pprof, the official golang profiling tool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file exists to give to this gist a proper name. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.svg | |
*.out | |
*.test | |
pbench | |
server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 | |
(http://www.wtfpl.net/about/) | |
Copyright (C) 2015 Ivan Fraixedes <[email protected]> (https://ivan.fraixed.es) | |
Everyone is permitted to copy and distribute verbatim or modified | |
copies of this license document, and changing it is allowed as long | |
as the name is changed. | |
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
0. You just DO WHAT THE FUCK YOU WANT TO. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.PHONY: test bench bench-profile profile-cpu-graph profile-mem-graph profile-server-cpu profile-server-mem build-server run-server run-vegeta-fib run-vegeta-fibb deps | |
test: | |
@go test *.go | |
bench: | |
@go test -bench . -benchmem *.go | |
bench-profile: | |
@go test -o pbench -bench . -benchmem -benchtime 3s -outputdir pprof-out -memprofile mem.out -cpuprofile cpu.out | |
profile-cpu-graph: | |
@go tool pprof -svg -output pprof-out/cpu.svg pbench pprof-out/cpu.out | |
profile-mem-graph: | |
@go tool pprof -svg -output pprof-out/mem.svg pbench pprof-out/mem.out | |
profile-server-cpu: | |
@go tool pprof http://localhost:8000/debug/pprof/profile | |
profile-server-mem: | |
@go tool pprof http://localhost:8000/debug/pprof/heap | |
build-server: | |
@go build -o server *.go | |
run-server: build-server | |
./server | |
run-vegeta-fib: | |
@vegeta attack -connections 200 -output vegeta-fib.out -targets vfib-target.txt | |
run-vegeta-fibb: | |
@vegeta attack -connections 200 -output vegeta-fibb.out -targets vfibb-target.txt | |
deps: | |
go get github.com/tsenart/vegeta | |
pprof-out: | |
mkdir pprof-out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"log" | |
"net/http" | |
_ "net/http/pprof" | |
"strconv" | |
"strings" | |
) | |
func main() { | |
http.HandleFunc("/fib", fib) | |
http.HandleFunc("/fibb", fibbuffer) | |
fmt.Println("Server listening on port 8000") | |
if err := http.ListenAndServe(":8000", nil); err != nil { | |
log.Fatalf("Server stop listening with error %v", err) | |
} | |
} | |
func fib(w http.ResponseWriter, r *http.Request) { | |
nums := strings.Split(r.FormValue("nums"), ",") | |
if len(nums) == 0 { | |
log.Printf("invalid `nums` query parameter: %v", nums) | |
respondJSON( | |
w, | |
http.StatusBadRequest, | |
[]byte(`{"status":"error","message":"Request doesn't constain nums query param or it's empty"}`)) | |
return | |
} | |
rb := fibResp{ | |
Status: "ok", | |
Results: make([]uint64, len(nums)), | |
} | |
// Create fibonacci function here because it isn't concurrent safe | |
// besides that we have more memory allocation to see in the profiler | |
fibFn := FibonacciMapCache() | |
for i, sn := range nums { | |
n, err := strconv.Atoi(sn) | |
if err != nil { | |
log.Printf("Atoi error %v (nums query param: %v)", err, nums) | |
respondJSON( | |
w, | |
http.StatusBadRequest, | |
[]byte(`{"status":"error","message":"Nums query param contains a values which cannot be parsed as a number"}`)) | |
return | |
} | |
rb.Results[i] = fibFn(uint(n)) | |
} | |
bj, err := json.Marshal(rb) | |
if err != nil { | |
log.Printf("Error marshalling response struct to JSON: %v", err) | |
w.WriteHeader(http.StatusInternalServerError) | |
return | |
} | |
respondJSON(w, http.StatusOK, bj) | |
} | |
func fibbuffer(w http.ResponseWriter, r *http.Request) { | |
nums := strings.Split(r.FormValue("nums"), ",") | |
if len(nums) == 0 { | |
log.Printf("invalid `nums` query parameter: %v", nums) | |
respondJSON( | |
w, | |
http.StatusBadRequest, | |
[]byte(`{"status":"error","message":"Request doesn't constain nums query param or it's empty"}`)) | |
return | |
} | |
rb := make([]uint64, len(nums)) | |
for i, sn := range nums { | |
n, err := strconv.Atoi(sn) | |
if err != nil { | |
log.Printf("Atoi error %v (nums query param: %v)", err, nums) | |
respondJSON( | |
w, | |
http.StatusBadRequest, | |
[]byte(`{"status":"error","message":"Nums query param contains a values which cannot be parsed as a number"}`)) | |
return | |
} | |
rb[i] = FibonacciLoop(uint(n)) | |
} | |
bj := fmt.Sprintf(`{"status":"ok","results":%s}`, strings.Replace(fmt.Sprintf("%v", rb), " ", ",", -1)) | |
respondJSON(w, http.StatusOK, []byte(bj)) | |
} | |
func respondJSON(w http.ResponseWriter, statusCode int, body []byte) { | |
h := w.Header() | |
h.Set("Content-Type", "application/json") | |
w.WriteHeader(statusCode) | |
w.Write(body) | |
} | |
type fibResp struct { | |
Status string `json:"status"` | |
Results []uint64 `json:"results"` | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "math" | |
func PowOf2(exponent uint) uint64 { | |
return uint64(math.Pow(float64(2), float64(exponent))) | |
} | |
func PowOf2Loop(exponent uint) uint64 { | |
var result uint64 = 1 | |
for i := uint(0); i < exponent; i++ { | |
result *= 2 | |
} | |
return result | |
} | |
func PowOf2Shift(exponent uint) uint64 { | |
if exponent == 0 { | |
return 1 | |
} | |
return 2 << (exponent - 1) | |
} | |
func FibonacciRecursive(n uint) uint64 { | |
if n <= 1 { | |
return uint64(n) | |
} | |
return FibonacciRecursive(n-1) + FibonacciRecursive(n-2) | |
} | |
func FibonacciMapCache() func(uint) uint64 { | |
cache := make(map[uint]uint64) | |
var fib func(n uint) uint64 | |
fib = func(n uint) uint64 { | |
if n <= 1 { | |
return uint64(n) | |
} | |
if r, ok := cache[n]; ok { | |
return r | |
} | |
r := fib(n-1) + fib(n-2) | |
cache[n] = r | |
return r | |
} | |
return fib | |
} | |
func FibonacciSliceCache() func(uint) uint64 { | |
cache := make([]uint64, 0) | |
var fib func(n uint) uint64 | |
fib = func(n uint) uint64 { | |
if n <= 1 { | |
return uint64(n) | |
} | |
if uint(len(cache)) < (n - 1) { | |
nc := make([]uint64, (n - 1)) | |
copy(nc, cache) | |
cache = nc | |
} | |
if n != 0 && cache[n-2] != 0 { | |
return cache[n-2] | |
} | |
r := fib(n-1) + fib(n-2) | |
cache[n-2] = r | |
return r | |
} | |
return fib | |
} | |
func FibonacciLoop(n uint) uint64 { | |
if n <= 1 { | |
return uint64(n) | |
} | |
n2 := uint64(0) | |
n1 := uint64(1) | |
for i := uint(2); i < n; i++ { | |
n1, n2 = n1+n2, n1 | |
} | |
return n1 + n2 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"math/rand" | |
"testing" | |
) | |
func TestPowOf2(t *testing.T) { | |
assertEq(t, 2, PowOf2(1)) | |
assertEq(t, 4, PowOf2(2)) | |
assertEq(t, 256, PowOf2(8)) | |
assertEq(t, 4096, PowOf2(12)) | |
} | |
func TestPowOf2Loop(t *testing.T) { | |
assertEq(t, 2, PowOf2Loop(1)) | |
assertEq(t, 4, PowOf2Loop(2)) | |
assertEq(t, 256, PowOf2Loop(8)) | |
assertEq(t, 4096, PowOf2Loop(12)) | |
} | |
func TestPowOf2Shift(t *testing.T) { | |
assertEq(t, 2, PowOf2Shift(1)) | |
assertEq(t, 4, PowOf2Shift(2)) | |
assertEq(t, 256, PowOf2Shift(8)) | |
assertEq(t, 4096, PowOf2Shift(12)) | |
} | |
func TestFibonacciRecursive(t *testing.T) { | |
assertEq(t, 0, FibonacciRecursive(0)) | |
assertEq(t, 1, FibonacciRecursive(1)) | |
assertEq(t, 1, FibonacciRecursive(2)) | |
assertEq(t, 144, FibonacciRecursive(12)) | |
} | |
func TestFibonacciMapCache(t *testing.T) { | |
assertEq(t, 0, FibonacciMapCache()(0)) | |
assertEq(t, 1, FibonacciMapCache()(1)) | |
assertEq(t, 1, FibonacciMapCache()(2)) | |
assertEq(t, 144, FibonacciMapCache()(12)) | |
} | |
func TestFibonacciSliceCache(t *testing.T) { | |
assertEq(t, 0, FibonacciSliceCache()(0)) | |
assertEq(t, 1, FibonacciSliceCache()(1)) | |
assertEq(t, 1, FibonacciSliceCache()(2)) | |
assertEq(t, 144, FibonacciSliceCache()(12)) | |
} | |
func TestFibonacciLoop(t *testing.T) { | |
assertEq(t, 0, FibonacciLoop(0)) | |
assertEq(t, 1, FibonacciLoop(1)) | |
assertEq(t, 1, FibonacciLoop(2)) | |
assertEq(t, 144, FibonacciLoop(12)) | |
} | |
func BenchmarkPowOf2(b *testing.B) { | |
ri := 0 | |
for i := 0; i < b.N; i++ { | |
if ri >= len(rnums) { | |
ri = 0 | |
} | |
n := rnums[ri] | |
ri++ | |
PowOf2(n) | |
} | |
} | |
func BenchmarkPowOf2Loop(b *testing.B) { | |
ri := 0 | |
for i := 0; i < b.N; i++ { | |
if ri >= len(rnums) { | |
ri = 0 | |
} | |
n := rnums[ri] | |
ri++ | |
PowOf2Loop(n) | |
} | |
} | |
func BenchmarkPowOf2Shift(b *testing.B) { | |
ri := 0 | |
for i := 0; i < b.N; i++ { | |
if ri >= len(rnums) { | |
ri = 0 | |
} | |
n := rnums[ri] | |
ri++ | |
PowOf2Shift(n) | |
} | |
} | |
func BenchmarkFibonacciRecursive(b *testing.B) { | |
fi := 0 | |
for i := 0; i < b.N; i++ { | |
if fi >= len(fnums) { | |
fi = 0 | |
} | |
n := fnums[fi] | |
fi++ | |
FibonacciRecursive(n) | |
} | |
} | |
func BenchmarkFibonacciMapCache(b *testing.B) { | |
fi := 0 | |
fib := FibonacciMapCache() | |
for i := 0; i < b.N; i++ { | |
if fi >= len(fnums) { | |
fi = 0 | |
} | |
n := fnums[fi] | |
fi++ | |
fib(n) | |
} | |
} | |
func BenchmarkFibonacciSliceCache(b *testing.B) { | |
fi := 0 | |
fib := FibonacciSliceCache() | |
for i := 0; i < b.N; i++ { | |
if fi >= len(fnums) { | |
fi = 0 | |
} | |
n := fnums[fi] | |
fi++ | |
fib(n) | |
} | |
} | |
func BenchmarkFibonacciLoop(b *testing.B) { | |
fi := 0 | |
for i := 0; i < b.N; i++ { | |
if fi >= len(fnums) { | |
fi = 0 | |
} | |
n := fnums[fi] | |
fi++ | |
FibonacciLoop(n) | |
} | |
} | |
const nlen = 100 | |
var rnums []uint | |
var fnums []uint | |
func init() { | |
rnums = generateRandomNums(nlen, 0) | |
fnums = generateRandomNums(nlen, 25) | |
} | |
func assertEq(t *testing.T, expected uint64, value uint64) { | |
if expected != value { | |
t.Fatalf("Expectation failed, got %d, expected %d", value, expected) | |
} | |
} | |
func generateRandomNums(amount uint, top uint) []uint { | |
s := make([]uint, amount) | |
for i := uint(0); i < amount; i++ { | |
if top > 0 { | |
s[i] = uint(rand.Int31n(int32(top))) | |
} else { | |
s[i] = uint(rand.Int31()) | |
} | |
} | |
return s | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
GET http://localhost:8000/fib?nums=568,7885,954,5461 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
GET http://localhost:8000/fibb?nums=568,7885,954,5461 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment