Memory allocation question

70 views
Skip to first unread message

Mark Bauermeister

unread,
Jun 6, 2019, 6:48:46 AM6/6/19
to golang-nuts
Sorry in advance for the somewhat messy code, but this is something I've been trying to understand all day and can't quite come up with an explanation for.
Basically, I wrote the below code to experiment with different data types and their impact on performance and memory efficiency.

The code itself creates a map, an array, an empty slice and a slice already preallocated with the 1,000,000 required integers.

Each data type is being filled with numbers ranging from 0 to 999,999, then I output the execution time ("latency") and the memory usage ("Heap" and "Stack").

Now here is what I don't quite understand: Why would an array occupy more space on the heap and stack than a slice?

Is there an issue in my code or the way I measure heap/stack allocation?



experiment.png
























package main

import (
    "fmt"
    "sync"
    "time"

    "runtime"

)

var mu sync.Mutex
var mem runtime.MemStats

func main() {
    imap()
    iarray()
    ipreSlice()
    islice()
}

func _green(text string) {
    fmt.Fprintf(color.Output, color.GreenString(text))
    fmt.Println()
}

func imap() {
    _green("Map Implementation")
    cache := make(map[int]int, 1000000)
    start := time.Now()

    for i := 0; i < 1000000; i++ {
        mu.Lock()
        cache[i] = i
        mu.Unlock()
    }
    end := time.Since(start)
    fmt.Printf("Latency: %v \n", end)

    var sum int64
    for _, v := range cache {
        sum += int64(v)
    }

    fmt.Printf("Checksum: %v \n", sum)
    printHeap()
    printStack()
}

func iarray() {
    _green("Array Implementation")
    cache := [1000000]int{}
    start := time.Now()

    for i := 0; i < 1000000; i++ {
        mu.Lock()
        cache[i] = i
        mu.Unlock()
    }
    end := time.Since(start)
    fmt.Printf("Latency: %v \n", end)

    var sum int64
    for _, v := range cache {
        sum += int64(v)
    }

    fmt.Printf("Checksum: %v \n", sum)
    printHeap()
    printStack()
}

func ipreSlice() {
    _green("Preallocated Slice Implementation")
    cache := make([]int, 1000000)
    start := time.Now()

    for i := 0; i < 1000000; i++ {
        mu.Lock()
        cache[i] = i
        mu.Unlock()
    }
    end := time.Since(start)
    fmt.Printf("Latency: %v \n", end)

    var sum int64
    for _, v := range cache {
        sum += int64(v)
    }

    fmt.Printf("Checksum: %v \n", sum)
    printHeap()
    printStack()
}

func islice() {
    _green("Slice Implementation")
    cache := []int{}
    start := time.Now()

    for i := 0; i < 1000000; i++ {
        mu.Lock()
        cache = append(cache, i)
        mu.Unlock()
    }
    end := time.Since(start)
    fmt.Printf("Latency: %v \n", end)

    var sum int64
    for _, v := range cache {
        sum += int64(v)
    }

    fmt.Printf("Checksum: %v \n", sum)
    printHeap()
    printStack()
}

func printHeap() {
    runtime.ReadMemStats(&mem)
    fmt.Printf("Heap: %v MB", float64(mem.HeapInuse)/1024/1024)
    fmt.Println()
}

func printStack() {
    runtime.ReadMemStats(&mem)
    fmt.Printf("Stack: %v MB", float64(mem.StackInuse)/1024/1024)
    fmt.Println()
    fmt.Println()
}

fge...@gmail.com

unread,
Jun 6, 2019, 7:20:17 AM6/6/19
to Mark Bauermeister, golang-nuts
To share code please use Share on play.golang.org.
For the rest, please no screenshots, plain text is best.

On 6/6/19, Mark Bauermeister <warfa...@gmail.com> wrote:
> Sorry in advance for the somewhat messy code, but this is something I've
> been trying to understand all day and can't quite come up with an
> explanation for.
> Basically, I wrote the below code to experiment with different data types
> and their impact on performance and memory efficiency.
>
> The code itself creates a map, an array, an empty slice and a slice already
>
> preallocated with the 1,000,000 required integers.
>
> Each data type is being filled with numbers ranging from 0 to 999,999, then
>
> I output the execution time ("latency") and the memory usage ("Heap" and
> "Stack").
>
> Now here is what I don't quite understand: Why would an array occupy more
> space on the heap and stack than a slice?
>
> Is there an issue in my code or the way I measure heap/stack allocation?
>
>
>
> [image: experiment.png] <about:invalid#zClosurez>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/golang-nuts/07bc3bd3-8c9d-48d9-8a36-7e9e42d79637%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

Conrado P. L. Gouvêa

unread,
Jun 6, 2019, 7:44:41 AM6/6/19
to Mark Bauermeister, golang-nuts
The garbage collector won't necessarily run between the functions.

If you add "runtime.GC()" between functions you'll see that the Array Implementation won't use the heap very much.

There is still some differences, though:

https://play.golang.org/p/aYZAzoCzUM4

Map Implementation

Latency: 0s
Checksum: 499999500000
Heap: 20.578125 MB
Stack: 0.1875 MB

Array Implementation

Latency: 0s
Checksum: 499999500000
Heap: 0.1640625 MB
Stack: 8.15625 MB

Preallocated Slice Implementation

Latency: 0s
Checksum: 499999500000
Heap: 3.9765625 MB
Stack: 4.15625 MB

Slice Implementation

Latency: 0s
Checksum: 499999500000
Heap: 4.6015625 MB
Stack: 0.1875 MB

Conrado


Reply all
Reply to author
Forward
0 new messages