Fastest way to zero an array

What is the fastest way to set all array elements to '0'?

This is how I did it. Is there a faster way?

for i := range seg { seg[i] = 0 } 

You can try to redefine teh value sin the array. For example:

var a [2]int

// Because 0 is teh default value for an int
fmt.Println(a)	// [0 0]

// Let's assign some values to the array
a[0] = 10
a[1] = 20
fmt.Println(a)	// [10 20]

// Redefine array 
a = [2]int{0, 0} 
fmt.Println(a)	// [0 0]
1 Like

Your example is not very practical. Let’s increase the size of the array by a small amount to 1024*1024. Have you finished typing the list of more than a million zeros yet?

A more practical and more general example is:

var a [1024*1024]int
a = [len(a)]int{}

Your example, then becomes

package main

import "fmt"

func main() {
	var a [2]int

	// Because 0 is the default value for an int
	fmt.Println(a) // [0 0]

	// Let's assign some values to the array
	a[0] = 10
	a[1] = 20
	fmt.Println(a) // [10 20]

	// Redefine array
	a = [len(a)]int{}
	fmt.Println(a) // [0 0]
}
[0 0]
[10 20]
[0 0]
2 Likes

yes!!!

This won’t work in my case because compiler says seg isn’t constant array.

go-projects go build twinprimes_ssoz.go
# command-line-arguments
./twinprimes_ssoz.go:232:27: non-constant array bound len(seg)
➜  go-projects 

It’s created like this, and is zeroed inside a loop at its end.

seg := make([]uint64, ((kb - 1) >> s) + 1)

You asked about a way to zero an array. Therefore, you received an answer about a way to zero an array.

However, you appear to be confused. In Go, arrays and slices are distinct types.

The Go Programming Language Specification

Array types

Slice types

For slices, post a new question. Be specific and include a full, reproducible example.

1 Like

Is this what you are looking for?

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

No. That answer has already been posted by others in a more general form.

1 Like

I think its very clear from the code I posted what my intent is.
Is there a faster way to fill that slice with all zeroes than the way I showed?

Hi @jzakiya
I believe it’s a tricky question.
Execute the following script with different array size and in different machines and you will get different results :slight_smile:

package main

import (
	"fmt"
	"math"
	"runtime"
	"sync"
	"time"
)

func main() {
	var seg [1024 * 1024]int

	// Part 1
	fillArrayWithOnes(seg[:])
	runtime.Gosched()

	start := time.Now()
	initializeSeq(seg[:])
	elapsed := time.Now().Sub(start)

	fmt.Printf("Sequential took %v\n", elapsed)
	fmt.Println("ns:", int64(elapsed/time.Nanosecond))

	// Part 2
	fillArrayWithOnes(seg[:])
	runtime.Gosched()

	start = time.Now()
	cleanWg := new(sync.WaitGroup)
	numGoroutines := runtime.NumCPU()
	sliceSize := int(math.Ceil(float64(len(seg)) / float64(numGoroutines)))
	for i := 0; i < numGoroutines; i++ {
		start := i * sliceSize
		end := (i + 1) * sliceSize
		if end > len(seg) {
			end = len(seg)
		}
		partialKeys := seg[start:end]
		cleanWg.Add(1)
		go initialize(partialKeys, cleanWg)
	}
	cleanWg.Wait()
	elapsed = time.Now().Sub(start)

	fmt.Printf("Goroutines took %v\n", elapsed)
	fmt.Println("ns:", int64(elapsed/time.Nanosecond))

}

func fillArrayWithOnes(currentArray []int) {
	for i := range currentArray {
		currentArray[i] = 1
	}
}

func initialize(currentArray []int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := range currentArray {
		currentArray[i] = 0
	}
}

func initializeSeq(currentArray []int) {
	for i := range currentArray {
		currentArray[i] = 0
	}
}

Hi @jzakiya
Maybe this can help you

package main

import "fmt"

func main() {
    arr := []int{1, 2, 3, 4, 5, 7, 8, 9, 10}
    fmt.Println(arr)

    value := 0
    arrSize := len(arr)

    fillArray(arr, arrSize, value)

    fmt.Println(arr)
}

func fillArray(arr []int, arrSize, value int) {
    arr[0] = value

    for i := 1; i < arrSize; i *= 2 {
	    copy(arr[i:], arr[:i])
    } 
}

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

it is the most efficient way to fill an array that I know of. I think it is
O(long(n))

Hey thanks @guille-acosta.

I modified your method to work for unit64 types, for my array type, and simplified it.

func fillArray(arr []uint64, value uint64) {
  arr[0] = value
  
  for i := 1; i < len(arr); i *= 2 {
	  copy(arr[i:], arr[:i])
  }
}

However upon testing in my code, it doesn’t provide any measurable performance increase.

FYI, I asked this question in the Rust forum in March 2020, and just happened to ask again today.
They have actually now included a fill method in their std lib, which they are looking to optimize.

I’m guessing, to make you technique actually faster it has to be implemented at the assembly level too. Also, the Rust method works for various number types, so that’s something else to consider to make it a generic function for all array number types.

I really think adding an optimized fill method would be very beneficial for Go programmers too, just from the aesthetic benefits alone, to make code easier to read.

Here’s the discussion in the Rust forum today.

The rust fill function is most probably using the c memcpy function or the assembly equivalent which is the most efficient.

I assume the Go compiler is optimizing the following pattern into using the memcpy equivalent. This is a very frequent pattern that is certainly optimized because it is a trivial and obvious optimization.

a := make([]int64, 1024*1024)
for i := range a {
    a[i] = 0
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.