Skip to content

Instantly share code, notes, and snippets.

@ifraixedes
Last active April 19, 2022 17:30

Revisions

  1. ifraixedes renamed this gist Apr 19, 2022. 1 changed file with 0 additions and 0 deletions.
  2. ifraixedes revised this gist Apr 19, 2022. 2 changed files with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions _golang_bcn_intro_talk_pprof
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    This file exists to give to this gist a proper name.
    File renamed without changes.
  3. ifraixedes revised this gist Mar 13, 2016. 3 changed files with 4 additions and 4 deletions.
    4 changes: 2 additions & 2 deletions makefile
    Original file line number Diff line number Diff line change
    @@ -29,10 +29,10 @@ run-server: build-server
    ./server

    run-vegeta-fib:
    @vegeta attack -connections 100 -output vegeta-fib.out -targets vfib-target.txt
    @vegeta attack -connections 200 -output vegeta-fib.out -targets vfib-target.txt

    run-vegeta-fibb:
    @vegeta attack -connections 100 -output vegeta-fibb.out -targets vfibb-target.txt
    @vegeta attack -connections 200 -output vegeta-fibb.out -targets vfibb-target.txt

    deps:
    go get github.com/tsenart/vegeta
    2 changes: 1 addition & 1 deletion vfib-target.txt
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    GET http://localhost:8000/fib?nums=568,7845,54,614
    GET http://localhost:8000/fib?nums=568,7885,954,5461
    2 changes: 1 addition & 1 deletion vfibb-target.txt
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    GET http://localhost:8000/fibb?nums=568,7845,54,614
    GET http://localhost:8000/fibb?nums=568,7885,954,5461
  4. ifraixedes revised this gist Mar 13, 2016. 4 changed files with 34 additions and 7 deletions.
    2 changes: 2 additions & 0 deletions .gitignore
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    *.svg
    *.out
    *.test
    pbench
    server
    37 changes: 30 additions & 7 deletions makefile
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@

    .PHONY: test bench
    .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
    @@ -8,11 +8,34 @@ bench:
    @go test -bench . -benchmem *.go

    bench-profile:
    @go test -bench . -benchmem -benchtime 3s -memprofile mem.out -cpuprofile cpu.out
    @go test -o pbench -bench . -benchmem -benchtime 3s -outputdir pprof-out -memprofile mem.out -cpuprofile cpu.out

    profile-cpu:
    @go tool pprof -svg -output cpu.svg golangbcn.test cpu.out
    profile-cpu-graph:
    @go tool pprof -svg -output pprof-out/cpu.svg pbench pprof-out/cpu.out

    profile-mem:
    @go tool pprof -svg -alloc_space -output mem.svg golangbcn.test mem.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 100 -output vegeta-fib.out -targets vfib-target.txt

    run-vegeta-fibb:
    @vegeta attack -connections 100 -output vegeta-fibb.out -targets vfibb-target.txt

    deps:
    go get github.com/tsenart/vegeta

    pprof-out:
    mkdir pprof-out
    1 change: 1 addition & 0 deletions vfib-target.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    GET http://localhost:8000/fib?nums=568,7845,54,614
    1 change: 1 addition & 0 deletions vfibb-target.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    GET http://localhost:8000/fibb?nums=568,7845,54,614
  5. ifraixedes revised this gist Mar 13, 2016. 1 changed file with 114 additions and 0 deletions.
    114 changes: 114 additions & 0 deletions server.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,114 @@
    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"`
    }
  6. ifraixedes revised this gist Mar 12, 2016. 4 changed files with 296 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions .gitignore
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    *.svg
    *.out
    *.test
    18 changes: 18 additions & 0 deletions makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@

    .PHONY: test bench

    test:
    @go test *.go

    bench:
    @go test -bench . -benchmem *.go

    bench-profile:
    @go test -bench . -benchmem -benchtime 3s -memprofile mem.out -cpuprofile cpu.out

    profile-cpu:
    @go tool pprof -svg -output cpu.svg golangbcn.test cpu.out

    profile-mem:
    @go tool pprof -svg -alloc_space -output mem.svg golangbcn.test mem.out

    100 changes: 100 additions & 0 deletions service.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,100 @@
    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
    }
    175 changes: 175 additions & 0 deletions service_test.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,175 @@
    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
    }
  7. ifraixedes created this gist Mar 12, 2016.
    13 changes: 13 additions & 0 deletions LICENSE
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004
    (http://www.wtfpl.net/about/)

    Copyright (C) 2015 Ivan Fraixedes <ivan@fraixed.es> (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.