[golang] map で delete したとき、次回GCでデータは解放されるが、バケットは解放されない

# 目的
map の キーと値のペアのデータを削除して、次回GCでその部分のデータが解放されたとしても、バケットの個数は減らないのでメモリを占有し続けることを理解する。
そして、その対策方法を知る。

# 結論
map の要素が増えて、一時的にバケット数が増えたが、しばらくして要素数が減少したとき、バケット数は減りません。この問題に対処する方法は以下の二つです。

- map を作り直す。
- map の値として使いたいデータが128バイト以下の時は、明示的にポインタを使用する。

# 考察
https://www.amazon.co.jp/-/en/Teiva-Harsanyi/dp/4295017531
> もう一つの解決策は、map型が配列へのポインタを保持するように変更することです。つまり、`map[int]*[128]byte`とすることです。

> 注記: キーや値が128バイトを超える場合、Goはそれらをマップのバケットに直接保存しません。その代わり、キーや値を参照するためのポインタを保存します。

下記の検証を見てもらうとわかる通り、129バイト以上のデータ(今回は固定長配列)をmapの値として使うと、内部で自動的に、それのポインタがmapのバケットに保存される仕組みとなっています。
なので、自分から明示的に、129バイト以上のデータのポインタをmapの値として使うことは全く意味がありません。

実際に検証結果を見てみましょう。

(1-1)の合計バケットサイズは「39215-47=39168」バイトで、(1-2)の合計バケットサイズも「39215-47=39168」バイトであることがわかります。これは、mapの値として明示的にポインタを使うことに全く意味がないということです。Goのmapが勝手にやってくれるので、わざわざ自分でやる必要はありません。

一方で、128バイト以下のデータのポインタをmapの値として使うことには意味があります。(2-1)のバケットサイズは「300335-47=300288」バイトで、一方で、(2-2)の合計バケットサイズは「39215-47=39168」バイトであることがわかります。後者のバケットサイズの方が小さいです。前者は、データがそのままバケットに保存されていたので、そのデータの分だけ合計バケットサイズが肥大化しています。mapの要素を削除しても、合計バケットサイズが減らないので、GCでは解放されず、参照が消えるまでメモリを占有し続けます。

また、(1-1), (1-2), (2-2)の合計バケットサイズは同じであることがわかります。これは当然の結果です。なぜなら、mapの要素数が同じで、キーが同じで、値のサイズは8バイト(ポインタなので)で同じなので、必要な合計バケットサイズも同じはずだからです。
ちなみにですが、キーの型が一緒でも、キーが異なる場合は、わずかに合計バケットサイズが異なる可能性はあると思います。なぜなら、キーのハッシュによってどのバケットに振り分けられるかが決まるので、mapごとにキーが違えば、ロードファクター(一つのバケットが保持する要素の平均個数)が異なる、結果として、バケットの個数も違ってくるからです。


# 検証
```
~/praprago$ go version
go version go1.23.2 linux/amd64
```

## (1-1) 128個の要素の固定長配列をmapの値として使う
``` main.go
package main

import (
	"fmt"
	"runtime"
)

// go run .
func main() {
	CallpraMap1()

	fmt.Println("CCCCCCCCCC_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()
}

```
``` pra_slice_memory.go
package main

import (
	"fmt"
	"runtime"
)

func PrintAlloc() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	println("Alloc:", m.Alloc/1024, "KB")
}

// //////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////// (1)
// map の lookup の計算量は O((Load Factor)/2) である。
// Load Factor は一つのバケットに配置されているキーと値のペアの数の平均値である。
// Load Factor が 6.5 を超えると、新しいバケットを作成し、全てのキーと値を再配置する。
// この再配置にはそれなりにコストがかかるため、スライスと理屈により、map を作成する際、最終的な要素数がわかっている場合は、定義時に指定することが望ましい。
// バケットを lookup する計算量は、ハッシュ値により O(1) である。
// 再配置などの時間を無視すれば、値の lookup は O(6.5/2) の計算量よりも大きくなることはない。
//
// map のバケット数は増加するが減少することはないので、キーを削除したとき、次のGCでキーと値のデータは解放されるが、バケットは解放されない。
// なので、バケットの個数が減らないことによるメモリ占有が気になる場合、工夫が必要である。
// 例えば、map を再作成して、バケットが増えてしまった map を使わないようにする方法がある。
// また、別の方法として、値にデータを使わずに、データへのポインタを使うことで、バケット一個あたりのメモリ使用量を減らすことができる。
//
//go:noinline
func praMap1() map[int][129]byte {
	fmt.Println("BBBBBBBBBBB_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	myMap := make(map[int][129]byte, 1000_000)
	for i := 0; i < 1000_000; i++ {
		myMap[i] = [129]byte{byte(1), byte(2), byte(3), byte(4), byte(5), byte(6), byte(7), byte(8)} // 残りの要素はゼロ値(byte(0))で埋められる。
	}

	if value, ok := myMap[100]; ok {
		fmt.Printf("myMap[100]: %v\n", value)
	}

	fmt.Println("BBBBBBBBBBB_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	// 全てのキーを削除する。
	for k := range myMap {
		delete(myMap, k)
	}

	fmt.Println("BBBBBBBBBBB_333444")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	return myMap
}

func CallpraMap1() {
	mymap := praMap1()

	fmt.Println("AAAAAAAAAA_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	if value, ok := mymap[100]; ok {
		fmt.Printf("mymap[100]: %v\n", value)
	}

	// これ以降 map への参照はないので、次回のGCでバケットも含めて解放される。

	fmt.Println("AAAAAAAAAA_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()
}

```
```
~/praprago$ go run .
BBBBBBBBBBB_111222
Alloc: 46 KB
Alloc: 46 KB
myMap[100]: [1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
BBBBBBBBBBB_222333
Alloc: 179841 KB
Alloc: 179840 KB
BBBBBBBBBBB_333444
Alloc: 179841 KB
Alloc: 39215 KB
AAAAAAAAAA_111222
Alloc: 39215 KB
Alloc: 39215 KB
AAAAAAAAAA_222333
Alloc: 39215 KB
Alloc: 47 KB
CCCCCCCCCC_111222
Alloc: 47 KB
Alloc: 47 KB
```
```
~/praprago$ go build -gcflags "-m=2" .
# github.com/XXX/praprago
./pra_slice_memory.go:8:6: can inline PrintAlloc with cost 72 as: func() { m = <nil>; runtime.ReadMemStats(&m); println("Alloc:", m.Alloc / uint64(1024), "KB") }
./pra_slice_memory.go:29:6: cannot inline praMap1: marked go:noinline
./pra_slice_memory.go:62:6: cannot inline CallpraMap1: function too complex: cost 725 exceeds budget 80
./main.go:9:6: cannot inline main: function too complex: cost 344 exceeds budget 80
./main.go:12:13: inlining call to fmt.Println
./main.go:13:12: inlining call to PrintAlloc
./main.go:15:12: inlining call to PrintAlloc
./pra_slice_memory.go:30:13: inlining call to fmt.Println
./pra_slice_memory.go:31:12: inlining call to PrintAlloc
./pra_slice_memory.go:33:12: inlining call to PrintAlloc
./pra_slice_memory.go:41:13: inlining call to fmt.Printf
./pra_slice_memory.go:44:13: inlining call to fmt.Println
./pra_slice_memory.go:45:12: inlining call to PrintAlloc
./pra_slice_memory.go:47:12: inlining call to PrintAlloc
./pra_slice_memory.go:54:13: inlining call to fmt.Println
./pra_slice_memory.go:55:12: inlining call to PrintAlloc
./pra_slice_memory.go:57:12: inlining call to PrintAlloc
./pra_slice_memory.go:65:13: inlining call to fmt.Println
./pra_slice_memory.go:66:12: inlining call to PrintAlloc
./pra_slice_memory.go:68:12: inlining call to PrintAlloc
./pra_slice_memory.go:71:13: inlining call to fmt.Printf
./pra_slice_memory.go:76:13: inlining call to fmt.Println
./pra_slice_memory.go:77:12: inlining call to PrintAlloc
./pra_slice_memory.go:79:12: inlining call to PrintAlloc
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap:
./pra_slice_memory.go:54:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_333444"}:
./pra_slice_memory.go:54:14:     from "BBBBBBBBBBB_333444" (spill) at ./pra_slice_memory.go:54:14
./pra_slice_memory.go:54:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:54:14:     from ... argument (spill) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:54:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap:
./pra_slice_memory.go:44:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_222333"}:
./pra_slice_memory.go:44:14:     from "BBBBBBBBBBB_222333" (spill) at ./pra_slice_memory.go:44:14
./pra_slice_memory.go:44:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:44:14:     from ... argument (spill) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:44:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:41:34: value escapes to heap:
./pra_slice_memory.go:41:34:   flow: {storage for ... argument} = &{storage for value}:
./pra_slice_memory.go:41:34:     from value (spill) at ./pra_slice_memory.go:41:34
./pra_slice_memory.go:41:34:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:41:34:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:41:34:     from ... argument (spill) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:41:34:     from fmt.format, fmt.a := "myMap[100]: %v\n", ... argument (assign-pair) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:41:34:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:41:34:     from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap:
./pra_slice_memory.go:30:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_111222"}:
./pra_slice_memory.go:30:14:     from "BBBBBBBBBBB_111222" (spill) at ./pra_slice_memory.go:30:14
./pra_slice_memory.go:30:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:30:14:     from ... argument (spill) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:30:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:35:15: make(map[int][129]byte, 1000000) escapes to heap:
./pra_slice_memory.go:35:15:   flow: myMap = &{storage for make(map[int][129]byte, 1000000)}:
./pra_slice_memory.go:35:15:     from make(map[int][129]byte, 1000000) (spill) at ./pra_slice_memory.go:35:15
./pra_slice_memory.go:35:15:     from myMap := make(map[int][129]byte, 1000000) (assign) at ./pra_slice_memory.go:35:8
./pra_slice_memory.go:35:15:   flow: ~r0 = myMap:
./pra_slice_memory.go:35:15:     from return myMap (return) at ./pra_slice_memory.go:59:2
./pra_slice_memory.go:30:13: ... argument does not escape
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap
./pra_slice_memory.go:35:15: make(map[int][129]byte, 1000000) escapes to heap
./pra_slice_memory.go:41:13: ... argument does not escape
./pra_slice_memory.go:41:34: value escapes to heap
./pra_slice_memory.go:44:13: ... argument does not escape
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap
./pra_slice_memory.go:54:13: ... argument does not escape
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap:
./pra_slice_memory.go:76:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_222333"}:
./pra_slice_memory.go:76:14:     from "AAAAAAAAAA_222333" (spill) at ./pra_slice_memory.go:76:14
./pra_slice_memory.go:76:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:76:14:     from ... argument (spill) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:76:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:71:34: value escapes to heap:
./pra_slice_memory.go:71:34:   flow: {storage for ... argument} = &{storage for value}:
./pra_slice_memory.go:71:34:     from value (spill) at ./pra_slice_memory.go:71:34
./pra_slice_memory.go:71:34:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:71:34:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:71:34:     from ... argument (spill) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:71:34:     from fmt.format, fmt.a := "mymap[100]: %v\n", ... argument (assign-pair) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:71:34:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:71:34:     from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap:
./pra_slice_memory.go:65:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_111222"}:
./pra_slice_memory.go:65:14:     from "AAAAAAAAAA_111222" (spill) at ./pra_slice_memory.go:65:14
./pra_slice_memory.go:65:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:65:14:     from ... argument (spill) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:65:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:13: ... argument does not escape
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap
./pra_slice_memory.go:71:13: ... argument does not escape
./pra_slice_memory.go:71:34: value escapes to heap
./pra_slice_memory.go:76:13: ... argument does not escape
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap:
./main.go:12:14:   flow: {storage for ... argument} = &{storage for "CCCCCCCCCC_111222"}:
./main.go:12:14:     from "CCCCCCCCCC_111222" (spill) at ./main.go:12:14
./main.go:12:14:     from ... argument (slice-literal-element) at ./main.go:12:13
./main.go:12:14:   flow: fmt.a = &{storage for ... argument}:
./main.go:12:14:     from ... argument (spill) at ./main.go:12:13
./main.go:12:14:     from fmt.a := ... argument (assign-pair) at ./main.go:12:13
./main.go:12:14:   flow: {heap} = *fmt.a:
./main.go:12:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:12:13
./main.go:12:13: ... argument does not escape
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap
```

## (1-2) 128個の要素の固定長配列のポインタをmapの値として使う
``` pra_slice_memory.go
package main

import (
	"fmt"
	"runtime"
)

func PrintAlloc() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	println("Alloc:", m.Alloc/1024, "KB")
}

// //////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////// (1)
// map の lookup の計算量は O((Load Factor)/2) である。
// Load Factor は一つのバケットに配置されているキーと値のペアの数の平均値である。
// Load Factor が 6.5 を超えると、新しいバケットを作成し、全てのキーと値を再配置する。
// この再配置にはそれなりにコストがかかるため、スライスと理屈により、map を作成する際、最終的な要素数がわかっている場合は、定義時に指定することが望ましい。
// バケットを lookup する計算量は、ハッシュ値により O(1) である。
// 再配置などの時間を無視すれば、値の lookup は O(6.5/2) の計算量よりも大きくなることはない。
//
// map のバケット数は増加するが減少することはないので、キーを削除したとき、次のGCでキーと値のデータは解放されるが、バケットは解放されない。
// なので、バケットの個数が減らないことによるメモリ占有が気になる場合、工夫が必要である。
// 例えば、map を再作成して、バケットが増えてしまった map を使わないようにする方法がある。
// また、別の方法として、値にデータを使わずに、データへのポインタを使うことで、バケット一個あたりのメモリ使用量を減らすことができる。
//
//go:noinline
func praMap1() map[int]*[129]byte {
	fmt.Println("BBBBBBBBBBB_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	myMap := make(map[int]*[129]byte, 1000_000)
	for i := 0; i < 1000_000; i++ {
		myMap[i] = &[129]byte{byte(1), byte(2), byte(3), byte(4), byte(5), byte(6), byte(7), byte(8)} // 残りの要素はゼロ値(byte(0))で埋められる。
	}

	if value, ok := myMap[100]; ok {
		fmt.Printf("myMap[100]: %v\n", value)
	}

	fmt.Println("BBBBBBBBBBB_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	// 全てのキーを削除する。
	for k := range myMap {
		delete(myMap, k)
	}

	fmt.Println("BBBBBBBBBBB_333444")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	return myMap
}

func CallpraMap1() {
	mymap := praMap1()

	fmt.Println("AAAAAAAAAA_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	if value, ok := mymap[100]; ok {
		fmt.Printf("mymap[100]: %v\n", value)
	}

	// これ以降 map への参照はないので、次回のGCでバケットも含めて解放される。

	fmt.Println("AAAAAAAAAA_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()
}

```
```
~/praprago$ go run .
BBBBBBBBBBB_111222
Alloc: 46 KB
Alloc: 47 KB
myMap[100]: &[1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
BBBBBBBBBBB_222333
Alloc: 179841 KB
Alloc: 179840 KB
BBBBBBBBBBB_333444
Alloc: 179841 KB
Alloc: 39215 KB
AAAAAAAAAA_111222
Alloc: 39215 KB
Alloc: 39215 KB
AAAAAAAAAA_222333
Alloc: 39215 KB
Alloc: 47 KB
CCCCCCCCCC_111222
Alloc: 47 KB
Alloc: 47 KB
```
```
~/praprago$ go build -gcflags "-m=2" .
# github.com/XXX/praprago
./pra_slice_memory.go:8:6: can inline PrintAlloc with cost 72 as: func() { m = <nil>; runtime.ReadMemStats(&m); println("Alloc:", m.Alloc / uint64(1024), "KB") }
./pra_slice_memory.go:29:6: cannot inline praMap1: marked go:noinline
./pra_slice_memory.go:62:6: cannot inline CallpraMap1: function too complex: cost 725 exceeds budget 80
./main.go:9:6: cannot inline main: function too complex: cost 344 exceeds budget 80
./main.go:12:13: inlining call to fmt.Println
./main.go:13:12: inlining call to PrintAlloc
./main.go:15:12: inlining call to PrintAlloc
./pra_slice_memory.go:30:13: inlining call to fmt.Println
./pra_slice_memory.go:31:12: inlining call to PrintAlloc
./pra_slice_memory.go:33:12: inlining call to PrintAlloc
./pra_slice_memory.go:41:13: inlining call to fmt.Printf
./pra_slice_memory.go:44:13: inlining call to fmt.Println
./pra_slice_memory.go:45:12: inlining call to PrintAlloc
./pra_slice_memory.go:47:12: inlining call to PrintAlloc
./pra_slice_memory.go:54:13: inlining call to fmt.Println
./pra_slice_memory.go:55:12: inlining call to PrintAlloc
./pra_slice_memory.go:57:12: inlining call to PrintAlloc
./pra_slice_memory.go:65:13: inlining call to fmt.Println
./pra_slice_memory.go:66:12: inlining call to PrintAlloc
./pra_slice_memory.go:68:12: inlining call to PrintAlloc
./pra_slice_memory.go:71:13: inlining call to fmt.Printf
./pra_slice_memory.go:76:13: inlining call to fmt.Println
./pra_slice_memory.go:77:12: inlining call to PrintAlloc
./pra_slice_memory.go:79:12: inlining call to PrintAlloc
./pra_slice_memory.go:37:14: &[129]byte{...} escapes to heap:
./pra_slice_memory.go:37:14:   flow: {heap} = &{storage for &[129]byte{...}}:
./pra_slice_memory.go:37:14:     from &[129]byte{...} (spill) at ./pra_slice_memory.go:37:14
./pra_slice_memory.go:37:14:     from myMap[i] = &[129]byte{...} (assign) at ./pra_slice_memory.go:37:12
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap:
./pra_slice_memory.go:54:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_333444"}:
./pra_slice_memory.go:54:14:     from "BBBBBBBBBBB_333444" (spill) at ./pra_slice_memory.go:54:14
./pra_slice_memory.go:54:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:54:14:     from ... argument (spill) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:54:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap:
./pra_slice_memory.go:44:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_222333"}:
./pra_slice_memory.go:44:14:     from "BBBBBBBBBBB_222333" (spill) at ./pra_slice_memory.go:44:14
./pra_slice_memory.go:44:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:44:14:     from ... argument (spill) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:44:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap:
./pra_slice_memory.go:30:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_111222"}:
./pra_slice_memory.go:30:14:     from "BBBBBBBBBBB_111222" (spill) at ./pra_slice_memory.go:30:14
./pra_slice_memory.go:30:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:30:14:     from ... argument (spill) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:30:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:35:15: make(map[int]*[129]byte, 1000000) escapes to heap:
./pra_slice_memory.go:35:15:   flow: myMap = &{storage for make(map[int]*[129]byte, 1000000)}:
./pra_slice_memory.go:35:15:     from make(map[int]*[129]byte, 1000000) (spill) at ./pra_slice_memory.go:35:15
./pra_slice_memory.go:35:15:     from myMap := make(map[int]*[129]byte, 1000000) (assign) at ./pra_slice_memory.go:35:8
./pra_slice_memory.go:35:15:   flow: ~r0 = myMap:
./pra_slice_memory.go:35:15:     from return myMap (return) at ./pra_slice_memory.go:59:2
./pra_slice_memory.go:30:13: ... argument does not escape
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap
./pra_slice_memory.go:35:15: make(map[int]*[129]byte, 1000000) escapes to heap
./pra_slice_memory.go:37:14: &[129]byte{...} escapes to heap
./pra_slice_memory.go:41:13: ... argument does not escape
./pra_slice_memory.go:44:13: ... argument does not escape
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap
./pra_slice_memory.go:54:13: ... argument does not escape
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap:
./pra_slice_memory.go:76:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_222333"}:
./pra_slice_memory.go:76:14:     from "AAAAAAAAAA_222333" (spill) at ./pra_slice_memory.go:76:14
./pra_slice_memory.go:76:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:76:14:     from ... argument (spill) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:76:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap:
./pra_slice_memory.go:65:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_111222"}:
./pra_slice_memory.go:65:14:     from "AAAAAAAAAA_111222" (spill) at ./pra_slice_memory.go:65:14
./pra_slice_memory.go:65:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:65:14:     from ... argument (spill) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:65:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:13: ... argument does not escape
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap
./pra_slice_memory.go:71:13: ... argument does not escape
./pra_slice_memory.go:76:13: ... argument does not escape
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap:
./main.go:12:14:   flow: {storage for ... argument} = &{storage for "CCCCCCCCCC_111222"}:
./main.go:12:14:     from "CCCCCCCCCC_111222" (spill) at ./main.go:12:14
./main.go:12:14:     from ... argument (slice-literal-element) at ./main.go:12:13
./main.go:12:14:   flow: fmt.a = &{storage for ... argument}:
./main.go:12:14:     from ... argument (spill) at ./main.go:12:13
./main.go:12:14:     from fmt.a := ... argument (assign-pair) at ./main.go:12:13
./main.go:12:14:   flow: {heap} = *fmt.a:
./main.go:12:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:12:13
./main.go:12:13: ... argument does not escape
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap
```


## (2-1) 129個の要素の固定長配列をmapの値として使う

``` pra_slice_memory.go
package main

import (
	"fmt"
	"runtime"
)

func PrintAlloc() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	println("Alloc:", m.Alloc/1024, "KB")
}

// //////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////// (1)
// map の lookup の計算量は O((Load Factor)/2) である。
// Load Factor は一つのバケットに配置されているキーと値のペアの数の平均値である。
// Load Factor が 6.5 を超えると、新しいバケットを作成し、全てのキーと値を再配置する。
// この再配置にはそれなりにコストがかかるため、スライスと理屈により、map を作成する際、最終的な要素数がわかっている場合は、定義時に指定することが望ましい。
// バケットを lookup する計算量は、ハッシュ値により O(1) である。
// 再配置などの時間を無視すれば、値の lookup は O(6.5/2) の計算量よりも大きくなることはない。
//
// map のバケット数は増加するが減少することはないので、キーを削除したとき、次のGCでキーと値のデータは解放されるが、バケットは解放されない。
// なので、バケットの個数が減らないことによるメモリ占有が気になる場合、工夫が必要である。
// 例えば、map を再作成して、バケットが増えてしまった map を使わないようにする方法がある。
// また、別の方法として、値にデータを使わずに、データへのポインタを使うことで、バケット一個あたりのメモリ使用量を減らすことができる。
//
//go:noinline
func praMap1() map[int][128]byte {
	fmt.Println("BBBBBBBBBBB_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	myMap := make(map[int][128]byte, 1000_000)
	for i := 0; i < 1000_000; i++ {
		myMap[i] = [128]byte{byte(1), byte(2), byte(3), byte(4), byte(5), byte(6), byte(7), byte(8)} // 残りの要素はゼロ値(byte(0))で埋められる。
	}

	if value, ok := myMap[100]; ok {
		fmt.Printf("myMap[100]: %v\n", value)
	}

	fmt.Println("BBBBBBBBBBB_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	// 全てのキーを削除する。
	for k := range myMap {
		delete(myMap, k)
	}

	fmt.Println("BBBBBBBBBBB_333444")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	return myMap
}

func CallpraMap1() {
	mymap := praMap1()

	fmt.Println("AAAAAAAAAA_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	if value, ok := mymap[100]; ok {
		fmt.Printf("mymap[100]: %v\n", value)
	}

	// これ以降 map への参照はないので、次回のGCでバケットも含めて解放される。

	fmt.Println("AAAAAAAAAA_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()
}

```
```
~/praprago$ go run .
BBBBBBBBBBB_111222
Alloc: 46 KB
Alloc: 47 KB
myMap[100]: [1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
BBBBBBBBBBB_222333
Alloc: 300487 KB
Alloc: 300383 KB
BBBBBBBBBBB_333444
Alloc: 300384 KB
Alloc: 300335 KB
AAAAAAAAAA_111222
Alloc: 300335 KB
Alloc: 300335 KB
AAAAAAAAAA_222333
Alloc: 300335 KB
Alloc: 47 KB
CCCCCCCCCC_111222
Alloc: 47 KB
Alloc: 47 KB
```
```
~/praprago$ go build -gcflags "-m=2" .
# github.com/XXX/praprago
./pra_slice_memory.go:8:6: can inline PrintAlloc with cost 72 as: func() { m = <nil>; runtime.ReadMemStats(&m); println("Alloc:", m.Alloc / uint64(1024), "KB") }
./pra_slice_memory.go:29:6: cannot inline praMap1: marked go:noinline
./pra_slice_memory.go:62:6: cannot inline CallpraMap1: function too complex: cost 725 exceeds budget 80
./main.go:9:6: cannot inline main: function too complex: cost 344 exceeds budget 80
./main.go:12:13: inlining call to fmt.Println
./main.go:13:12: inlining call to PrintAlloc
./main.go:15:12: inlining call to PrintAlloc
./pra_slice_memory.go:30:13: inlining call to fmt.Println
./pra_slice_memory.go:31:12: inlining call to PrintAlloc
./pra_slice_memory.go:33:12: inlining call to PrintAlloc
./pra_slice_memory.go:41:13: inlining call to fmt.Printf
./pra_slice_memory.go:44:13: inlining call to fmt.Println
./pra_slice_memory.go:45:12: inlining call to PrintAlloc
./pra_slice_memory.go:47:12: inlining call to PrintAlloc
./pra_slice_memory.go:54:13: inlining call to fmt.Println
./pra_slice_memory.go:55:12: inlining call to PrintAlloc
./pra_slice_memory.go:57:12: inlining call to PrintAlloc
./pra_slice_memory.go:65:13: inlining call to fmt.Println
./pra_slice_memory.go:66:12: inlining call to PrintAlloc
./pra_slice_memory.go:68:12: inlining call to PrintAlloc
./pra_slice_memory.go:71:13: inlining call to fmt.Printf
./pra_slice_memory.go:76:13: inlining call to fmt.Println
./pra_slice_memory.go:77:12: inlining call to PrintAlloc
./pra_slice_memory.go:79:12: inlining call to PrintAlloc
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap:
./pra_slice_memory.go:54:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_333444"}:
./pra_slice_memory.go:54:14:     from "BBBBBBBBBBB_333444" (spill) at ./pra_slice_memory.go:54:14
./pra_slice_memory.go:54:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:54:14:     from ... argument (spill) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:54:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap:
./pra_slice_memory.go:44:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_222333"}:
./pra_slice_memory.go:44:14:     from "BBBBBBBBBBB_222333" (spill) at ./pra_slice_memory.go:44:14
./pra_slice_memory.go:44:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:44:14:     from ... argument (spill) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:44:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:41:34: value escapes to heap:
./pra_slice_memory.go:41:34:   flow: {storage for ... argument} = &{storage for value}:
./pra_slice_memory.go:41:34:     from value (spill) at ./pra_slice_memory.go:41:34
./pra_slice_memory.go:41:34:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:41:34:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:41:34:     from ... argument (spill) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:41:34:     from fmt.format, fmt.a := "myMap[100]: %v\n", ... argument (assign-pair) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:41:34:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:41:34:     from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./pra_slice_memory.go:41:13
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap:
./pra_slice_memory.go:30:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_111222"}:
./pra_slice_memory.go:30:14:     from "BBBBBBBBBBB_111222" (spill) at ./pra_slice_memory.go:30:14
./pra_slice_memory.go:30:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:30:14:     from ... argument (spill) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:30:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:35:15: make(map[int][128]byte, 1000000) escapes to heap:
./pra_slice_memory.go:35:15:   flow: myMap = &{storage for make(map[int][128]byte, 1000000)}:
./pra_slice_memory.go:35:15:     from make(map[int][128]byte, 1000000) (spill) at ./pra_slice_memory.go:35:15
./pra_slice_memory.go:35:15:     from myMap := make(map[int][128]byte, 1000000) (assign) at ./pra_slice_memory.go:35:8
./pra_slice_memory.go:35:15:   flow: ~r0 = myMap:
./pra_slice_memory.go:35:15:     from return myMap (return) at ./pra_slice_memory.go:59:2
./pra_slice_memory.go:30:13: ... argument does not escape
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap
./pra_slice_memory.go:35:15: make(map[int][128]byte, 1000000) escapes to heap
./pra_slice_memory.go:41:13: ... argument does not escape
./pra_slice_memory.go:41:34: value escapes to heap
./pra_slice_memory.go:44:13: ... argument does not escape
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap
./pra_slice_memory.go:54:13: ... argument does not escape
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap:
./pra_slice_memory.go:76:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_222333"}:
./pra_slice_memory.go:76:14:     from "AAAAAAAAAA_222333" (spill) at ./pra_slice_memory.go:76:14
./pra_slice_memory.go:76:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:76:14:     from ... argument (spill) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:76:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:71:34: value escapes to heap:
./pra_slice_memory.go:71:34:   flow: {storage for ... argument} = &{storage for value}:
./pra_slice_memory.go:71:34:     from value (spill) at ./pra_slice_memory.go:71:34
./pra_slice_memory.go:71:34:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:71:34:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:71:34:     from ... argument (spill) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:71:34:     from fmt.format, fmt.a := "mymap[100]: %v\n", ... argument (assign-pair) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:71:34:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:71:34:     from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./pra_slice_memory.go:71:13
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap:
./pra_slice_memory.go:65:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_111222"}:
./pra_slice_memory.go:65:14:     from "AAAAAAAAAA_111222" (spill) at ./pra_slice_memory.go:65:14
./pra_slice_memory.go:65:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:65:14:     from ... argument (spill) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:65:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:13: ... argument does not escape
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap
./pra_slice_memory.go:71:13: ... argument does not escape
./pra_slice_memory.go:71:34: value escapes to heap
./pra_slice_memory.go:76:13: ... argument does not escape
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap:
./main.go:12:14:   flow: {storage for ... argument} = &{storage for "CCCCCCCCCC_111222"}:
./main.go:12:14:     from "CCCCCCCCCC_111222" (spill) at ./main.go:12:14
./main.go:12:14:     from ... argument (slice-literal-element) at ./main.go:12:13
./main.go:12:14:   flow: fmt.a = &{storage for ... argument}:
./main.go:12:14:     from ... argument (spill) at ./main.go:12:13
./main.go:12:14:     from fmt.a := ... argument (assign-pair) at ./main.go:12:13
./main.go:12:14:   flow: {heap} = *fmt.a:
./main.go:12:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:12:13
./main.go:12:13: ... argument does not escape
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap
```
## (2-2) 129個の要素の固定長配列のポインタをmapの値として使う

``` pra_slice_memory.go
package main

import (
	"fmt"
	"runtime"
)

func PrintAlloc() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	println("Alloc:", m.Alloc/1024, "KB")
}

// //////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////// (1)
// map の lookup の計算量は O((Load Factor)/2) である。
// Load Factor は一つのバケットに配置されているキーと値のペアの数の平均値である。
// Load Factor が 6.5 を超えると、新しいバケットを作成し、全てのキーと値を再配置する。
// この再配置にはそれなりにコストがかかるため、スライスと理屈により、map を作成する際、最終的な要素数がわかっている場合は、定義時に指定することが望ましい。
// バケットを lookup する計算量は、ハッシュ値により O(1) である。
// 再配置などの時間を無視すれば、値の lookup は O(6.5/2) の計算量よりも大きくなることはない。
//
// map のバケット数は増加するが減少することはないので、キーを削除したとき、次のGCでキーと値のデータは解放されるが、バケットは解放されない。
// なので、バケットの個数が減らないことによるメモリ占有が気になる場合、工夫が必要である。
// 例えば、map を再作成して、バケットが増えてしまった map を使わないようにする方法がある。
// また、別の方法として、値にデータを使わずに、データへのポインタを使うことで、バケット一個あたりのメモリ使用量を減らすことができる。
//
//go:noinline
func praMap1() map[int]*[128]byte {
	fmt.Println("BBBBBBBBBBB_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	myMap := make(map[int]*[128]byte, 1000_000)
	for i := 0; i < 1000_000; i++ {
		myMap[i] = &[128]byte{byte(1), byte(2), byte(3), byte(4), byte(5), byte(6), byte(7), byte(8)} // 残りの要素はゼロ値(byte(0))で埋められる。
	}

	if value, ok := myMap[100]; ok {
		fmt.Printf("myMap[100]: %v\n", value)
	}

	fmt.Println("BBBBBBBBBBB_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	// 全てのキーを削除する。
	for k := range myMap {
		delete(myMap, k)
	}

	fmt.Println("BBBBBBBBBBB_333444")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	return myMap
}

func CallpraMap1() {
	mymap := praMap1()

	fmt.Println("AAAAAAAAAA_111222")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()

	if value, ok := mymap[100]; ok {
		fmt.Printf("mymap[100]: %v\n", value)
	}

	// これ以降 map への参照はないので、次回のGCでバケットも含めて解放される。

	fmt.Println("AAAAAAAAAA_222333")
	PrintAlloc()
	runtime.GC()
	PrintAlloc()
}

```
```
~/praprago$ go run .
BBBBBBBBBBB_111222
Alloc: 46 KB
Alloc: 46 KB
myMap[100]: &[1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
BBBBBBBBBBB_222333
Alloc: 164216 KB
Alloc: 164215 KB
BBBBBBBBBBB_333444
Alloc: 164216 KB
Alloc: 39215 KB
AAAAAAAAAA_111222
Alloc: 39215 KB
Alloc: 39215 KB
AAAAAAAAAA_222333
Alloc: 39215 KB
Alloc: 47 KB
CCCCCCCCCC_111222
Alloc: 47 KB
Alloc: 47 KB
```
```
~/praprago$ go build -gcflags "-m=2" .
# github.com/XXX/praprago
./pra_slice_memory.go:8:6: can inline PrintAlloc with cost 72 as: func() { m = <nil>; runtime.ReadMemStats(&m); println("Alloc:", m.Alloc / uint64(1024), "KB") }
./pra_slice_memory.go:29:6: cannot inline praMap1: marked go:noinline
./pra_slice_memory.go:62:6: cannot inline CallpraMap1: function too complex: cost 725 exceeds budget 80
./main.go:9:6: cannot inline main: function too complex: cost 344 exceeds budget 80
./main.go:12:13: inlining call to fmt.Println
./main.go:13:12: inlining call to PrintAlloc
./main.go:15:12: inlining call to PrintAlloc
./pra_slice_memory.go:30:13: inlining call to fmt.Println
./pra_slice_memory.go:31:12: inlining call to PrintAlloc
./pra_slice_memory.go:33:12: inlining call to PrintAlloc
./pra_slice_memory.go:41:13: inlining call to fmt.Printf
./pra_slice_memory.go:44:13: inlining call to fmt.Println
./pra_slice_memory.go:45:12: inlining call to PrintAlloc
./pra_slice_memory.go:47:12: inlining call to PrintAlloc
./pra_slice_memory.go:54:13: inlining call to fmt.Println
./pra_slice_memory.go:55:12: inlining call to PrintAlloc
./pra_slice_memory.go:57:12: inlining call to PrintAlloc
./pra_slice_memory.go:65:13: inlining call to fmt.Println
./pra_slice_memory.go:66:12: inlining call to PrintAlloc
./pra_slice_memory.go:68:12: inlining call to PrintAlloc
./pra_slice_memory.go:71:13: inlining call to fmt.Printf
./pra_slice_memory.go:76:13: inlining call to fmt.Println
./pra_slice_memory.go:77:12: inlining call to PrintAlloc
./pra_slice_memory.go:79:12: inlining call to PrintAlloc
./pra_slice_memory.go:37:14: &[128]byte{...} escapes to heap:
./pra_slice_memory.go:37:14:   flow: {heap} = &{storage for &[128]byte{...}}:
./pra_slice_memory.go:37:14:     from &[128]byte{...} (spill) at ./pra_slice_memory.go:37:14
./pra_slice_memory.go:37:14:     from myMap[i] = &[128]byte{...} (assign) at ./pra_slice_memory.go:37:12
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap:
./pra_slice_memory.go:54:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_333444"}:
./pra_slice_memory.go:54:14:     from "BBBBBBBBBBB_333444" (spill) at ./pra_slice_memory.go:54:14
./pra_slice_memory.go:54:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:54:14:     from ... argument (spill) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:54:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:54:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:54:13
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap:
./pra_slice_memory.go:44:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_222333"}:
./pra_slice_memory.go:44:14:     from "BBBBBBBBBBB_222333" (spill) at ./pra_slice_memory.go:44:14
./pra_slice_memory.go:44:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:44:14:     from ... argument (spill) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:44:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:44:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:44:13
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap:
./pra_slice_memory.go:30:14:   flow: {storage for ... argument} = &{storage for "BBBBBBBBBBB_111222"}:
./pra_slice_memory.go:30:14:     from "BBBBBBBBBBB_111222" (spill) at ./pra_slice_memory.go:30:14
./pra_slice_memory.go:30:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:30:14:     from ... argument (spill) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:30:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:30:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:30:13
./pra_slice_memory.go:35:15: make(map[int]*[128]byte, 1000000) escapes to heap:
./pra_slice_memory.go:35:15:   flow: myMap = &{storage for make(map[int]*[128]byte, 1000000)}:
./pra_slice_memory.go:35:15:     from make(map[int]*[128]byte, 1000000) (spill) at ./pra_slice_memory.go:35:15
./pra_slice_memory.go:35:15:     from myMap := make(map[int]*[128]byte, 1000000) (assign) at ./pra_slice_memory.go:35:8
./pra_slice_memory.go:35:15:   flow: ~r0 = myMap:
./pra_slice_memory.go:35:15:     from return myMap (return) at ./pra_slice_memory.go:59:2
./pra_slice_memory.go:30:13: ... argument does not escape
./pra_slice_memory.go:30:14: "BBBBBBBBBBB_111222" escapes to heap
./pra_slice_memory.go:35:15: make(map[int]*[128]byte, 1000000) escapes to heap
./pra_slice_memory.go:37:14: &[128]byte{...} escapes to heap
./pra_slice_memory.go:41:13: ... argument does not escape
./pra_slice_memory.go:44:13: ... argument does not escape
./pra_slice_memory.go:44:14: "BBBBBBBBBBB_222333" escapes to heap
./pra_slice_memory.go:54:13: ... argument does not escape
./pra_slice_memory.go:54:14: "BBBBBBBBBBB_333444" escapes to heap
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap:
./pra_slice_memory.go:76:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_222333"}:
./pra_slice_memory.go:76:14:     from "AAAAAAAAAA_222333" (spill) at ./pra_slice_memory.go:76:14
./pra_slice_memory.go:76:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:76:14:     from ... argument (spill) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:76:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:76:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:76:13
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap:
./pra_slice_memory.go:65:14:   flow: {storage for ... argument} = &{storage for "AAAAAAAAAA_111222"}:
./pra_slice_memory.go:65:14:     from "AAAAAAAAAA_111222" (spill) at ./pra_slice_memory.go:65:14
./pra_slice_memory.go:65:14:     from ... argument (slice-literal-element) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: fmt.a = &{storage for ... argument}:
./pra_slice_memory.go:65:14:     from ... argument (spill) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:     from fmt.a := ... argument (assign-pair) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:14:   flow: {heap} = *fmt.a:
./pra_slice_memory.go:65:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_slice_memory.go:65:13
./pra_slice_memory.go:65:13: ... argument does not escape
./pra_slice_memory.go:65:14: "AAAAAAAAAA_111222" escapes to heap
./pra_slice_memory.go:71:13: ... argument does not escape
./pra_slice_memory.go:76:13: ... argument does not escape
./pra_slice_memory.go:76:14: "AAAAAAAAAA_222333" escapes to heap
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap:
./main.go:12:14:   flow: {storage for ... argument} = &{storage for "CCCCCCCCCC_111222"}:
./main.go:12:14:     from "CCCCCCCCCC_111222" (spill) at ./main.go:12:14
./main.go:12:14:     from ... argument (slice-literal-element) at ./main.go:12:13
./main.go:12:14:   flow: fmt.a = &{storage for ... argument}:
./main.go:12:14:     from ... argument (spill) at ./main.go:12:13
./main.go:12:14:     from fmt.a := ... argument (assign-pair) at ./main.go:12:13
./main.go:12:14:   flow: {heap} = *fmt.a:
./main.go:12:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:12:13
./main.go:12:13: ... argument does not escape
./main.go:12:14: "CCCCCCCCCC_111222" escapes to heap
```



# 参考

https://victoriametrics.com/blog/go-map/

https://stackoverflow.com/questions/29677670/what-is-the-big-o-performance-of-maps-in-golang

https://stackoverflow.com/questions/67445332/why-does-the-bucket-of-map-in-golang-contain-up-to-8-key-elem-pairs-why-the-num

> [Ricardo Erikson](https://twitter.com/ricardoerikson) adds in ["Memory Allocation and Performance in Golang Maps"](https://levelup.gitconnected.com/memory-allocation-and-performance-in-golang-maps-b267b5ad9217).
>  He refers to [Alexander Kochetkov](https://twitter.com/avk_ai)'s article ["Some insights on Maps in Golang"](https://hackernoon.com/some-insights-on-maps-in-golang-rm5v3ywh), which adds:
>  
> > **Average number of items in buckets is called load factor.**
> > 
> > Load factor divided by 2 will be the average lookup time, and collisions rate will define dispersion of it.
> > Formally the complexity is still O(n), but in practice collisions is rare enough and keys will spread to buckets almost evenly.
>  
>  Ricardo adds:
>  
> > A map in Go is a hash table that stores its key/value pairs into buckets. Each bucket is an array that holds up to 8 entries.
> > The default number of buckets is 1.
> > 
> > Once the number of entries across each bucket reaches an average load of buckets (aka load factor), the map gets bigger by doubling the number of buckets.
> > Every time a map grows, it allocates memory for the new-coming entries.
> > 
> > In practice, every time the load of the buckets reaches 6.5 or more, the map grows. This value is hardcoded and was chosen to optimize memory usage.

https://levelup.gitconnected.com/memory-allocation-and-performance-in-golang-maps-b267b5ad9217

https://hackernoon.com/some-insights-on-maps-in-golang-rm5v3ywh
> Average number of items in buckets is called load factor. Load factor divided by 2 will be the average lookup time, and collisions rate will define dispersion of it. Formally the complexity is still O(n), but in practice collisions is rare enough and keys will spread to buckets almost evenly.

https://www.amazon.co.jp/-/en/Teiva-Harsanyi/dp/4295017531