post Image
Goのstring型が思ったより容量食いだった話

表題の通り、Goのstring型を何の気なしに使っていたら思ったよりバイナリサイズ・消費メモリサイズが大きくなっていたという話題です。

Goプログラミングでバイナリサイズやメモリ消費量をカリカリにチューニングすることは多くないと思われるので、本稿の内容が重要になることは滅多にないだろうことを最初にお断りしておきます。

mapのサイズが案外大きい

ある日、筆者は次のようなGoコードを書いていました。

var oui = map[string]string{
    "AAAB": "Xerox",
    "AAAC": "Xerox",
    "AAAD": "Xerox",
    "AAAE": "Xerox",
    "AAAF": "Xerox",

    /* snip */

    "/Pv7": "Cisco",
    "/PxI": "Apple",
    "/P53": "HitachiR",
    "/P7C": "Invensys",
    "/P+q": "IeeeRegi",
 }

このmapは要素数が約2.4万、キーはASCII4文字固定、値はASCII2〜8文字というものです。これをビルドしてみると、このmapがバイナリサイズのうち950KBほどを占めていることに気づきました。

1要素あたりASCII6文字〜12文字しか情報が無いはずなのに、950KB/2.4万=約40バイトということで案外サイズが大きいと感じます。

mapのキーの型をstringからint32に変えてみた

思ったよりサイズを食っていると感じたので、mapのキーの型をstringからuint32に変更してみました。キーはASCII4文字固定1なので1:1対応で32bit整数に変換できます。

var oui = map[uint32]string{
    0x1: "Xerox",
    0x2: "Xerox",
    0x3: "Xerox",
    0x4: "Xerox",
    0x5: "Xerox",

    /* snip */

    0xfcfbfb: "Cisco",
    0xfcfc48: "Apple",
    0xfcfe77: "HitachiR",
    0xfcfec2: "Invensys",
    0xfcffaa: "IeeeRegi",
}

このように変更したところ、バイナリサイズが380KBほど減少しました。計算してみると、1要素あたり16バイトほど減った計算になります。

いくらなんでも減りすぎではないか?と感じたので原因を調べてみました。

string型はどう実現されているか

今回の現象の原因はstring型の実装にあります。string型に対応する構造体は次のようなものです。

type stringStruct struct {
    str unsafe.Pointer
    len int
}

src/runtime/string.go より)

つまり、string型は文字列実体へのポインタと文字列長を表すint型とで構成されています。64bit環境であればポインタで8バイト、文字列長で8バイト、さらに文字列の実体の分もディスクまたはメモリが必要になります。mapのキーをstring型(8+8+4文字=20バイト)からint32(=4バイト)に変更することで1要素あたり16バイト節約できたのは当然ということになります。

8バイト以下のstringuint64にする

同じノリでmapの値の型もuint64にしてみました。

var oui = map[uint32]uint64{
    0x1: 0x0000005865726f78, // Xerox
    0x2: 0x0000005865726f78, // Xerox
    0x3: 0x0000005865726f78, // Xerox
    0x4: 0x0000005865726f78, // Xerox
    0x5: 0x0000005865726f78, // Xerox

    /* snip */

    0xfcfbfb: 0x000000436973636f, // Cisco
    0xfcfc48: 0x0000004170706c65, // Apple
    0xfcfe77: 0x4869746163686952, // HitachiR
    0xfcfec2: 0x496e76656e737973, // Invensys
    0xfcffaa: 0x4965656552656769, // IeeeRegi
}

容量削減のために無理をしている分、値を取り出すのに多少頑張る必要があります。

    abbr64, ok := oui[oui24hash]
    b := make([]byte, 8)
    binary.BigEndian.PutUint64(b, abbr64)
    abbr := string(bytes.TrimLeft(b, "\x00"))

これをビルドすると更に290KB減となり、期待通りバイナリサイズ削減効果が得られました(同じ値を持つ要素があるため、キーのときより削減幅が小さくなっています)。当初のサイズと比較すると約950KBから約280KBと1/3以下になったわけで、私の用途には非常に良い結果が得られました。


  1. もともと3バイト整数をBase64でASCII4文字にしていた 


『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。