Speaker

倉澤大樹 Hiroki Kurasawa

倉澤大樹 Hiroki Kurasawa

株式会社ZOZOバックエンドエンジニア

Actions

ZOZOTOWNの検索機能の改善に努めています。

encoding/json/v2のUnmarshalはこう変わった:内部実装で見る設計改善

# 概要
Go 1.25で実験的に導入されたencoding/json/v2は、v1から大きく設計を見直しました。現在はまだ実験的な立ち位置ですが、今後標準パッケージとして採用される可能性があります。本LTでは、Unmarshal関数の内部実装を比較しながら、(1)パッケージ分離による責務の明確化、(2)sync.Poolによるメモリ効率化、(3)4つのセグメントバッファ設計という3つの改善ポイントを解説します。標準ライブラリの進化から学ぶ、実践的な設計改善のお話です。

# 発表内容の詳細
## 背景と動機
encoding/json(v1)には以下の課題がありました

- パフォーマンス: Decoder.Token()のメモリアロケーションが多い
- 設計: 低レベル処理(構文解析)と高レベル処理(型変換)が混在
- 拡張性: カスタマイズオプションが限定的

v2ではこれらを根本から見直し、パフォーマンスと保守性を両立させています。

# 主な内容(5分構成)

## 1. パッケージ分離とexport変数(1分30秒)

v2の2層アーキテクチャ:
┌─────────────────┐
│ json パッケージ │ ← 高レベル(Go型への変換)
└─────────────────┘
↓ export変数
┌─────────────────┐
│ jsontext │ ← 低レベル(構文解析)
└─────────────────┘
安全な内部API制御:
```
var export = jsontext.Internal.Export(&internal.AllowInternalUse)

// jsontextの内部機能を安全に利用
dec := export.GetBufferedDecoder(data, opts...)
```

**学べるポイント:**

- 責務の分離による保守性向上
- internal パッケージ + export パターンによるアクセス制御

## 2. sync.Poolによるメモリ効率化(1分30秒)

構造体サイズの変化:
```
// v1: 約200バイト → 毎回初期化でもOK
type decodeState struct { ... }

// v2: 約3KB(15倍!) → プールで再利用が効果的
type decoderState struct { ... }
```

**プールの効果(秒間1000リクエストの場合):**

- プールなし: 1000個 × 3KB = 3MB/秒のアロケーション
→ 頻繁なGC → レスポンス悪化

- プールあり: 数個 × 3KB = 約30KB(100倍効率的)
→ GCほぼなし → 安定したパフォーマンス

実装例:
```
// プールから取得・返却
dec := bufferedDecoderPool.Get().(*Decoder)
defer putBufferedDecoder(dec)

// reset()で再初期化
dec.s.reset(data, nil, opts...)
```

**学べるポイント:**

- 大きなオブジェクトはsync.Poolで再利用
- 構造体サイズが設計判断に影響

---

## 3. 4つのセグメントバッファ設計(1分30秒)

**decoderBufferの設計:**
```
buf: [...................................]
0 prevStart prevEnd len cap
| | | | |
既読 前回値 未読 未使用

不変条件: 0 ≤ prevStart ≤ prevEnd ≤ len(buf) ≤ cap(buf)
具体例:
json{"name":"Alice","age":30}
```

"Alice"を読んだ直後:
```
buf: [{"name":"Alice","age":30}]
↑ ↑
prevStart prevEnd

既読部分: {"name":"Alice"
前回の値: "Alice" ← エラー報告に使用
未読部分: ,"age":30}
```

この設計の利点:

- エラー時に問題の値を正確に報告できる
- ストリーミング処理が効率的
- メモリを節約しながら必要な情報を保持

**学べるポイント:**

- 状態管理の設計パターン
- エラーハンドリングを考慮した設計

## 4. まとめ(30秒)
v2から学べる実践的な設計パターン:

- 責務の分離 - 低レベルと高レベルを明確に分ける
- 安全な内部API - export + internal パッケージ
- オブジェクト再利用 - sync.Poolの効果的な使用
- 状態管理 - 4セグメント設計

これらは標準ライブラリだけでなく、日々の開発でも応用できる考え方です。

倉澤大樹 Hiroki Kurasawa

株式会社ZOZOバックエンドエンジニア

Actions

Please note that Sessionize is not responsible for the accuracy or validity of the data provided by speakers. If you suspect this profile to be fake or spam, please let us know.

Jump to top