map [byte] byte {0:10}は、値用に1つ、キーごとに1つ、少なくとも2バイトを使用する必要があります。しかし、各ハッシュマップの実装として、アイテムごとの隠れたコストもあります。gccgoとgcの両方のGoマップのエントリごとのメモリオーバーヘッドはどれくらいですか?
5 に答える
Nickのプログラムのクロスプラットフォームの再実装は次のとおりです。欠陥があると思うところの変更も含まれています。また、より多くの測定データポイントを追加します。
注:より広い「エントリ」範囲を可能にするために、以下の測定マップはmap[int16]byte
です。
package main
import (
"fmt"
"runtime"
"unsafe"
)
func Alloc() uint64 {
var stats runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&stats)
return stats.Alloc - uint64(unsafe.Sizeof(hs[0]))*uint64(cap(hs))
}
var hs = []*map[int16]byte{}
func main() {
hs := []*map[int16]byte{}
n := 1000
before := Alloc()
for i := 0; i < n; i++ {
h := map[int16]byte{}
hs = append(hs, &h)
}
after := Alloc()
emptyPerMap := float64(after-before) / float64(n)
fmt.Printf("Bytes used for %d empty maps: %d, bytes/map %.1f\n", n, after-before, emptyPerMap)
hs = nil
k := 1
for p := 1; p < 16; p++ {
before = Alloc()
for i := 0; i < n; i++ {
h := map[int16]byte{}
for j := 0; j < k; j++ {
h[int16(j)] = byte(j)
}
hs = append(hs, &h)
}
after = Alloc()
fullPerMap := float64(after-before) / float64(n)
fmt.Printf("Bytes used for %d maps with %d entries: %d, bytes/map %.1f\n", n, k, after-before, fullPerMap)
fmt.Printf("Bytes per entry %.1f\n", (fullPerMap-emptyPerMap)/float64(k))
k *= 2
}
}
出力
jnml@fsc-r630:~/src/tmp$ go build && ./tmp && go version && uname -a
Bytes used for 1000 empty maps: 146816, bytes/map 146.8
Bytes used for 1000 maps with 1 entries: 147040, bytes/map 147.0
Bytes per entry 0.2
Bytes used for 1000 maps with 2 entries: 147040, bytes/map 147.0
Bytes per entry 0.1
Bytes used for 1000 maps with 4 entries: 247136, bytes/map 247.1
Bytes per entry 25.1
Bytes used for 1000 maps with 8 entries: 439056, bytes/map 439.1
Bytes per entry 36.5
Bytes used for 1000 maps with 16 entries: 818688, bytes/map 818.7
Bytes per entry 42.0
Bytes used for 1000 maps with 32 entries: 1194688, bytes/map 1194.7
Bytes per entry 32.7
Bytes used for 1000 maps with 64 entries: 2102976, bytes/map 2103.0
Bytes per entry 30.6
Bytes used for 1000 maps with 128 entries: 4155072, bytes/map 4155.1
Bytes per entry 31.3
Bytes used for 1000 maps with 256 entries: 6698688, bytes/map 6698.7
Bytes per entry 25.6
Bytes used for 1000 maps with 512 entries: 14142976, bytes/map 14143.0
Bytes per entry 27.3
Bytes used for 1000 maps with 1024 entries: 51349184, bytes/map 51349.2
Bytes per entry 50.0
Bytes used for 1000 maps with 2048 entries: 102467264, bytes/map 102467.3
Bytes per entry 50.0
Bytes used for 1000 maps with 4096 entries: 157214816, bytes/map 157214.8
Bytes per entry 38.3
Bytes used for 1000 maps with 8192 entries: 407031200, bytes/map 407031.2
Bytes per entry 49.7
Bytes used for 1000 maps with 16384 entries: 782616864, bytes/map 782616.9
Bytes per entry 47.8
go version devel +83b0b94af636 Sat Mar 09 16:25:30 2013 +1100 linux/amd64
Linux fsc-r630 3.2.0-38-generic #61-Ubuntu SMP Tue Feb 19 12:18:21 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
jnml@fsc-r630:~/src/tmp$
数値が(約4倍)良くなっているのを見るのはいいことです。リリースバージョン(1.0.3)の数値は、わずかに高くなっています。
jnml@fsc-r630:~/src/tmp$ go build && ./tmp
Bytes used for 1000 empty maps: 144192, bytes/map 144.2
Bytes used for 1000 maps with 1 entries: 144192, bytes/map 144.2
Bytes per entry 0.0
Bytes used for 1000 maps with 2 entries: 144192, bytes/map 144.2
Bytes per entry 0.0
Bytes used for 1000 maps with 4 entries: 315648, bytes/map 315.6
Bytes per entry 42.9
Bytes used for 1000 maps with 8 entries: 436288, bytes/map 436.3
Bytes per entry 36.5
Bytes used for 1000 maps with 16 entries: 885824, bytes/map 885.8
Bytes per entry 46.4
Bytes used for 1000 maps with 32 entries: 1331264, bytes/map 1331.3
Bytes per entry 37.1
Bytes used for 1000 maps with 64 entries: 2292800, bytes/map 2292.8
Bytes per entry 33.6
Bytes used for 1000 maps with 128 entries: 4935920, bytes/map 4935.9
Bytes per entry 37.4
Bytes used for 1000 maps with 256 entries: 12164160, bytes/map 12164.2
Bytes per entry 47.0
Bytes used for 1000 maps with 512 entries: 29887808, bytes/map 29887.8
Bytes per entry 58.1
Bytes used for 1000 maps with 1024 entries: 56840768, bytes/map 56840.8
Bytes per entry 55.4
Bytes used for 1000 maps with 2048 entries: 108736064, bytes/map 108736.1
Bytes per entry 53.0
Bytes used for 1000 maps with 4096 entries: 184368752, bytes/map 184368.8
Bytes per entry 45.0
Bytes used for 1000 maps with 8192 entries: 431340576, bytes/map 431340.6
Bytes per entry 52.6
Bytes used for 1000 maps with 16384 entries: 815378816, bytes/map 815378.8
Bytes per entry 49.8
jnml@fsc-r630:~/src/tmp$
バッファが関係しているようで、必要な場合にのみ成長します。gccgo についてはわかりませんが、遊び場で試してみました。基本的に、空のマップに 128 バイトを割り当て、必要に応じて拡張します。
ここで見ることができます:http://play.golang.org/p/RjohbSOq0x
これは、マップのオーバーヘッドを測定するための実験です。Linux でのみ動作します。
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"runtime"
"strconv"
"strings"
)
func ReadRss() int {
data, err := ioutil.ReadFile("/proc/self/statm")
if err != nil {
log.Fatal(err)
}
rss, err := strconv.Atoi(strings.Fields(string(data))[1])
if err != nil {
log.Fatal(err)
}
return rss * os.Getpagesize()
}
func main() {
hs := []*map[byte]byte{}
before := ReadRss()
n := 10000
for i := 0; i < n; i++ {
h := map[byte]byte{}
hs = append(hs, &h)
}
after := ReadRss()
empty_per_map := float64(after-before)/float64(n)
fmt.Printf("Bytes used for %d empty maps: %d, bytes/map %.1f\n", n, after-before, empty_per_map)
hs = nil
runtime.GC()
before = ReadRss()
for i := 0; i < n; i++ {
h := map[byte]byte{}
for j := byte(0); j < 100; j++ {
h[j] = j
}
hs = append(hs, &h)
}
after = ReadRss()
full_per_map := float64(after-before)/float64(n)
fmt.Printf("Bytes used for %d maps with 100 entries: %d, bytes/map %.1f\n", n, after-before, full_per_map)
fmt.Printf("Bytes per entry %.1f\n", (full_per_map - empty_per_map)/100)
}
これは、go 1.0.3 を使用して私の 64 ビット Linux マシンに出力されます。
Bytes used for 10000 empty maps: 1695744, bytes/map 169.6
Bytes used for 10000 maps with 100 entries: 43876352, bytes/map 4387.6
Bytes per entry 42.2
またはgo 1.0を使用
Bytes used for 10000 empty maps: 1388544, bytes/map 138.9
Bytes used for 10000 maps with 100 entries: 199323648, bytes/map 19932.4
Bytes per entry 197.9
メモリの測定は、Go の内部メモリ統計ではなく、Linux OS を使用して行われます。メモリ測定値は 4k ページで返されるため粗いため、多数のマップが作成されます。
したがって、ラウンド数では、各マップのコストは約 170 バイトで、各エントリのコストは go 1.0.3 を使用して 42 バイトです (1.0 の場合はさらに多くなります)。