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

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

このコミットは、Go言語の標準ライブラリであるnetパッケージ内のhosts.goファイルに対する変更です。hosts.goは、システムがホスト名解決(DNSルックアップなど)を行う際に使用する/etc/hostsファイルのようなローカルのホストファイルに関するロジックを扱います。具体的には、ホストファイルの内容をキャッシュし、そのキャッシュの有効期限を管理する部分に修正が加えられています。

コミット

commit f837078c505aae1f2fae3d35c525d4666d582989
Author: Rui Ueyama <ruiu@google.com>
Date:   Wed Jun 11 20:33:44 2014 -0700

    net: do not call time.Now() twice
    
    LGTM=bradfitz
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/104080043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f837078c505aae1f2fae3d35c525d4666d582989

元コミット内容

net: do not call time.Now() twice

変更の背景

このコミットの背景には、パフォーマンス最適化とコードの効率化があります。元のコードでは、readHosts関数内でキャッシュの有効期限を設定する際に、time.Now()が複数回呼び出されていました。time.Now()はシステムコールを伴う可能性があり、頻繁に呼び出されるとオーバーヘッドが発生する可能性があります。

具体的には、readHosts関数内でホストファイルのキャッシュを更新する際、キャッシュの有効期限(hosts.expire)を設定するためにtime.Now().Add(cacheMaxAge)という式が使われていました。しかし、このreadHosts関数が呼び出される直前、または関数内の別の場所で既に現在の時刻を取得している場合、再度time.Now()を呼び出すことは冗長であり、わずかながらパフォーマンスの低下を招く可能性があります。

このコミットは、この冗長なtime.Now()の呼び出しを排除し、既に取得済みの時刻変数(now)を再利用することで、コードの効率性を向上させ、潜在的なパフォーマンスのボトルネックを解消することを目的としています。

前提知識の解説

Go言語のtimeパッケージとtime.Now()

Go言語のtimeパッケージは、時刻の取得、操作、フォーマットなど、時間に関する機能を提供します。time.Now()関数は、現在のローカル時刻をtime.Time型で返します。この関数は、システムクロックから現在の時刻を取得するため、内部的にはシステムコール(例: Linuxのgettimeofdayclock_gettime)を伴うことがあります。システムコールはユーザー空間からカーネル空間へのコンテキストスイッチを伴うため、頻繁に呼び出されると一定のオーバーヘッドが発生します。

キャッシュと有効期限(Expiration)

ソフトウェア開発において、キャッシュは頻繁にアクセスされるデータを一時的に保存し、その後のアクセスを高速化するための一般的な手法です。キャッシュされたデータは、いつまでも有効であるとは限りません。データの鮮度を保つため、キャッシュには通常、有効期限(expiration)が設定されます。有効期限が切れたデータは無効とみなされ、再度オリジナルのデータソースから取得し直す必要があります。

このコミットの文脈では、/etc/hostsファイルの内容がメモリ上にキャッシュされており、hosts.expireというフィールドがそのキャッシュの有効期限を示しています。cacheMaxAgeは、キャッシュが有効である最大期間を定義する定数または変数です。

Go言語のnetパッケージとホスト名解決

Go言語のnetパッケージは、ネットワークI/O機能を提供します。これには、TCP/IP、UDP、DNSルックアップ、HTTPクライアント/サーバーなどが含まれます。hosts.goファイルは、このnetパッケージの一部であり、特にホスト名解決のメカニズムに関連しています。

システムがホスト名をIPアドレスに解決する際、通常はまずローカルの/etc/hostsファイル(WindowsではC:\Windows\System32\drivers\etc\hosts)を参照します。このファイルにエントリが見つからない場合、DNSサーバーに問い合わせを行います。netパッケージは、このローカルホストファイルの読み込みとキャッシュを管理し、ホスト名解決の効率を高めています。

readHosts関数は、ホストファイルの内容を読み込み、パースし、メモリ上のデータ構造(hosts.byNamehosts.byAddrなど)に格納する役割を担っています。この関数が呼び出されるたびに、キャッシュが更新され、新しい有効期限が設定されます。

技術的詳細

このコミットの技術的なポイントは、time.Now()の呼び出し回数を減らすことによるマイクロ最適化です。

readHosts関数は、ホストファイルのキャッシュを更新する際に、現在の時刻を基準として新しい有効期限を計算します。元のコードでは、この計算のためにtime.Now()を呼び出していました。しかし、この関数が実行されるコンテキストでは、既に現在の時刻がnowという変数に格納されていることが想定されます(コミットの差分からは、now変数がどこで定義されているかは直接読み取れませんが、変更後のコードがnowを使用していることから、そのように推測できます)。

time.Now()は、システムクロックにアクセスするため、完全にゼロコストではありません。特に、高頻度で呼び出される関数内で何度も同じ時刻を取得する場合、そのオーバーヘッドが累積し、わずかながらパフォーマンスに影響を与える可能性があります。このコミットでは、既に取得済みのnow変数を再利用することで、不要なシステムコールや時刻取得処理を回避し、コードの実行効率を向上させています。

これは、Go言語の標準ライブラリのような、パフォーマンスが重視される基盤コードにおいて、細かな最適化がいかに重要であるかを示す良い例です。個々のtime.Now()の呼び出しコストは小さいですが、システム全体で頻繁に発生する処理においては、このような最適化が全体のレイテンシやスループットに寄与することがあります。

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

--- a/src/pkg/net/hosts.go
+++ b/src/pkg/net/hosts.go
@@ -51,7 +51,7 @@ func readHosts() {
 			}
 		}
 		// Update the data cache.
-		hosts.expire = time.Now().Add(cacheMaxAge)
+		hosts.expire = now.Add(cacheMaxAge)
 		hosts.path = hp
 		hosts.byName = hs
 		hosts.byAddr = is

コアとなるコードの解説

変更はsrc/pkg/net/hosts.goファイルのreadHosts関数内の一行に限定されています。

  • 変更前:

    hosts.expire = time.Now().Add(cacheMaxAge)
    

    この行では、hosts構造体のexpireフィールド(キャッシュの有効期限)を設定しています。time.Now()を呼び出して現在の時刻を取得し、それにcacheMaxAge(キャッシュの最大有効期間)を加算することで、新しい有効期限を計算していました。

  • 変更後:

    hosts.expire = now.Add(cacheMaxAge)
    

    変更後も同様にhosts.expireを設定していますが、現在の時刻を取得するためにtime.Now()を直接呼び出す代わりに、既にスコープ内で利用可能なnowという変数を使用しています。このnow変数は、readHosts関数が呼び出された際、またはその関数内の別の場所で一度だけ現在の時刻を取得して格納されたものと推測されます。

この変更により、hosts.expireを設定する際にtime.Now()が二重に呼び出されることがなくなり、パフォーマンスの向上が期待されます。これは、同じ関数スコープ内で同じ目的のために複数回時刻を取得する冗長性を排除する、クリーンな最適化です。

関連リンク

参考にした情報源リンク

  • コミット情報 (./commit_data/19522.txt)
  • Go言語のtimeパッケージに関する一般的な知識
  • Go言語のnetパッケージに関する一般的な知識
  • ソフトウェアにおけるキャッシュの概念I have generated the detailed explanation based on the provided instructions and the commit data. I have included all the required sections in the specified order, provided a detailed explanation in Japanese, and linked to the GitHub commit page and the Go CL. I have also explained the background, prerequisite knowledge, and technical details. The output is in Markdown format and is sent to standard output only, as requested.