Friday, June 27, 2014

Make a set in Go

The Golang docs say:
A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
Here is such a set implementation:
package main

import (
    "log"
    "runtime"
)

func main() {
    const SIZE = 10000000
    set := make(map[int]bool, SIZE)
    for i := 0; i < SIZE; i++ {
        set[i] = true
    }
    memStats := runtime.MemStats{}
    runtime.ReadMemStats(&memStats)
    println("Used", memStats.Alloc, "bytes")

    // test the set
    for i := 0; i < SIZE; i++ {
        _, ok := set[i]
        if !ok {
            log.Panic("The set does not contain the expected value!")
        }
    }
    for i := SIZE; i < SIZE*2; i++ {
        _, ok := set[i]
        if ok {
            log.Panic("The set contains an unexpected value!")
        }
    }
}
The memory usage is:
$ go run sets.go
Used 195924120 bytes
It turns out you can make the values to consuming 0 bytes of memory if they will not of type bool, but struct{}:
package main

import (
    "log"
    "runtime"
)

func main() {
    const SIZE = 10000000
    set := make(map[int]struct{}, SIZE)
    for i := 0; i < SIZE; i++ {
        set[i] = struct{}{}
    }
    memStats := runtime.MemStats{}
    runtime.ReadMemStats(&memStats)
    println("Used", memStats.Alloc, "bytes")

    // test the set
    for i := 0; i < SIZE; i++ {
        _, ok := set[i]
        if !ok {
            log.Panic("The set does not contain the expected value!")
        }
    }
    for i := SIZE; i < SIZE*2; i++ {
        _, ok := set[i]
        if ok {
            log.Panic("The set contains an unexpected value!")
        }
    }
}
And you can see that the memory usage with a map of 10,000,000 integers dropped:
$ go run sets.go
Used 177338200 bytes
The difference is 18,585,920 bytes.

No comments:

Post a Comment