Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17444] ファイルの概要

このコミットは、Go言語の標準ライブラリimage/colorパッケージからPlan9PaletteWebSafePaletteという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パッケージは、画像処理において基本的な色表現や色空間変換を提供する非常に汎用的なパッケージです。しかし、このパッケージ内にPlan9PaletteWebSafePaletteという具体的なカラーパレットの定義が含まれていました。

これらのパレットは、それぞれ256色と216色のRGBA値の配列として定義されており、その初期化には「非自明な初期化時コード(non-trivial init-time code)」、特に「インターフェース変換(interface conversions)」が伴っていました。コミットメッセージによると、この初期化コードは「現在40KiBのテキスト」という、比較的小さなパッケージにとっては無視できないサイズを持っていました。

Go言語では、パッケージがインポートされると、そのパッケージのinit関数やトップレベル変数の初期化が実行されます。もしPlan9PaletteWebSafePaletteimage/colorパッケージ内に存在し続けると、image/colorをインポートする全てのGoプログラムは、たとえこれらの特定のパレットを使用しない場合でも、その40KiBの初期化コードをロードし、実行する必要がありました。これは、特にリソースが限られた環境や、起動時間を重視するアプリケーションにおいて、不必要なメモリフットプリントと起動時間の増加という「負担(burden)」となっていました。

このコミットは、このような不必要なオーバーヘッドを削減し、image/colorパッケージの軽量性を保つことを目的としています。パレットを独立したimage/color/paletteパッケージに分離することで、これらのパレットが必要なプログラムだけが新しいパッケージをインポートし、その初期化コストを支払うように設計が変更されました。これにより、Goプログラム全体の効率性が向上します。

前提知識の解説

Go言語のパッケージとinit関数

Go言語では、コードは「パッケージ」という単位で管理されます。パッケージは関連する機能の集合であり、他のパッケージからインポートして利用できます。Goプログラムが実行される際、インポートされたパッケージは、そのパッケージ内のinit関数が自動的に実行されます。init関数は、パッケージがロードされる際に一度だけ実行され、パッケージレベルの変数の初期化や、プログラムの実行に必要なセットアップ処理などを行います。

このコミットの文脈では、Plan9PaletteWebSafePaletteのようなグローバル変数の初期化が、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言語のパッケージシステムと初期化メカニズムを最適化することにあります。

  1. パッケージの分離:

    • 既存のsrc/pkg/image/color/palette.goファイル(Plan9PaletteWebSafePaletteの定義が含まれていた)が削除されました。
    • 新しいパッケージimage/color/paletteが作成され、その中にsrc/pkg/image/color/palette/palette.goとしてパレットの定義が移動されました。
    • パレットを生成するスクリプトgen.gosrc/pkg/image/color/gen.goからsrc/pkg/image/color/palette/gen.goに移動し、新しいパッケージ名paletteを使用するように変更されました。
  2. 初期化コストの削減:

    • Plan9PaletteWebSafePaletteは、[]color.Color型の変数として定義されています。color.RGBAcolor.Colorインターフェースを実装しているため、これらのパレットの初期化時には、color.RGBAの具体的な値がcolor.Colorインターフェース値に変換されるプロセスが発生します。
    • このインターフェース変換は、パレットの要素数(256色や216色)が多いため、それなりのCPUサイクルとメモリ割り当てを必要とします。
    • パレットを独立したパッケージに移動することで、image/colorパッケージ自体はこれらのパレットの初期化コストを負担しなくなります。image/colorをインポートするだけのプログラムは、この初期化処理をスキップできます。
  3. 依存関係の変更:

    • image/gifパッケージなど、以前image/colorパッケージ内のパレットを参照していたコードは、新しいimage/color/paletteパッケージをインポートし、そこからパレットを参照するように変更されました。
    • 例えば、image/gif/writer.goでは、color.Plan9Paletteの代わりにpalette.Plan9を使用するように変更されています。これにより、image/gifパッケージは明示的にパレットパッケージに依存するようになります。

この変更は、Goの標準ライブラリが、利用されない機能の初期化コストを最小限に抑えるように設計されていることの一例を示しています。これは、Goの「シンプルさ」と「効率性」という設計哲学に合致するものです。

コアとなるコードの変更箇所

このコミットでは、主に以下のファイルが変更されています。

  1. src/pkg/image/color/palette.go:

    • このファイルは完全に削除されました。以前はPlan9PaletteWebSafePaletteの定義が含まれていました。
  2. src/pkg/image/color/{ => palette}/gen.go:

    • src/pkg/image/color/gen.gosrc/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パッケージの型を使用するようになります。
  3. src/pkg/image/color/palette/palette.go:

    • この新しいファイルが作成されました。
    • 削除されたsrc/pkg/image/color/palette.goの内容が、新しいパッケージ名と型参照に合わせて修正された上で、このファイルに移動されました。
    • Plan9WebSafeという変数名で、それぞれPlan 9パレットとWebセーフパレットが[]color.Color型として定義されています。
  4. src/pkg/image/gif/reader_test.go:

    • GIFテストコード内の定数名が、headerからheaderStrpaletteからpaletteStrtrailerからtrailerStrに変更されました。これは直接的な機能変更ではなく、コードの可読性向上のためのリファクタリングです。
  5. src/pkg/image/gif/writer.go:

    • image/color/paletteパッケージがインポートされました。
    • Options構造体のコメントで、color.Plan9Palettepalette.Plan9に置き換えられました。
    • Encode関数内で、image.NewPalettedを呼び出す際にcolor.Plan9Palette[:opts.NumColors]の代わりにpalette.Plan9[:opts.NumColors]が使用されるように変更されました。

コアとなるコードの解説

このコミットの核心は、Plan9PaletteWebSafePaletteという大きな定数データとその初期化ロジックを、image/colorパッケージからimage/color/paletteという独立したパッケージに分離した点にあります。

src/pkg/image/color/palette/gen.go の変更

gen.goは、Plan9WebSafeパレットの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色の定義
}

これにより、Plan9WebSafeというパレットデータは、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パッケージの依存関係がクリーンになり、不要な初期化コストが削減されます。

関連リンク

参考にした情報源リンク