[インデックス 17444] ファイルの概要
このコミットは、Go言語の標準ライブラリimage/color
パッケージからPlan9Palette
とWebSafePalette
という2つの標準カラーパレットを、image/color/palette
という新しい独立したパッケージに移動させるものです。この変更の主な目的は、これらのパレットが持つ初期化時のコード(特にインターフェース変換に関連する約40KiBのテキストデータ)が、image/color
パッケージをインポートする全てのGoプログラムに不必要な負担をかけることを避けるためです。
コミット
- コミットハッシュ:
aeb8b45866b8e9664d931c1c297b122237fe679e
- Author: Nigel Tao nigeltao@golang.org
- Date: Fri Aug 30 16:03:16 2013 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/aeb8b45866b8e9664d931c1c297b122237fe679e
元コミット内容
image/color/palette: move Plan9Palette and WebSafePalette out of the
image/color package into their own package. They require some non-
trivial init-time code (interface conversions, currently 40KiB of text)
that would otherwise burden any Go program that imported image/color.
R=r
CC=golang-dev
https://golang.org/cl/13256046
変更の背景
この変更の背景には、Go言語のパッケージ設計におけるパフォーマンスと効率性の考慮があります。image/color
パッケージは、画像処理において基本的な色表現や色空間変換を提供する非常に汎用的なパッケージです。しかし、このパッケージ内にPlan9Palette
とWebSafePalette
という具体的なカラーパレットの定義が含まれていました。
これらのパレットは、それぞれ256色と216色のRGBA値の配列として定義されており、その初期化には「非自明な初期化時コード(non-trivial init-time code)」、特に「インターフェース変換(interface conversions)」が伴っていました。コミットメッセージによると、この初期化コードは「現在40KiBのテキスト」という、比較的小さなパッケージにとっては無視できないサイズを持っていました。
Go言語では、パッケージがインポートされると、そのパッケージのinit
関数やトップレベル変数の初期化が実行されます。もしPlan9Palette
やWebSafePalette
がimage/color
パッケージ内に存在し続けると、image/color
をインポートする全てのGoプログラムは、たとえこれらの特定のパレットを使用しない場合でも、その40KiBの初期化コードをロードし、実行する必要がありました。これは、特にリソースが限られた環境や、起動時間を重視するアプリケーションにおいて、不必要なメモリフットプリントと起動時間の増加という「負担(burden)」となっていました。
このコミットは、このような不必要なオーバーヘッドを削減し、image/color
パッケージの軽量性を保つことを目的としています。パレットを独立したimage/color/palette
パッケージに分離することで、これらのパレットが必要なプログラムだけが新しいパッケージをインポートし、その初期化コストを支払うように設計が変更されました。これにより、Goプログラム全体の効率性が向上します。
前提知識の解説
Go言語のパッケージとinit
関数
Go言語では、コードは「パッケージ」という単位で管理されます。パッケージは関連する機能の集合であり、他のパッケージからインポートして利用できます。Goプログラムが実行される際、インポートされたパッケージは、そのパッケージ内のinit
関数が自動的に実行されます。init
関数は、パッケージがロードされる際に一度だけ実行され、パッケージレベルの変数の初期化や、プログラムの実行に必要なセットアップ処理などを行います。
このコミットの文脈では、Plan9Palette
やWebSafePalette
のようなグローバル変数の初期化が、Goの型システムにおけるインターフェース変換を伴うため、コンパイルされたバイナリサイズや実行時の初期化コストに影響を与えていました。
image/color
パッケージ
image/color
パッケージは、Go言語の標準ライブラリの一部であり、画像処理における色の表現と変換に関する基本的な機能を提供します。color.Color
インターフェースは、任意の色モデル(RGB, CMYKなど)の色を抽象化し、RGBA
構造体は赤、緑、青、アルファの各成分を持つ色を表現します。このパッケージは、画像データの操作や表示において中心的な役割を果たします。
カラーパレット(Color Palette)
カラーパレットは、画像が使用できる色の有限なセットを定義するものです。特に、インデックスカラー画像(GIFなど)では、各ピクセルが直接色情報を持つのではなく、パレット内の色のインデックスを参照します。
- Plan9Palette: Plan 9オペレーティングシステムで使われていた256色のパレットです。24ビットRGB空間を4x4x4のサブディビジョンに分割し、各サブキューブに4つのシェードを持つことで、連続的なトーンの表現に優れています。
- WebSafePalette: 初期バージョンのNetscape Navigatorで普及した216色のパレットで、「Webセーフカラー」または「Netscapeカラーキューブ」としても知られています。これは、異なるプラットフォームやブラウザで一貫して表示されることを意図した色のセットです。
インターフェース変換とパフォーマンス
Go言語では、具体的な型をインターフェース型に変換する際に、実行時コストが発生する場合があります。特に、大きなデータ構造(この場合は多数のcolor.RGBA
構造体からなる配列)を[]color.Color
のようなインターフェースのスライスに変換する場合、各要素に対してインターフェース値の構築(型情報とデータポインタのペアの作成)が必要となり、これが初期化時のCPU時間とメモリ使用量に影響を与える可能性があります。コミットメッセージで言及されている「40KiBのテキスト」は、この初期化コードがコンパイルされたバイナリサイズに与える影響を示唆しています。
技術的詳細
このコミットの技術的な核心は、Go言語のパッケージシステムと初期化メカニズムを最適化することにあります。
-
パッケージの分離:
- 既存の
src/pkg/image/color/palette.go
ファイル(Plan9Palette
とWebSafePalette
の定義が含まれていた)が削除されました。 - 新しいパッケージ
image/color/palette
が作成され、その中にsrc/pkg/image/color/palette/palette.go
としてパレットの定義が移動されました。 - パレットを生成するスクリプト
gen.go
もsrc/pkg/image/color/gen.go
からsrc/pkg/image/color/palette/gen.go
に移動し、新しいパッケージ名palette
を使用するように変更されました。
- 既存の
-
初期化コストの削減:
Plan9Palette
とWebSafePalette
は、[]color.Color
型の変数として定義されています。color.RGBA
はcolor.Color
インターフェースを実装しているため、これらのパレットの初期化時には、color.RGBA
の具体的な値がcolor.Color
インターフェース値に変換されるプロセスが発生します。- このインターフェース変換は、パレットの要素数(256色や216色)が多いため、それなりのCPUサイクルとメモリ割り当てを必要とします。
- パレットを独立したパッケージに移動することで、
image/color
パッケージ自体はこれらのパレットの初期化コストを負担しなくなります。image/color
をインポートするだけのプログラムは、この初期化処理をスキップできます。
-
依存関係の変更:
image/gif
パッケージなど、以前image/color
パッケージ内のパレットを参照していたコードは、新しいimage/color/palette
パッケージをインポートし、そこからパレットを参照するように変更されました。- 例えば、
image/gif/writer.go
では、color.Plan9Palette
の代わりにpalette.Plan9
を使用するように変更されています。これにより、image/gif
パッケージは明示的にパレットパッケージに依存するようになります。
この変更は、Goの標準ライブラリが、利用されない機能の初期化コストを最小限に抑えるように設計されていることの一例を示しています。これは、Goの「シンプルさ」と「効率性」という設計哲学に合致するものです。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが変更されています。
-
src/pkg/image/color/palette.go
:- このファイルは完全に削除されました。以前は
Plan9Palette
とWebSafePalette
の定義が含まれていました。
- このファイルは完全に削除されました。以前は
-
src/pkg/image/color/{ => palette}/gen.go
:src/pkg/image/color/gen.go
がsrc/pkg/image/color/palette/gen.go
にリネームされました。- 生成される
palette.go
ファイルのパッケージ宣言がpackage color
からpackage palette
に変更されました。 import "image/color"
が追加され、パレットの要素型がRGBA
からcolor.RGBA
に、パレット変数名がPlan9Palette
からPlan9
に、WebSafePalette
からWebSafe
に変更されました。これにより、新しいpalette
パッケージがimage/color
パッケージの型を使用するようになります。
-
src/pkg/image/color/palette/palette.go
:- この新しいファイルが作成されました。
- 削除された
src/pkg/image/color/palette.go
の内容が、新しいパッケージ名と型参照に合わせて修正された上で、このファイルに移動されました。 Plan9
とWebSafe
という変数名で、それぞれPlan 9パレットとWebセーフパレットが[]color.Color
型として定義されています。
-
src/pkg/image/gif/reader_test.go
:- GIFテストコード内の定数名が、
header
からheaderStr
、palette
からpaletteStr
、trailer
からtrailerStr
に変更されました。これは直接的な機能変更ではなく、コードの可読性向上のためのリファクタリングです。
- GIFテストコード内の定数名が、
-
src/pkg/image/gif/writer.go
:image/color/palette
パッケージがインポートされました。Options
構造体のコメントで、color.Plan9Palette
がpalette.Plan9
に置き換えられました。Encode
関数内で、image.NewPaletted
を呼び出す際にcolor.Plan9Palette[:opts.NumColors]
の代わりにpalette.Plan9[:opts.NumColors]
が使用されるように変更されました。
コアとなるコードの解説
このコミットの核心は、Plan9Palette
とWebSafePalette
という大きな定数データとその初期化ロジックを、image/color
パッケージからimage/color/palette
という独立したパッケージに分離した点にあります。
src/pkg/image/color/palette/gen.go
の変更
gen.go
は、Plan9
とWebSafe
パレットのGoソースコードを生成するためのスクリプトです。
変更前はpackage color
としてpalette.go
を生成していましたが、変更後はpackage palette
として生成するように変わりました。
また、生成されるコード内でcolor.RGBA
のようにimage/color
パッケージの型を明示的に参照するように変更されました。これにより、palette
パッケージはimage/color
パッケージに依存する形になります。
// 変更前 (src/pkg/image/color/gen.go)
// fmt.Println("package color")
// fmt.Sprintf("\\tRGBA{0x%02x, 0x%02x, 0x%02x, 0xff},", c[0], c[1], c[2])
// fmt.Println("var Plan9Palette = []Color{")
// fmt.Println("var WebSafePalette = []Color{")
// 変更後 (src/pkg/image/color/palette/gen.go)
fmt.Println("// Package palette provides standard color palettes.")
fmt.Println("package palette")
fmt.Println()
fmt.Println(`import "image/color"`) // image/colorパッケージをインポート
// ...
fmt.Sprintf("\\tcolor.RGBA{0x%02x, 0x%02x, 0x%02x, 0xff},", c[0], c[1], c[2]) // color.RGBAを使用
// ...
fmt.Println("var Plan9 = []color.Color{") // 変数名をPlan9に変更し、型をcolor.Colorに
// ...
fmt.Println("var WebSafe = []color.Color{") // 変数名をWebSafeに変更し、型をcolor.Colorに
src/pkg/image/color/palette/palette.go
の新規作成
このファイルは、gen.go
によって生成される実際のパレットデータを含むファイルです。
新しいpalette
パッケージに属し、image/color
パッケージをインポートしてcolor.Color
型を使用します。
// generated by go run gen.go; DO NOT EDIT
// Package palette provides standard color palettes.
package palette
import "image/color"
// Plan9 is a 256-color palette that partitions the 24-bit RGB space
// ... (パレットのRGBA値の定義)
var Plan9 = []color.Color{
color.RGBA{0x00, 0x00, 0x00, 0xff},
// ... 256色の定義
}
// WebSafe is a 216-color palette that was popularized by early versions
// ... (パレットのRGBA値の定義)
var WebSafe = []color.Color{
color.RGBA{0x00, 0x00, 0x00, 0xff},
// ... 216色の定義
}
これにより、Plan9
とWebSafe
というパレットデータは、image/color/palette
パッケージがインポートされたときにのみ初期化されるようになります。
src/pkg/image/gif/writer.go
の変更
image/gif
パッケージは、GIF画像の書き込みを行う際にPlan9Palette
を使用していたため、新しいimage/color/palette
パッケージをインポートするように変更されました。
// 変更前
// import "image/color"
// color.Plan9Palette is used in place of a nil Quantizer.
// pm = image.NewPaletted(b, color.Plan9Palette[:opts.NumColors])
// 変更後
import (
"errors"
"image"
"image/color"
"image/color/palette" // 新しいパッケージをインポート
"image/draw"
"io"
)
// Quantizer is used to produce a palette with size NumColors.
// palette.Plan9 is used in place of a nil Quantizer. // コメントも更新
Quantizer draw.Quantizer
// ...
func Encode(w io.Writer, m image.Image, o *Options) error {
// ...
if !ok || len(pm.Palette) > opts.NumColors {
// TODO: Pick a better sub-sample of the Plan 9 palette.
pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors]) // 新しいパッケージのパレットを参照
// ...
}
// ...
}
この変更により、image/gif
パッケージは、image/color
パッケージから直接パレットデータに依存するのではなく、image/color/palette
パッケージを介して依存するようになりました。これにより、image/color
パッケージの依存関係がクリーンになり、不要な初期化コストが削減されます。
関連リンク
- Go CL: https://golang.org/cl/13256046
- GitHub Commit: https://github.com/golang/go/commit/aeb8b45866b8e9664d931c1c297b122237fe679e
参考にした情報源リンク
- Go言語のパッケージと初期化: https://go.dev/doc/effective_go#initialization
- Go言語のインターフェース: https://go.dev/doc/effective_go#interfaces
- Plan 9 Operating System Color: http://plan9.bell-labs.com/magic/man2html/6/color
- Web colors - Web-safe colors: http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors
- Go image/color package documentation: https://pkg.go.dev/image/color
- Go image/color/palette package documentation (after this change): https://pkg.go.dev/image/color/palette