Skip to content

Instantly share code, notes, and snippets.

@ifraixedes
Last active April 19, 2022 17:30
Show Gist options
  • Save ifraixedes/fd8b585c3da6dacb6385 to your computer and use it in GitHub Desktop.
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 exists to give to this gist a proper name.
*.svg
*.out
*.test
pbench
server
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.
.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
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"`
}
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
}
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
}
GET http://localhost:8000/fib?nums=568,7885,954,5461
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