Go言語色んなサイズを測ってみた
目次
1. 色んなタイプのサイズ比較
2. まとめたテーブル
3. おわりに
1. 色んなタイプのサイズ比較
Go言語の仕様を学ぶため、色んなタイプのサイズを比較してみました。
一覧は最後のテーブルにまとめます。
- この比較は、以下の環境で行われています:
- システム:Mac
- アーキテクチャ:64ビット
- Goのバージョン:go version go1.18.1 darwin/amd64
go version go1.18.1 darwin/amd64
比較するデータ型は以下の通りです:
1. int / uint (サイズはアーキテクチャによって異なる場合がある)
2. int8 / uint8
3. int16 / uint16
4. int32 / uint32
5. int64 / uint64
6. float32
7. float64
8. complex64
9. complex128
10. byte (uint8の別名)
11. rune (int32の別名: Unicodeのコードポイントを表す)
12. bool
13. string
14. スライス (例: []int)
15. マップ (例: map[string]int)
16. チャネル (例: chan int)
17. 構造体 (例: struct{})
18. インターフェース
なお今回はunsafe.Sizeof()
を使用して比較します。
func Sizeof(x ArbitraryType) uintptr
注釈:unsafe.Sizeof()
の使用について
本記事でのデータ型サイズの比較には unsafe.Sizeof()
関数を使用しています。この関数は Go の unsafe
パッケージに含まれ、型の静的なメモリサイズをバイト数で返します。しかし、以下の点に注意が必要です:
-
型安全性の迂回:
unsafe
パッケージは、Go の型安全性の制約を迂回する機能を提供します。これにより、直接的なメモリ操作やシステムレベルのプログラミングが可能になりますが、使用には注意が必要です。 -
動的データ構造の扱い:
unsafe.Sizeof()
は「静的な」サイズを返すため、スライス、マップ、チャネルなどの動的なデータ構造の実際に確保されるメモリ量を完全には反映しません。これらの構造は実行時にサイズが変更され、unsafe.Sizeof()
はそのヘッダのサイズのみを返します。
それでは。
コード
package main
import (
"fmt"
"unsafe"
)
type MyInterface interface {
SomeMethod()
}
type MyStruct struct {
a int
b string
}
func main() {
fmt.Println("サイズ比較:")
fmt.Printf("int / uint: %v / %v\n", unsafe.Sizeof(int(0)), unsafe.Sizeof(uint(0)))
fmt.Printf("int8 / uint8: %v / %v\n", unsafe.Sizeof(int8(0)), unsafe.Sizeof(uint8(0)))
fmt.Printf("int16 / uint16: %v / %v\n", unsafe.Sizeof(int16(0)), unsafe.Sizeof(uint16(0)))
fmt.Printf("int32 / uint32: %v / %v\n", unsafe.Sizeof(int32(0)), unsafe.Sizeof(uint32(0)))
fmt.Printf("int64 / uint64: %v / %v\n", unsafe.Sizeof(int64(0)), unsafe.Sizeof(uint64(0)))
fmt.Printf("float32: %v\n", unsafe.Sizeof(float32(0)))
fmt.Printf("float64: %v\n", unsafe.Sizeof(float64(0)))
fmt.Printf("complex64: %v\n", unsafe.Sizeof(complex64(0)))
fmt.Printf("complex128: %v\n", unsafe.Sizeof(complex128(0)))
fmt.Printf("byte: %v\n", unsafe.Sizeof(byte(0)))
fmt.Printf("rune: %v\n", unsafe.Sizeof(rune(0)))
fmt.Printf("bool: %v\n", unsafe.Sizeof(bool(false)))
fmt.Printf("string: %v\n", unsafe.Sizeof(string("")))
// スライス、マップ、チャネル、ポインタ、構造体、インターフェースのサイズは
// 実行時に依存するため、以下のようにサンプルデータを使って測定することができます。
fmt.Printf("スライス: %v\n", unsafe.Sizeof([]int{}))
var s = []int{}
fmt.Printf("スライス中身あり: %v\n", unsafe.Sizeof(s))
fmt.Printf("マップ: %v\n", unsafe.Sizeof(map[string]int{}))
var m = map[string]int{}
m["a"] = 1
fmt.Printf("マップ中身あり: %v\n", unsafe.Sizeof(m))
fmt.Printf("チャネル: %v\n", unsafe.Sizeof(make(chan int)))
ch := make(chan int, 10)
fmt.Printf("チャネル中身あり: %v\n", unsafe.Sizeof(ch))
fmt.Printf("構造体: %v\n", unsafe.Sizeof(struct{}{}))
fmt.Printf("構造体中身あり: %v\n", unsafe.Sizeof(MyStruct{1, "a"}))
fmt.Printf("インターフェース: %v\n", unsafe.Sizeof(MyInterface(nil)))
fmt.Printf("型の定義のないインターフェース: %v\n", unsafe.Sizeof(new(interface{})))
}
実行結果
サイズ比較:
int / uint: 8 / 8
int8 / uint8: 1 / 1
int16 / uint16: 2 / 2
int32 / uint32: 4 / 4
int64 / uint64: 8 / 8
float32: 4
float64: 8
complex64: 8
complex128: 16
byte: 1
rune: 4
bool: 1
string: 16
スライス: 24
スライス中身あり: 24
マップ: 8
マップ中身あり: 8
チャネル: 8
チャネル中身あり: 8
構造体: 0
構造体中身あり: 24
インターフェース: 16
型の定義のないインターフェース: 8
2. まとめたテーブル
データ型 | サイズ (バイト) |
---|---|
int / uint | 8 / 8 |
int8 / uint8 | 1 / 1 |
int16 / uint16 | 2 / 2 |
int32 / uint32 | 4 / 4 |
int64 / uint64 | 8 / 8 |
float32 | 4 |
float64 | 8 |
complex64 | 8 |
complex128 | 16 |
byte (uint8の別名) | 1 |
rune (int32の別名) | 4 |
bool | 1 |
string | 16 |
スライス (例: []int) | 24 |
マップ (例: map[string]int) | 8 |
チャネル (例: chan int) | 8 |
構造体 (例: struct{}) | 0 |
インターフェース | 16 |
3. おわりに
サイズ数を測るにあたり、様々な型を知ることができました。
またinterfaceは型とポインタ(8+8)の16を持っていることを知りました。
他にも色んな言語で色んなサイズを測ってみたいです。
あと、正式なサイズの測り方を知りたい......。