Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

golang slice allocation performance

I stumbled upon an interesting thing while checking performance of memory allocation in GO.

package main

import (
      "fmt"
      "time"
    )

func main(){
   const alloc int = 65536
   now := time.Now()
   loop := 50000
   for i := 0; i<loop;i++{
      sl := make([]byte, alloc)
      i += len(sl) * 0
   }
   elpased := time.Since(now)
   fmt.Printf("took %s to allocate %d bytes %d times", elpased, alloc, loop) 
}

I am running this on a Core-i7 2600 with go version 1.6 64bit (also same results on 32bit) and 16GB of RAM (on WINDOWS 10) so when alloc is 65536 (exactly 64K) it runs for 30 seconds (!!!!). When alloc is 65535 it takes ~200ms. Can someone explain this to me please? I tried the same code at home with my core i7-920 @ 3.8GHZ but it didn't show same results (both took around 200ms). Anyone has an idea what's going on?

like image 218
J. Dow Avatar asked Mar 21 '16 08:03

J. Dow


People also ask

Are arrays faster than slices Golang?

The performance of arrays is better than slices when accessing a single element. The length of the array is part of the type. It has a certain meaning in a specific scenario.

How is Golang memory efficient?

Only the values stored in the memory managed by the stack are changed. This makes the process of storing and retrieving data from the stack very fast since there is no lookup required. We can just store and retrieve data from the top most block on it. Any data that is stored on the stack has to be finite and static.

How do I dynamically allocate memory in Golang?

In Go dynamic memory block is allocated mainly using new and make. New allocates exact one memory block that is used to create struct type value, whereas, make creates more than one memory block and returns the reference, like a slice, map or channel value.

What is difference between Array and slice in Golang?

Slices in Go and Golang The basic difference between a slice and an array is that a slice is a reference to a contiguous segment of an array. Unlike an array, which is a value-type, slice is a reference type. A slice can be a complete array or a part of an array, indicated by the start and end index.


2 Answers

Setting GOGC=off improved performance (down to less than 100ms). Why? becaue of escape analysis. When you build with go build -gcflags -m the compiler prints whatever allocations escapes to heap. It really depends on your machine and GO compiler version but when the compiler decides that the allocation should move to heap it means 2 things: 1. the allocation will take longer (since "allocating" on the stack is just 1 cpu instruction) 2. the GC will have to clean up that memory later - costing more CPU time for my machine, the allocation of 65536 bytes escapes to heap and 65535 doesn't. that's why 1 bytes changed the whole proccess from 200ms to 30s. Amazing..

like image 73
J. Dow Avatar answered Oct 21 '22 15:10

J. Dow


Note/Update 2021: as Tapir Liui notes in Go101 with this tweet:

As of Go 1.17, Go runtime will allocate the elements of slice x on stack if the compiler proves they are only used in the current goroutine and N <= 64KB:

var x = make([]byte, N)

And Go runtime will allocate the array y on stack if the compiler proves it is only used in the current goroutine and N <= 10MB:

var y [N]byte

Then how to allocated (the elements of) a slice which size is larger than 64KB but not larger than 10MB on stack (and the slice is only used in one goroutine)?

Just use the following way:

var y [N]byte
var x = y[:]

Considering stack allocation is faster than heap allocation, that would have a direct effect on your test, for alloc equals to 65536 and more.

Tapir adds:

In fact, we could allocate slices with arbitrary sum element sizes on stack.

const N = 500 * 1024 * 1024 // 500M
var v byte = 123

func createSlice() byte {
 var s = []byte{N: 0}
 for i := range s { s[i] = v }
 return s[v]
}

Changing 500 to 512 make program crash.

like image 22
VonC Avatar answered Oct 21 '22 17:10

VonC