CodeBlanks Logo Icon CodeBlanks

Challenge 3 - Performance

Write an analysis finding a bottleneck with benchmark and pprof.

Challenge: cut allocations

You have a slow function that joins numbers with commas:

func yavas(n []int) string {
    s := ""
    for i, x := range n {
        if i > 0 { s += "," }
        s += strconv.Itoa(x)
    }
    return s
}

Every += allocates a new string. Your task: produce the same output with far fewer allocations using strings.Builder and strconv.AppendInt, and measure the difference with testing.AllocsPerRun.

Plan

Reserve capacity first: about 20 bytes per number is enough, buf := make([]byte, 0, len(n)*21). In the loop append the comma and strconv.AppendInt(buf, int64(x), 10); return string(buf) at the end. To measure, run both functions inside testing.AllocsPerRun(100, ...) and compare. Also check that the outputs are equal.

Practice Fast version

Write the yavas and hizli functions. For n := []int{1, 22, 333, 4444}, print on one line whether both outputs are equal and whether hizli allocates less than yavas.

Starter code

main.go
package main

import (
    "fmt"
    "strconv"
    "testing"
)

func main() {
    // buraya yaz
}

Sample solution

main.go
package main

import (
    "fmt"
    "strconv"
    "testing"
)

func yavas(n []int) string {
    s := ""
    for i, x := range n {
        if i > 0 {
            s += ","
        }
        s += strconv.Itoa(x)
    }
    return s
}

func hizli(n []int) string {
    buf := make([]byte, 0, len(n)*21)
    for i, x := range n {
        if i > 0 {
            buf = append(buf, ',')
        }
        buf = strconv.AppendInt(buf, int64(x), 10)
    }
    return string(buf)
}

var sink string

func main() {
    n := []int{1, 22, 333, 4444}
    a := testing.AllocsPerRun(100, func() { sink = yavas(n) })
    b := testing.AllocsPerRun(100, func() { sink = hizli(n) })
    fmt.Println(yavas(n) == hizli(n), b < a)
}

Expected output

true true

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What makes <code>strconv.AppendInt</code> better than <code>Itoa</code> here?

  • It runs concurrently
  • It writes the number into an existing byte slice without an intermediate string Correct answer
  • It's shorter to write
  • It also supports decimals

Explanation: Itoa allocates a new string per number; AppendInt appends to the given buffer and doesn't allocate if the buffer is big enough.

2 Append to the buffer

Question
buf = strconv.____(buf, int64(x), 10)
Answer
buf = strconv.AppendInt(buf, int64(x), 10)

Explanation: AppendInt appends the number's decimal form to an existing byte slice.

3 Method and cost

Term Meaning
s += ... A new string each step
strconv.Itoa One string per number
AppendInt Writes into a buffer
make(..., 0, cap) Capacity up front

All lessons in Advanced