Go の標準組み込みマップで動作するスイステーブルについて

2026/09/03 20:59

Go の標準組み込みマップで動作するスイステーブルについて

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

Go 1.24 では、溢れバケットを使用していた古い Map ランタイム実装が、「Swiss Tables」に基づく新しい設計に置き換えられました。

Map
構体は現在、ランタイム時にマップを表現し、
used
(エントリ数)、
seed
(異なるキー分布のためのランダムハッシュシード)、
dirPtr
(ストレージへのポインタ)などのフィールドを含みます。マップはデータを「グループ」に格納しており、これは最小のストレージ単位で、単一の
uint64
内に最大 8 つの鍵値ペアおよび 8 バイトの制御バイトを保持します。制御バイトでは上位ビットがスロットの状態を示し、0 が有効なエントリ、1(低位ビットと共に)が空または削除済み(トムストーン)スロットを表します。ハッシュは H1(64 ビット上では上位 57 ビット)と H2(下部 7 ビット)に分割され、H1 はストレージ場所を選択し、H2 は制御バイトに格納されて初期フィルタリングに使用されます。マップが一つのグループの容量を超えると、グループ数を倍増させ、「テーブル」を導入してそれらを管理します。Go は、開始グループが埋まった場合、テーブル内のキーを検索するために三角形プローブシーケンス(+1, +2, +3...)を使用します。
dirPtr
フィールドは、複数のテーブルが存在する場合に直接グループを指すのではなく、ディレクトリ配列を通じた指し回しに変化します。マップが 1024 スロット(128 グループ)の最大容量に達すると、グループ数を倍増するのではなく、2 つの新しいテーブルに分割されます。テーブルは削除済みスロットを含む負荷率上限を 7/8(87.5%)に維持し、欠落キーの検索パフォーマンスを最適化します。Go は、拡張および分割時にハッシュビットがディレクトリエントリと特定のテーブルを選択する方法を管理するために
globalDepth
localDepth
を使用します。新しい実装は、微ベンチマークにおいて Go 1.23 より最大 60% 高速なマップ操作を提供し、ハッシュマップを使用するアプリケーションに対してコード変更なしで重要な速度向上をもたらします。今後、Go 1.27 など将来のバージョンでは、大規模データ構造および高頻度ハッシュ化操作に対する CPU キャッシュ局所性をさらに改善し、メモリ配置を最適化しパディングを削減することを目的とした「mapsplitgroup」と呼ばれる実験的な調整が計画されています。

本文

Go マップの内部動作と Go 1.24 での実装変更:Swiss Tables

Go 1.24 でランタイム実装が大きく変更されました。この記事では、新しい「スイス式テーブル(Swiss Tables)」に基づくマップの内部構造と動作について、視覚的かつ段階的に解説します。


ランタイムでのマップとは何か

make
関数で初期化されたマップは、言語レベル上の型ではなく、ランタイム構造体へのポインタです。

m := make(map[string]int)
  • map[string]int
    は言語レベルの型であり、キーに文字列、値に整数を使用することを示します。
  • 実際には、
    m
    internal/runtime/maps.Map
    構造体へのポインタを指しています。

マップ構造体の内部フィールド

type Map struct {
	used uint64
	seed uintptr
	...
}
フィールド役割
used
現在格納されているアクティブなエントリの数
len(m)
はこれへのアクセス(O(1))で取得できます。
seed
ハッシュ値の分散を制御するシード。マップごとにランダムに設定され、同じキーでも異なるマップでは異なるハッシュ位置に格納される原因となります。

注意: これらのフィールドは、実際のキー・バリューエントリ自体を指すものではありません。


グループ (Group)

最も小さいストレージ単位はグループです。

  • 最大 8 組のキー・バリューペアを格納します(1 つのグループ内)。
  • 各スロットに対応する コントロールバイト(Control Byte) を持つ配列を含みます。

コントロールバイトとハッシュ値 (H2)

Go はキーをハッシュ化し、結果を以下のように分割します(64 ビットターゲットの場合):

  • H1: 上位 57 ビット → 検索開始位置のテーブル選択に使用
  • H2: 下位 7 ビット → グループ内でのスロット探索に使用

コントロールバイトの構成例(8ビット):

状態上位ビット (フラグ)内容説明
アクティブ
0
H2
キーが存在するスロット。例:
cow
(H2=42) →
00101010
1
10000000
(8)
空のスロット。探索を早期終了させるため。
削除済み (Tombstone)
1
11111110
(-2)
以前は存在したキーが削除されたスロット。探索を継続するため。

スロットの探索プロセス

  1. キー
    "cow"
    を追加する際、Go はハッシュから H2 = 42 を取得します。
  2. グループ内の全 8 バイトを同時に比較(SIMD 命令を使用)し、一致する候補スロットを特定します。
  3. ヒットしたスロットに対して完全なキーの等価チェック(
    ==
    )を行います。

テーブル (Table)

グループ単体のストレージが満杯(通常はアクティブエントリ/空スロット比など)になった場合、マップは成長しテーブルに切り替わります。

  • 最初のテーブルは 2 つのグループ(合計 16 スロット)を持ちます。
  • ストレージ単位が大きくなったため、再分配が必要です。

再分配と開始位置の決定 (H1)

既存のエントリを新しいグループへ移す際、Go はハッシュ値の H1 を使用します。

$$ \text{Starting Group} = \text{H1} % \text{Number of Groups} $$

  • グループ数が 2 の場合(
    % 2
    )、H1 の最下位ビットのみで十分です。
  • 一部のキーはグループ 0、一部はグループ 1 に移されます。

三角探索シーケンス (Triangular Probe Sequence)

再分配後の「新しい」空きスロットが見つからない場合(全埋め)、Go は三角探索シーケンスを用いて空きスロットを探します。

  • ステップ: 開始位置から +1, +2, +3... と広げるようにグループを順番にチェックします。
  • 特徴: グループ数が 2 の累乗(例:4, 8, 16)なので、探索前に全グループを一度だけ正確に訪れることができます。

ディレクトリ (Directory)

グループ単体のマップからテーブル backed マップへ移行すると、ストレージアクセスの仕組みが変化します。

  • dirPtr
    はグループを直接指さなくなり、ディレクトリエントリ配列へのポインタになります。
  • ディレクトリは、ハッシュ値 H1 の上位ビット(左端) を使用して適切なテーブルを選択する役割を持ちます。

テーブルの分割と成長

  • 最大 128 グループ(1024 スロット)まで成長可能ですが、その限界に達した際もさらに成長すると分割が発生します。
  • ロードファクター制限: テーブルは完全に満杯になるのを待たず、7/8 (87.5%) 程度の使用率で再構築されます(削除済みスロットを含む計算)。

ディレクトリの深さ

ディレクトリがテーブルの分割に対応し続けるために、ハッシュビット数の概念を導入します。

  • GlobalDepth: ディレクトリ全体を指すための H1 の上位ビット数。
  • LocalDepth: 個別のテーブルを特定するための H1 の上位ビット数。

重要: テーブルが分割されても、既存の他のテーブルは再構築せず、ディレクトリの深さ(ビット数)を増やすことで指し示すだけとなります。


Go 1.24 で何が変化したか

Go 1.23 以前の古い実装との比較です。

旧実装(バケットとオーバーフローチェーン)

  • バケットが満杯になると、追加の「オーバーフローバケット」を連結していました。
  • チェーンが長くなるほどポインタ読み込みのコストが増大します。

新しい実装(Swiss Tables)

  • すべてのグループを 1 つの配列に割り当てています。
  • オバーフローではなく、三角探索シーケンスで空きスロットを見つけます。
  • 成長戦略: 全マップを再構築するのではなく、選択されたテーブルのみを再構築します。

性能影響

  • マップ関連の操作は、特定のカテゴリで最大 60% 高速化
  • アプリケーション全体のベンチマークでは CPU 時間に対して約 1.5% の改善

バONUS: 削除処理について

「空」と「削除済み(Tombstone)」の違いが検索コストに大きく影響します。

スロットの状態検索時の動作
キーが見つからず、かつ空きがある場合 → 探索早期終了
削除済みキーは以前存在したが削除されたため → 探索を継続(後続のグループを確認)。
  • 小規模なマップでは、削除後は自動的に「空」になります。
  • 大規模なマップ(テーブル backed)では、空きがない場合のみ「削除済み」としてマークされます。

なぜロードファクターなのか?

Go はなぜ 100% まで埋める代わりに、7/8 (87.5%) の制限を設けるのでしょうか?

  • 探索コストの削減: テーブルがほぼ満杯だと、空のスロットを見つけるのが困難になります。
  • 三角探索の影響: 空のスロットは探索を早期に中断させますが、それが「必要なキー」を含むグループでなければ無駄なチェックになります。
  • Abseil の採用: Go は Google のライブラリ Abseil が採用している Swiss Tables の仕様(7/8 の限界)を採用しています。

分裂型グループレイアウト(The split group layout)

Go 1.27 で実験的導入された、さらなるメモリ効率化のレイアウトです(

GOEXPERIMENT=mapsplitgroup
)。

従来のレイアウト

  • キーと値をペアにして格納。
  • 末尾に値がない場合、パディングにより無駄なメモリを消費。
type slot struct {
	key   Key
	elem  Elem
}

分裂型レイアウト(Split Layout)

  • キー配列値配列を分離して格納。
  • キー検索時に一致すれば初めて値配列にアクセスする。
  • メモリ効率化:グループあたり約 56 バイトの節約が可能になります。
type group struct {
	ctrl uint64
	keys [8]Key
	elem [8]Elem
}

リソース

より詳細な情報が必要な場合は以下のドキュメントを参照してください。

同じ日のほかのニュース

一覧に戻る →

2026/09/06 5:31

民間ドイツのロケットが歴史を刻み、欧州大陸から軌道への到達に成功

## Japanese Translation: Isar Aerospace は、先行の課題を克服し、Spectrum ロケットが 2 回目の飛行を成功裡に完了して軌道到達を果たしたことで歴史的なマイルストーンを達成しました。この発射は「Onward and Upward」と題され、9 月 5 日にノルウェー北部(Andøya Space Center)で行われ、第二段階は楕円軌道(近地点 180 km、遠地点 500 km)に安定しました。この成功は、3 月の事故(予期せぬバルブの作動と姿勢制御喪失により発生)に続く広範な工学努力を検証しています。調査ではこれらの始発事象が特定されました。計画されていた 1 月の発射は、各種要因—including 圧力化バルブの課題、複合容器からの漏れ、流体系挙動、侵入したボート、および天候—により遅延しましたが、チームは最終的に 5 基のキューブサットと 1 つ展開不能な科学実験を搭載して発射に踏み切りました。現在、ミッションの完全な成功は、軌道の円化後にペイロードを展開することによります。95 フィートの高さを持つ二段階ロケットは、低地球軌道へ約 1,000 キログラムを運ぶことができます。ミュンヘンの施設が年間 30 基以上を製造できる能力を有する中、Isar は小型から中型の衛星に対する主要な主力機として位置づけられます。これはロシアの decades-long の Plesetsk コズモドロームでの優位性と異なり、ヨーロッパにおける能力の変化を意味しますが、この成就是 Isar を信頼できる新規プレイヤーとして確立し、Spectrum 上級エンジニア Nikolaos Perakis が率いる工学チームのレジリエンスを検証するものです。

2026/09/06 7:08

プログラマがLAN について信じている虚偽

## 日本語訳: 元の要約は実際にかなり強力です。要点リストの断片的な箇条書きを、ネットワーク複雑性に関する一貫した物語に成功裏に統合しています。ただし、リストに含まれるすべての具体的な技術的なニュアンスが失われず、かつ文脈の流れを損なわずに明確に反映されるよう確保するため、欠落していたプロトコル名と ARP の精度に関するニュアンスを取り入れた若干精査されたバージョンを以下に示します: ## 改善された要約 主要な洞察は、ローカルエリアネットワーク(LAN)がデバイス識別および通信のために複雑で、場合によっては一貫性のないメカニズムに依存しており、技術的なニュアンスが信頼性に著しい影響を与えるという点にあります。単純な 1 対 1 のマッピングとは異なり、ネットワークアドレスは常に一意ではありません。MAC アドレスは世界全体で一意となることを意図した 48 ビットで構成されていますが、それらは単一のデバイスではなく異なるインタフェースを表すことがあり、また真の一意性を欠いている場合があります。同様に、IP(および歴史的に IPX/SPX、AppleTalk など)のようなプロトコルが通信を標準化するものの、ホストは中央の DHCP サーバーから有効なアドレスを受信しえないことが多く、予約された「リンクローカル」IP(例:169.254.0.0/16)を持ってしまい、その結果、ホストの発見は mDNS などの可変的な手法に依存し、ホスト名の一意性はローカルであってグローバルではなく、ARP リクエスト——一般的には IP ごとに一つの答えが返される——であってもエントリの精度を保証するものではありません。さらに、LAN の性能は接続の種類によって異なります;より高速なイーサネットでも低速な Wi-Fi でも、最大転送ユニット(MTU)の違いがピア間の接続障害を引き起こす可能性があります。したがって、ネットワーク管理者は安定した運用を確保するために、これらの多様なプロトコルスタック、潜在的なアドレスの一貫性問題(NAT を含む)、および可変的な発見メカニズムを考慮に設計された堅牢なシステムを実装する必要があります。

2026/09/01 16:31

Show HN: フライバイ ~レトロな双葉機飛行ゲーム~

## 日本語訳: 要約: 本テキストは、航空業界標準の操縦系を模倣した専用のゲーム設定をご紹介します。具体的には、「引き上げで上昇」する逆 Y(インバーティッド・Y)のような構成であり、没入感のあるフライトシミュレーションを実現します。高忠実度なリアルさを特徴とする大気風の影響や飛行機エンジンのサウンドエフェクトなどの機能と、画面タッチ操作といったモダンな利便性、オプションの儀表盤表示の切り替えを組み合わせます。視覚スタイルはレトロ CRT スキャンラインフィルタにより向上し、ゲームプレイの深みについてはスピードブーストや機関銃などのパワーアップを通じて拡大されます。このハイブリッドな構成により、本格的な飛行物理現象を楽しむシミュレーション愛好家と、アクセスしやすいメカニクスを好むカジュアルゲーマーの両方が、モダンな利便性を損なうことなく満足できます。

Go の標準組み込みマップで動作するスイステーブルについて | そっか~ニュース