[インデックス 15985] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http パッケージにおけるHTTPサーバーの応答ヘッダー処理に関する改善とテストの追加を目的としています。具体的には、http.ResponseWriter の Write, WriteHeader, Header, Flush メソッドの呼び出し順序と、それらがHTTP応答ヘッダーに与える影響について、より堅牢なテストを追加し、関連するコードのコメントを改善しています。さらに、特定のシナリオにおけるメモリアロケーションを削減する最適化も含まれています。
コミット
commit 972cb4b442d6348868051c71304b348992519f4a
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Mar 28 11:35:24 2013 -0700
net/http: more tests, better comments, remove an allocation
Add more tests around the various orders handlers can access
and flush response headers.
Also clarify the documentation on fields of response and
chunkWriter.
While there, remove an allocation (a header clone) for simple
handlers.
benchmark old ns/op new ns/op delta
BenchmarkServerFakeConnWithKeepAliveLite 15245 14966 -1.83%
benchmark old allocs new allocs delta
BenchmarkServerFakeConnWithKeepAliveLite 24 23 -4.17%
benchmark old bytes new bytes delta
BenchmarkServerFakeConnWithKeepAliveLite 1717 1668 -2.85%
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/8101043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/972cb4b442d6348868051c71304b348992519f4a
元コミット内容
net/http: more tests, better comments, remove an allocation
このコミットは、net/http パッケージに対して以下の変更を行います。
- ハンドラが応答ヘッダーにアクセスし、フラッシュする様々な順序に関するテストを追加します。
responseおよびchunkWriter構造体のフィールドに関するドキュメントを明確化します。- その過程で、シンプルなハンドラの場合にヘッダーのクローン作成というアロケーションを削除します。
ベンチマーク結果:
BenchmarkServerFakeConnWithKeepAliveLite
ns/op(操作あたりのナノ秒): 15245 -> 14966 (-1.83%)allocs(アロケーション数): 24 -> 23 (-4.17%)bytes(割り当てられたバイト数): 1717 -> 1668 (-2.85%)
変更の背景
このコミットの背景には、Go 1.0 と Go 1.1 における http.ResponseWriter の WriteHeader メソッドの挙動の違いがあります。
Go 1.0 では、rw.WriteHeader が呼び出されると、応答ヘッダーが即座にワイヤー(ネットワーク)にフラッシュされていました。これは、ハンドラが WriteHeader の呼び出し後にヘッダーを変更した場合、その変更がクライアントに送信される応答には反映されないことを意味します。
しかし、Go 1.1 では、実際のワイヤーへのフラッシュが遅延されるように変更されました。この変更の意図は、サーバーがより多くの(またはすべての)出力を見てから Content-Length やより適切な Content-Type を追加できるようにするためです。例えば、ハンドラが rw.Write を呼び出すまで Content-Length が不明な場合、Go 1.1 では最初の Write が行われるまでヘッダーの送信を遅延させることで、正確な Content-Length を含めることが可能になります。
この遅延フラッシュの挙動はパフォーマンスや柔軟性の向上に寄与しますが、Go 1.0 との互換性を維持するためには注意が必要です。特に、ハンドラが(誤って)最初の Write の後にヘッダーを変更した場合でも、WriteHeader が呼び出された時点でのヘッダーの状態が保持され、それがワイヤーに送信されるようにする必要があります。このコミットは、この互換性の問題に対処し、ヘッダーのライフサイクルをより明確にするためのものです。
また、net/http パッケージはGo言語のウェブアプリケーション開発の基盤であり、そのパフォーマンスは非常に重要です。不要なメモリアロケーションを削減することは、特に高負荷な環境においてサーバーの効率を向上させる上で役立ちます。このコミットは、シンプルなハンドラにおけるヘッダーのクローン作成というアロケーションを削除することで、このパフォーマンス改善にも貢献しています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の net/http パッケージに関する知識が必要です。
-
http.ResponseWriterインターフェース:- HTTPハンドラがクライアントに応答を書き込むために使用するインターフェースです。
- 主なメソッド:
Header() Header: 応答ヘッダーを表すhttp.Headerマップを返します。このマップを変更することで、応答ヘッダーを設定できます。WriteHeader(statusCode int): HTTPステータスコードを書き込み、ヘッダーの送信を開始します。このメソッドが呼び出されると、通常、ヘッダーはワイヤーにフラッシュされます。Write(data []byte) (int, error): 応答ボディにデータを書き込みます。WriteHeaderがまだ呼び出されていない場合、Writeは暗黙的にWriteHeader(http.StatusOK)を呼び出してからボディを書き込みます。
-
http.Header型:map[string][]stringのエイリアスであり、HTTPヘッダーのキーと値のペアを表現します。キーは大文字・小文字を区別しません。
-
HTTP応答のライフサイクル:
- HTTPハンドラがリクエストを受け取ると、
http.ResponseWriterと*http.Requestが渡されます。 - ハンドラは
rw.Header().Set("Key", "Value")のようにヘッダーを設定します。 - ハンドラは
rw.WriteHeader(statusCode)を呼び出してステータスコードを設定し、ヘッダーの送信をトリガーします。 - ハンドラは
rw.Write([]byte("body"))を呼び出して応答ボディを書き込みます。 WriteHeaderが明示的に呼び出されない場合でも、最初のWrite呼び出し時に暗黙的にWriteHeader(http.StatusOK)が呼び出されます。
- HTTPハンドラがリクエストを受け取ると、
-
http.Flusherインターフェース:http.ResponseWriterがこのインターフェースを実装している場合、Flush()メソッドを呼び出すことで、バッファリングされている応答データを強制的にクライアントに送信できます。これは、ストリーミング応答などで役立ちます。
-
Content-LengthとTransfer-Encoding: chunked:- HTTP応答のボディの長さをクライアントに伝えるためのヘッダーです。
Content-Lengthは、ボディの長さが事前にわかっている場合に使用されます。Transfer-Encoding: chunkedは、ボディの長さが事前にわからない場合や、データをストリーミングする場合に使用されます。この場合、ボディはチャンク(塊)に分割され、各チャンクの前にその長さが記述されます。
-
メモリアロケーションとパフォーマンス:
- Go言語では、オブジェクトがヒープに割り当てられると、ガベージコレクションの対象となります。ガベージコレクションはオーバーヘッドを伴うため、不要なアロケーションを削減することはパフォーマンス向上につながります。
- 特にHTTPサーバーのような高頻度でリクエストを処理するシステムでは、小さなアロケーションの削減でも全体のスループットに大きな影響を与えることがあります。
技術的詳細
このコミットの技術的詳細は、主に net/http パッケージ内の response 構造体と chunkWriter 構造体の内部挙動、およびそれらがHTTP応答ヘッダーの送信にどのように関与しているかに焦点を当てています。
response 構造体 (src/pkg/net/http/server.go)
response 構造体は、HTTPリクエストに対するサーバー側の応答を管理します。このコミットでは、以下のフィールドが追加・変更されています。
handlerHeader Header: これはハンドラがrw.Header()を通じてアクセスし、変更するヘッダーマップです。calledHeader bool: 新しく追加されたフィールドで、ハンドラがrw.Header()メソッドを呼び出したかどうかを追跡します。
response.Header() メソッドの変更点:
以前は、response.Header() が呼び出されると、w.cw.header が w.handlerHeader のクローンで初期化される可能性がありました。このコミットでは、w.calledHeader = true が設定されるようになり、w.cw.header のクローン作成ロジックが WriteHeader メソッドに移動しました。
response.WriteHeader(code int) メソッドの変更点:
このメソッドは、HTTPステータスコードを設定し、ヘッダーの送信を論理的に開始します。
重要な変更は、w.cw.header = w.handlerHeader.clone() の行が条件付きになったことです。
if w.calledHeader && w.cw.header == nil { w.cw.header = w.handlerHeader.clone() }
これは、ハンドラが rw.Header() を明示的に呼び出し(w.calledHeader が true)、かつ chunkWriter のヘッダーがまだクローンされていない場合にのみ、ヘッダーのクローンを作成することを意味します。
また、Content-Length のチェックが w.cw.header.get から w.handlerHeader.get に変更され、Content-Length の削除も w.cw.header.Del から w.handlerHeader.Del に変更されました。これにより、handlerHeader が直接操作されるようになります。
chunkWriter 構造体 (src/pkg/net/http/server.go)
chunkWriter 構造体は、応答ボディの書き込みと、必要に応じてチャンクエンコーディングを処理します。この構造体は、実際のHTTPヘッダーをワイヤーに書き込む役割を担います。
header Header: このフィールドは、ワイヤーに書き込まれる実際のヘッダーマップです。wroteHeader bool: このフィールドは、ヘッダーが実際にワイヤーに書き込まれたかどうかを示します。
chunkWriter.writeHeader(p []byte) メソッドの変更点:
このメソッドは、応答ヘッダーをワイヤーに書き込む実際のロジックを含んでいます。
最も重要な変更は、cw.header の初期化ロジックです。
以前は、cw.header = w.handlerHeader.clone() が無条件に実行されていました。
新しいロジックでは、cw.header が nil の場合、以下の条件で初期化されます。
w.handlerDoneがtrueの場合(ハンドラが処理を完了し、これ以上ヘッダーを変更しないことが保証される場合)、cw.headerはw.handlerHeaderを直接参照します。これにより、不要なクローン作成が回避されます。w.handlerDoneがfalseの場合(ハンドラがまだヘッダーを変更する可能性がある場合)、cw.headerはw.handlerHeader.clone()を呼び出して、ヘッダーマップのスナップショットを作成します。これは、Go 1.0 との互換性を維持するためであり、ハンドラがWriteHeader呼び出し後にヘッダーを(誤って)変更しても、その変更がワイヤーに送信されるヘッダーに影響を与えないようにするためです。
メモリアロケーションの削減
このコミットの主要な最適化は、シンプルなハンドラ(rw.Header() を明示的に呼び出さないハンドラ)の場合に、ヘッダーのクローン作成を回避することです。
- Go 1.0 の挙動:
WriteHeaderが呼び出されると、handlerHeaderのクローンがchunkWriter.headerに格納され、それがワイヤーに送信されました。これは、ハンドラがrw.Header()を呼び出したかどうかにかかわらず発生しました。 - Go 1.1 の変更(このコミット):
- ハンドラが
rw.Header()を一度も呼び出さなかった場合(response.calledHeaderがfalse)、response.WriteHeaderはヘッダーのクローンを作成しません。 chunkWriter.writeHeaderが呼び出された際、response.handlerDoneがtrueであれば(つまり、ハンドラが処理を終えており、これ以上ヘッダーを変更しないことが確実であれば)、chunkWriter.headerはresponse.handlerHeaderを直接参照します。- これにより、シンプルなハンドラではヘッダーのクローン作成というアロケーションが完全に回避され、パフォーマンスが向上します。
- ハンドラが
テストの追加 (src/pkg/net/http/serve_test.go)
TestHeaderToWire という新しいテスト関数が追加され、WriteHeader, Write, Header, Flush の様々な組み合わせにおけるヘッダーの挙動が検証されています。これにより、Go 1.1 の遅延フラッシュの挙動がGo 1.0 との互換性を保ちつつ正しく機能することが保証されます。
テストケースの例:
write without Header:Writeのみ呼び出し、Content-LengthとContent-Typeが自動的に設定されることを確認。Header mutation before write:Headerを変更後Writeを呼び出し、Write後にHeaderを変更しても反映されないことを確認。flush then write:Flush後にWriteを呼び出し、チャンクエンコーディングが使用され、Flush後にHeaderを変更しても反映されないことを確認。sniff-on-first-write content-type: 最初のWriteでコンテンツタイプがスニッフィングされ、明示的なContent-Typeがない場合にtext/htmlなどが設定されることを確認。explicit content-type wins: 明示的に設定されたContent-Typeがスニッフィング結果よりも優先されることを確認。empty handler: 空のハンドラの場合にContent-Type: text/plainとContent-Length: 0が設定されることを確認。WriteHeader call:WriteHeader呼び出し後にHeaderを変更しても反映されないことを確認。
これらのテストは、net/http パッケージの堅牢性を高め、開発者が ResponseWriter の挙動をより正確に理解するのに役立ちます。
ベンチマーク結果の解釈
コミットメッセージに含まれるベンチマーク結果は、この変更がパフォーマンスに与える好影響を示しています。
BenchmarkServerFakeConnWithKeepAliveLiteは、最もシンプルなHTTPリクエストとハンドラ(rw.Header()を呼び出さない)をシミュレートするベンチマークです。ns/op(操作あたりのナノ秒): 15245 -> 14966 (-1.83%)- これは、1つのHTTPリクエストを処理するのにかかる時間が約1.83%短縮されたことを意味します。
allocs(アロケーション数): 24 -> 23 (-4.17%)- これは、1つのHTTPリクエスト処理あたりのメモリアロケーション数が1つ削減されたことを意味します。この1つの削減は、まさにヘッダーのクローン作成が回避されたことによるものです。
bytes(割り当てられたバイト数): 1717 -> 1668 (-2.85%)- これは、1つのHTTPリクエスト処理あたりに割り当てられるメモリ量が約2.85%削減されたことを意味します。
これらの結果は、特に高負荷なHTTPサーバーにおいて、このコミットが全体的なスループットとメモリ効率の向上に貢献することを示しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルと行に集中しています。
-
src/pkg/net/http/serve_test.go:TestHeaderToWire関数が新規追加されました (約200行の追加)。この関数は、WriteHeader,Write,Header,Flushの様々な組み合わせにおけるヘッダーの挙動を検証する多数のテストケースを含んでいます。BenchmarkServerFakeConnWithKeepAliveLiteベンチマーク関数が新規追加されました (約30行の追加)。これは、最もシンプルなハンドラにおけるパフォーマンスを測定します。
-
src/pkg/net/http/server.go:chunkWriter構造体の定義が変更されました。header Headerフィールドのコメントがより詳細になりました。wroteHeader boolフィールドのコメントがより詳細になりました。
response構造体の定義にcalledHeader boolフィールドが追加されました。response.Header()メソッドの変更。w.calledHeader = trueの設定が追加されました。- 条件付きのヘッダークローンロジックが削除されました(
WriteHeaderに移動)。
response.WriteHeader(code int)メソッドの変更。w.cw.header = w.handlerHeader.clone()の行がif w.calledHeader && w.cw.header == nil { ... }という条件付きのブロック内に移動しました。Content-Lengthの取得と削除がw.cw.headerからw.handlerHeaderに変更されました。
chunkWriter.writeHeader(p []byte)メソッドの変更。cw.headerの初期化ロジックが大幅に変更され、w.handlerDoneの状態に基づいてクローンを作成するか、直接参照するかが決定されるようになりました。Content-Lengthの設定ロジックがcw.header.getからw.handlerDone && cw.header.getに変更されました。
コアとなるコードの解説
src/pkg/net/http/server.go の変更点詳細
type chunkWriter struct の変更
type chunkWriter struct {
res *response
// header is either the same as res.handlerHeader,
// or a deep clone if the handler called Header.
header Header
// wroteHeader tells whether the header's been written to "the
// wire" (or rather: w.conn.buf). this is unlike
// (*response).wroteHeader, which tells only whether it was
// logically written.
wroteHeader bool
// ... (他のフィールドは省略)
}
headerフィールドのコメントが更新され、res.handlerHeaderと同じであるか、ハンドラがHeader()を呼び出した場合にディープクローンであるかが明確にされました。wroteHeaderフィールドのコメントも更新され、ヘッダーが物理的にワイヤーに書き込まれた状態を示すことが強調されました。これは(*response).wroteHeader(論理的に書き込まれたかを示す)とは異なる点が明記されています。
type response struct の変更
type response struct {
// ... (他のフィールドは省略)
// handlerHeader is copied into cw.header at WriteHeader
// time, and privately mutated thereafter.
handlerHeader Header
calledHeader bool // handler accessed handlerHeader via Header
// ... (他のフィールドは省略)
}
calledHeader boolフィールドが追加されました。これは、ハンドラがrw.Header()メソッドを呼び出してhandlerHeaderにアクセスしたかどうかを追跡するためのものです。このフラグは、ヘッダーのクローン作成を最適化するために使用されます。
func (w *response) Header() Header の変更
func (w *response) Header() Header {
if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeader {
// Accessing the header between logically writing it
// and physically writing it means we need to allocate
// a clone to snapshot the logically written state.
w.cw.header = w.handlerHeader.clone()
}
w.calledHeader = true
return w.handlerHeader
}
w.calledHeader = trueが追加されました。これにより、ハンドラがHeader()メソッドを呼び出したことが記録されます。if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeaderの条件は、ヘッダーが論理的に書き込まれた(w.wroteHeaderがtrue)が、まだ物理的にワイヤーに書き込まれていない(!w.cw.wroteHeader)状態で、かつchunkWriterのヘッダーがまだ設定されていない場合に、handlerHeaderのクローンを作成してw.cw.headerに割り当てるためのものです。これは、ハンドラがWriteHeaderを呼び出した後にHeader()を呼び出してヘッダーを変更しようとした場合に、その時点でのヘッダーの状態をスナップショットとして保持するための互換性ロジックです。
func (w *response) WriteHeader(code int) の変更
func (w *response) WriteHeader(code int) {
// ... (既存のロジック)
w.wroteHeader = true
w.status = code
if w.calledHeader && w.cw.header == nil {
w.cw.header = w.handlerHeader.clone()
}
if cl := w.handlerHeader.get("Content-Length"); cl != "" {
v, err := strconv.ParseInt(cl, 10, 64)
if err == nil && v >= 0 {
w.contentLength = v
} else {
log.Printf("http: invalid Content-Length of %q", cl)
w.handlerHeader.Del("Content-Length")
}
}
}
if w.calledHeader && w.cw.header == nil { w.cw.header = w.handlerHeader.clone() }が追加されました。これは、ハンドラがHeader()を呼び出しており(w.calledHeaderがtrue)、かつchunkWriterのヘッダーがまだクローンされていない場合にのみ、handlerHeaderのクローンを作成してw.cw.headerに割り当てます。これにより、Header()を呼び出さないシンプルなハンドラでは、このクローン作成が回避され、アロケーションが削減されます。Content-Lengthの取得と削除の対象がw.cw.headerからw.handlerHeaderに変更されました。これは、handlerHeaderがハンドラが操作する「真の」ヘッダーマップであることを反映しています。
func (cw *chunkWriter) writeHeader(p []byte) の変更
func (cw *chunkWriter) writeHeader(p []byte) {
// ... (既存のロジック)
cw.wroteHeader = true
w := cw.res
if cw.header == nil {
if w.handlerDone {
// The handler won't be making further changes to the
// response header map, so we use it directly.
cw.header = w.handlerHeader
} else {
// Snapshot the header map, since it might be some
// time before we actually write w.cw to the wire and
// we don't want the handler's potential future
// (arguably buggy) modifications to the map to make
// it into the written headers. This preserves
// compatibility with Go 1.0, which always flushed the
// headers on a call to rw.WriteHeader.
cw.header = w.handlerHeader.clone()
}
}
// If the handler is done but never sent a Content-Length
// response header and this is our first (and last) write, set
// it, even to zero. This helps HTTP/1.0 clients keep their
// "keep-alive" connections alive.
if w.handlerDone && cw.header.get("Content-Length") == "" && w.req.Method != "HEAD" {
w.contentLength = int64(len(p))
cw.header.Set("Content-Length", strconv.Itoa(len(p)))
}
// ... (既存のロジック)
code := w.status
// ... (既存のロジック)
}
cw.headerの初期化ロジックが大幅に変更されました。if cw.header == nilブロック内で、w.handlerDoneの値に基づいてcw.headerの設定方法が分岐します。w.handlerDoneがtrueの場合(ハンドラが処理を完了し、これ以上ヘッダーを変更しないことが保証される場合)、cw.headerはw.handlerHeaderを直接参照します。これにより、不要なクローン作成が回避されます。w.handlerDoneがfalseの場合(ハンドラがまだヘッダーを変更する可能性がある場合)、cw.headerはw.handlerHeader.clone()を呼び出して、ヘッダーマップのスナップショットを作成します。これは、Go 1.0 との互換性を維持するためであり、ハンドラがWriteHeader呼び出し後にヘッダーを(誤って)変更しても、その変更がワイヤーに送信されるヘッダーに影響を与えないようにするためです。
Content-Lengthの自動設定ロジックの条件がdoneからw.handlerDoneに変更されました。これは、response構造体のhandlerDoneフィールドが、ハンドラの完了状態を正確に反映していることを利用しています。code := w.statusの行が、cw.headerの初期化ロジックの後に移動しました。これは、codeがcw.headerの初期化ロジックに影響を与えないため、より適切な位置に配置されたものです。
src/pkg/net/http/serve_test.go の変更点詳細
func TestHeaderToWire(t *testing.T) の追加
このテスト関数は、net/http パッケージの ResponseWriter がヘッダーをワイヤーに書き込む際の様々な挙動を検証するために追加されました。特に、Write, WriteHeader, Header, Flush の呼び出し順序がヘッダーにどのように影響するかを詳細にテストします。
各テストケースは、handler 関数(テスト対象の http.HandlerFunc)と check 関数(応答の出力が期待通りであるかを検証する)のペアで構成されます。
主要なテストシナリオ:
write without Header:rw.Writeのみ呼び出した場合に、Content-LengthとContent-Type: text/plainが自動的に設定されることを確認します。Header mutation before write:rw.Header()でヘッダーを設定し、rw.Writeを呼び出した後、再度rw.Header()でヘッダーを変更しても、最初のWrite時点のヘッダーが送信されることを確認します。これは、WriteHeaderが論理的に呼び出された後にヘッダーがスナップショットされる挙動を検証します。write then useless Header mutation:rw.Writeを呼び出した後、rw.Header().Setでヘッダーを変更しても、その変更が応答に含まれないことを確認します。flush then write:rw.(Flusher).Flush()を呼び出した後、rw.Writeを呼び出した場合に、Transfer-Encoding: chunkedが使用されることを確認します。また、Flush後にヘッダーを変更しても反映されないことを確認します。header then flush:rw.Header().Setでヘッダーを設定し、rw.(Flusher).Flush()を呼び出した後、rw.Writeを呼び出した場合に、設定したヘッダーが送信され、Transfer-Encoding: chunkedが使用されることを確認します。sniff-on-first-write content-type:Content-Typeを明示的に設定せずにHTMLコンテンツをrw.Writeした場合に、Content-Type: text/htmlが自動的にスニッフィングされて設定されることを確認します。explicit content-type wins:Content-Typeを明示的に設定した場合に、コンテンツスニッフィングの結果よりも明示的な設定が優先されることを確認します。empty handler: ハンドラが何も書き込まない場合に、Content-Type: text/plainとContent-Length: 0が設定されることを確認します。only Header, no write:rw.Header().Setでヘッダーを設定し、WriteやWriteHeaderを呼び出さない場合に、設定したヘッダーが送信されることを確認します。WriteHeader call:rw.WriteHeader(404)を呼び出した後、rw.Header().Setでヘッダーを変更しても、その変更が応答に含まれないことを確認します。
これらのテストは、net/http パッケージの ResponseWriter の複雑な挙動を網羅的に検証し、Go 1.1 で導入された遅延フラッシュのロジックが期待通りに機能し、かつGo 1.0 との互換性が維持されていることを保証します。
func BenchmarkServerFakeConnWithKeepAliveLite(b *testing.B) の追加
このベンチマーク関数は、BenchmarkServerFakeConnWithKeepAlive と同様ですが、よりシンプルなシナリオを想定しています。具体的には、ハンドラが rw.Header() を明示的に呼び出さない場合を測定します。
func BenchmarkServerFakeConnWithKeepAliveLite(b *testing.B) {
b.ReportAllocs()
req := []byte(strings.Replace(`GET / HTTP/1.1
Host: golang.org
`, "\n", "\r\n", -1))
res := []byte("Hello world!\n")
conn := &rwTestConn{
Reader: &repeatReader{content: req, count: b.N},
Writer: ioutil.Discard,
closec: make(chan bool, 1),
}
handled := 0
handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
handled++
rw.Write(res)
})
ln := &oneConnListener{conn: conn}
go Serve(ln, handler)
<-conn.closec
if b.N != handled {
b.Errorf("b.N=%d but handled %d", b.N, handled)
}
}
- このベンチマークは、
rw.Header()を呼び出さないハンドラが、ヘッダーのクローン作成を回避できることによるパフォーマンス向上を具体的に測定するために追加されました。 b.ReportAllocs()が呼び出されており、アロケーション数も報告されるように設定されています。これにより、メモリアロケーションの削減効果が数値として確認できます。
関連リンク
- Go言語の
net/httpパッケージのドキュメント: https://pkg.go.dev/net/http - Go 1.1 リリースノート (HTTP/2.0 のサポートなど、関連する変更点が含まれる可能性があります): https://go.dev/doc/go1.1 (当時のリリースノートはGo 1.1のページに統合されている可能性があります)
- Go言語の
http.ResponseWriterインターフェースに関する議論やドキュメント: https://pkg.go.dev/net/http#ResponseWriter
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (
src/pkg/net/http/server.go,src/pkg/net/http/serve_test.go) - コミットメッセージに記載されている Go CL (Code Review) リンク: https://golang.org/cl/8101043 (このリンクは古い可能性があり、現在のGoのコードレビューシステムでは異なるURL形式になっている可能性がありますが、当時のCLへの参照として重要です。)
- Go言語のHTTPサーバーの内部実装に関する一般的な知識とベストプラクティス。
- HTTP/1.1 の仕様 (RFC 2616 または後継の RFC 7230-7235) におけるヘッダー処理、
Content-Length、Transfer-Encoding: chunkedに関する情報。 - Go言語のベンチマークに関するドキュメント: https://go.dev/doc/articles/go_benchmarking_code (当時の情報源は異なる可能性がありますが、ベンチマークの概念理解に役立ちます)
- Go言語のガベージコレクションとメモリアロケーションに関する一般的な情報。
- Go 1.0 と Go 1.1 のリリースノートや変更履歴(当時の情報源を特定するのは困難ですが、Goの公式ブログやメーリングリストのアーカイブが参考になる可能性があります)。
- Go言語の
net/httpパッケージの進化に関するブログ記事や技術解説。