[インデックス 17948] ファイルの概要
このコミットは、Go言語の標準ライブラリである database/sql
パッケージ内の putConnDBLocked
メソッドにおける冗長な条件式を削除し、パフォーマンスを改善するものです。具体的には、データベース接続プールのアイドル接続数を管理するロジックにおいて、既に別の条件で暗黙的に満たされているチェックを削除することで、不要なメソッド呼び出しとそれに伴うオーバーヘッドを削減しています。
コミット
commit c8869e9caf17c0b993b515924b163a6c8286be77
Author: Alberto García Hierro <alberto@garciahierro.com>
Date: Tue Dec 10 16:10:09 2013 +0400
database/sql: Remove redundant condition in if
The final condition (db.maxIdleConnsLocked() > db.freeConn.Len()) can
only be true iff db.maxIdleConnsLocked() is greater than 0, so previously
checking if it's greater than 0 is a waste, specially when that involves
a method call which (ATM) can't be inlined and includes a switch.
Dissasembly follows (test for err == nil has been omitted for clarity):
Before:
43c357: cmp $0x0,%bl
43c35a: jne 43c3ce <database/sql.(*DB).putConnDBLocked+0x1ce>
43c35c: mov %rax,(%rsp)
43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
43c365: mov 0x8(%rsp),%rbx
43c36a: cmp $0x0,%rbx
43c36e: jle 43c3ce <database/sql.(*DB).putConnDBLocked+0x1ce>
43c370: mov 0x30(%rsp),%rbx
43c375: mov %rbx,(%rsp)
43c379: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
43c37e: mov 0x30(%rsp),%rdx
43c383: mov 0x8(%rsp),%rcx
43c388: mov 0x28(%rdx),%rbp
43c38c: mov 0x28(%rbp),%rbx
43c390: cmp %rcx,%rbx
43c393: jge 43c3ce <database/sql.(*DB).putConnDBLocked+0x1ce>
43c395: mov 0x28(%rdx),%rbp
43c399: mov %rbp,(%rsp)
43c39d: mov 0x38(%rsp),%rcx
43c3a2: mov $0x556c60,%eax
43c3a7: mov %rax,0x8(%rsp)\n 43c3ac: mov %rcx,0x10(%rsp)\n 43c3b1: callq 4db5b0 <container/list.(*List).PushFront>\n \n After:\n 43c357: cmp $0x0,%bl\n 43c35a: jne 43c3b5 <database/sql.(*DB).putConnDBLocked+0x1b5>\n 43c35c: mov %rax,(%rsp)\n 43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>\n 43c365: mov 0x30(%rsp),%rdx\n 43c36a: mov 0x8(%rsp),%rcx\n 43c36f: mov 0x28(%rdx),%rbp\n 43c373: mov 0x28(%rbp),%rbx\n 43c377: cmp %rcx,%rbx\n 43c37a: jge 43c3b5 <database/sql.(*DB).putConnDBLocked+0x1b5>\n 43c37c: mov 0x28(%rdx),%rbp\n 43c380: mov %rbp,(%rsp)\n 43c384: mov 0x38(%rsp),%rcx\n 43c389: mov $0x556c60,%eax\n 43c38e: mov %rax,0x8(%rsp)\n 43c393: mov %rcx,0x10(%rsp)\n 43c398: callq 4db590 <container/list.(*List).PushFront>\n \n R=golang-dev, bradfitz, iant\n CC=golang-dev\n https://golang.org/cl/14656044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c8869e9caf17c0b993b515924b163a6c8286be77
元コミット内容
このコミットは、Go言語の database/sql
パッケージ内の putConnDBLocked
関数における条件式を簡素化するものです。変更前のコードでは、アイドル状態のデータベース接続をプールに戻す際に、以下の条件をチェックしていました。
else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() {
この条件式には db.maxIdleConnsLocked() > 0
という部分が含まれており、これは db.maxIdleConnsLocked() > db.freeConn.Len()
という後続の条件が真であるためには、db.maxIdleConnsLocked()
が必然的に0より大きい値でなければならないため、冗長でした。
変更の背景
この変更の背景には、Go言語のランタイムとコンパイラの最適化に関する考慮があります。コミットメッセージに明記されているように、db.maxIdleConnsLocked()
メソッドの呼び出しは、当時(2013年)のGoコンパイラではインライン化されず、さらに内部で switch
ステートメントを含んでいたため、比較的高価な操作でした。
冗長な条件 db.maxIdleConnsLocked() > 0
を削除することで、この高価なメソッド呼び出しを1回減らすことができます。これにより、putConnDBLocked
関数が実行される際のCPUサイクルを節約し、データベース接続の解放およびプールへの返却処理の効率を向上させることが目的です。特に、高負荷なシステムで頻繁にデータベース接続が取得・解放される場合、このような小さな最適化が全体のスループットに寄与する可能性があります。
コミットメッセージに示されているアセンブリコードの比較は、この最適化が実際に生成される機械語レベルで命令数を削減していることを明確に示しています。
前提知識の解説
Go言語の database/sql
パッケージ
database/sql
パッケージは、Go言語でSQLデータベースを操作するための汎用的なインターフェースを提供します。このパッケージ自体は特定のデータベースドライバーを含まず、データベース固有の機能は外部のドライバーパッケージによって提供されます。主な機能は以下の通りです。
sql.DB
: データベースへの接続プールを管理する構造体です。複数のゴルーチンから安全に利用できるように設計されており、接続のオープン、クローズ、アイドル接続の管理などを自動的に行います。- 接続プール: データベースへの接続はコストの高い操作であるため、
database/sql
は接続プールを内部に持ち、一度確立した接続を再利用します。これにより、接続の確立・切断のオーバーヘッドを削減し、アプリケーションのパフォーマンスを向上させます。 maxIdleConns
: アイドル状態(使用されていない)でプールに保持される接続の最大数を設定します。この設定は、接続の再利用効率に直接影響します。freeConn
:sql.DB
構造体内部で、現在アイドル状態にある接続を保持するためのリスト(または同様のデータ構造)です。
Goコンパイラの最適化とインライン化
Goコンパイラは、生成されるバイナリのパフォーマンスを向上させるために様々な最適化を行います。その一つが「インライン化(inlining)」です。
- インライン化: 関数呼び出しのオーバーヘッド(スタックフレームの作成、引数の渡し、戻り値の処理など)を避けるために、呼び出し元のコードに関数本体のコードを直接埋め込む最適化手法です。これにより、実行時のパフォーマンスが向上しますが、バイナリサイズが増加する可能性があります。
- インライン化の制約: Goコンパイラは、関数のサイズ、複雑さ、再帰の有無など、様々な要因に基づいてインライン化を行うかどうかを決定します。特に、
switch
ステートメントのような制御フローが複雑な関数は、インライン化されにくい傾向があります。
アセンブリコードの分析
アセンブリコードは、CPUが直接実行する機械語命令を人間が読める形式で表現したものです。このコミットメッセージに含まれるアセンブリコードは、Goプログラムがコンパイルされた後の低レベルな動作を示しています。
cmp
: 比較命令。2つのオペランドを比較し、フラグレジスタを設定します。jne
/jle
/jge
: 条件分岐命令。フラグレジスタの状態に基づいて、指定されたアドレスにジャンプします。mov
: データ転送命令。レジスタやメモリ間でデータを移動します。callq
: 関数呼び出し命令。指定されたアドレスの関数を実行し、戻りアドレスをスタックにプッシュします。
アセンブリコードを比較することで、Goのソースコードの変更が、最終的な実行ファイルにどのような影響を与え、命令数がどのように変化したかを正確に把握することができます。
技術的詳細
このコミットの技術的詳細なポイントは、論理的な冗長性の排除と、それがもたらすパフォーマンス上のメリットにあります。
変更前の条件式は以下の通りでした。
err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len()
ここで注目すべきは、db.maxIdleConnsLocked() > 0
と db.maxIdleConnsLocked() > db.freeConn.Len()
の2つの条件です。
もし db.maxIdleConnsLocked() > db.freeConn.Len()
が真であるならば、db.maxIdleConnsLocked()
は db.freeConn.Len()
よりも大きい値である必要があります。db.freeConn.Len()
は接続プールの現在のアイドル接続数であり、これは非負の値(0以上)です。したがって、db.maxIdleConnsLocked()
が db.freeConn.Len()
よりも大きいということは、db.maxIdleConnsLocked()
が必然的に0よりも大きいことを意味します。
つまり、db.maxIdleConnsLocked() > db.freeConn.Len()
が真である限り、db.maxIdleConnsLocked() > 0
も常に真となります。このため、db.maxIdleConnsLocked() > 0
の条件は冗長であり、評価する必要がありません。
この冗長な条件を削除することで、db.maxIdleConnsLocked()
メソッドの呼び出しが1回削減されます。コミットメッセージによると、このメソッドは当時インライン化されず、内部に switch
ステートメントを含んでいたため、呼び出しコストが無視できないものでした。インライン化されない関数呼び出しは、スタックフレームのセットアップ、引数のプッシュ、レジスタの保存・復元など、一定のオーバーヘッドを伴います。
コミットメッセージに示されたアセンブリコードの比較は、この最適化の効果を具体的に示しています。
Before:
43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
43c379: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
変更前は maxIdleConnsLocked
が2回呼び出されています。
After:
43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
変更後は maxIdleConnsLocked
が1回しか呼び出されていません。
このアセンブリコードの比較から、冗長な条件の削除によって、実際に maxIdleConnsLocked
メソッドの呼び出しが1回減少し、それに伴う命令数も削減されていることが確認できます。これにより、putConnDBLocked
関数が実行される際のCPUサイクルが節約され、全体的なパフォーマンスが向上します。これは、特にデータベース接続のライフサイクルが頻繁に発生するような高負荷なアプリケーションにおいて、顕著な効果をもたらす可能性があります。
コアとなるコードの変更箇所
変更は src/pkg/database/sql/sql.go
ファイルの1箇所のみです。
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -791,7 +791,7 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
req <- dc
return true
- } else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() {
+ } else if err == nil && !db.closed && db.maxIdleConnsLocked() > db.freeConn.Len() {
dc.listElem = db.freeConn.PushFront(dc)
return true
}
具体的には、putConnDBLocked
関数の else if
条件式から db.maxIdleConnsLocked() > 0
という部分が削除されました。
コアとなるコードの解説
putConnDBLocked
関数は、データベース接続(dc *driverConn
)をプールに戻す際に呼び出される内部関数です。この関数は、接続が再利用可能かどうか、またアイドル接続の最大数を超えていないかなどを判断します。
変更前のコードでは、接続をアイドルプールに戻す条件として、以下の4つを論理積(AND)で結合していました。
err == nil
: 接続がエラーなく正常にクローズされたか、または使用後にエラーが発生しなかったか。!db.closed
: データベース接続プール自体が閉じられていないか。db.maxIdleConnsLocked() > 0
: アイドル接続の最大数が0より大きいか(つまり、アイドル接続を許可しているか)。db.maxIdleConnsLocked() > db.freeConn.Len()
: 現在のアイドル接続数が、設定された最大アイドル接続数よりも少ないか。
このコミットでは、3番目の条件 db.maxIdleConnsLocked() > 0
が削除されました。
その理由は、4番目の条件 db.maxIdleConnsLocked() > db.freeConn.Len()
が真である場合、db.maxIdleConnsLocked()
は必ず db.freeConn.Len()
よりも大きい値であるため、自動的に db.maxIdleConnsLocked()
が0より大きいことが保証されるからです。db.freeConn.Len()
はアイドル接続の数を表し、常に0以上の値を取ります。したがって、db.maxIdleConnsLocked()
が db.freeConn.Len()
より大きいということは、db.maxIdleConnsLocked()
が負の値であることはありえず、必然的に正の値であると判断できます。
この変更により、db.maxIdleConnsLocked()
メソッドの呼び出しが1回削減され、特にこのメソッドがインライン化されず、内部に switch
ステートメントを持つためにコストが高いという当時の状況において、パフォーマンスの向上が期待されました。コードの論理的な簡潔さも向上しています。
関連リンク
- Go言語
database/sql
パッケージのドキュメント: https://pkg.go.dev/database/sql - Go言語の接続プールに関する議論(一般的な情報): https://go.dev/doc/database/sql-connections
参考にした情報源リンク
- Go CL 14656044: https://golang.org/cl/14656044
- Go言語のインライン化に関する一般的な情報(Goのバージョンによって挙動は異なる可能性があります): https://go.dev/blog/go1.10 (Go 1.10のブログ記事ですが、インライン化の概念について触れられています)
- アセンブリ言語の基礎知識(一般的な情報源)
- Go言語の
database/sql
パッケージのソースコード (コミット当時のバージョン) - Go言語のコンパイラ最適化に関する一般的な情報# [インデックス 17948] ファイルの概要
このコミットは、Go言語の標準ライブラリである database/sql
パッケージ内の putConnDBLocked
メソッドにおける冗長な条件式を削除し、パフォーマンスを改善するものです。具体的には、データベース接続プールのアイドル接続数を管理するロジックにおいて、既に別の条件で暗黙的に満たされているチェックを削除することで、不要なメソッド呼び出しとそれに伴うオーバーヘッドを削減しています。
コミット
commit c8869e9caf17c0b993b515924b163a6c8286be77
Author: Alberto García Hierro <alberto@garciahierro.com>
Date: Tue Dec 10 16:10:09 2013 +0400
database/sql: Remove redundant condition in if
The final condition (db.maxIdleConnsLocked() > db.freeConn.Len()) can
only be true iff db.maxIdleConnsLocked() is greater than 0, so previously
checking if it's greater than 0 is a waste, specially when that involves
a method call which (ATM) can't be inlined and includes a switch.
Dissasembly follows (test for err == nil has been omitted for clarity):
Before:
43c357: cmp $0x0,%bl
43c35a: jne 43c3ce <database/sql.(*DB).putConnDBLocked+0x1ce>
43c35c: mov %rax,(%rsp)\n 43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>\n 43c365: mov 0x8(%rsp),%rbx\n 43c36a: cmp $0x0,%rbx\n 43c36e: jle 43c3ce <database/sql.(*DB).putConnDBLocked+0x1ce>\n 43c370: mov 0x30(%rsp),%rbx\n 43c375: mov %rbx,(%rsp)\n 43c379: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>\n 43c37e: mov 0x30(%rsp),%rdx\n 43c383: mov 0x8(%rsp),%rcx\n 43c388: mov 0x28(%rdx),%rbp\n 43c38c: mov 0x28(%rbp),%rbx\n 43c390: cmp %rcx,%rbx\n 43c393: jge 43c3ce <database/sql.(*DB).putConnDBLocked+0x1ce>\n 43c395: mov 0x28(%rdx),%rbp\n 43c399: mov %rbp,(%rsp)\n 43c39d: mov 0x38(%rsp),%rcx\n 43c3a2: mov $0x556c60,%eax\n 43c3a7: mov %rax,0x8(%rsp)\n 43c3ac: mov %rcx,0x10(%rsp)\n 43c3b1: callq 4db5b0 <container/list.(*List).PushFront>\n \n After:\n 43c357: cmp $0x0,%bl\n 43c35a: jne 43c3b5 <database/sql.(*DB).putConnDBLocked+0x1b5>\n 43c35c: mov %rax,(%rsp)\n 43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>\n 43c365: mov 0x30(%rsp),%rdx\n 43c36a: mov 0x8(%rsp),%rcx\n 43c36f: mov 0x28(%rdx),%rbp\n 43c373: mov 0x28(%rbp),%rbx\n 43c377: cmp %rcx,%rbx\n 43c37a: jge 43c3b5 <database/sql.(*DB).putConnDBLocked+0x1b5>\n 43c37c: mov 0x28(%rdx),%rbp\n 43c380: mov %rbp,(%rsp)\n 43c384: mov 0x38(%rsp),%rcx\n 43c389: mov $0x556c60,%eax\n 43c38e: mov %rax,0x8(%rsp)\n 43c393: mov %rcx,0x10(%rsp)\n 43c398: callq 4db590 <container/list.(*List).PushFront>\n \n R=golang-dev, bradfitz, iant\n CC=golang-dev\n https://golang.org/cl/14656044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c8869e9caf17c0b993b515924b163a6c8286be77
元コミット内容
このコミットは、Go言語の database/sql
パッケージ内の putConnDBLocked
関数における条件式を簡素化するものです。変更前のコードでは、アイドル状態のデータベース接続をプールに戻す際に、以下の条件をチェックしていました。
else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() {
この条件式には db.maxIdleConnsLocked() > 0
という部分が含まれており、これは db.maxIdleConnsLocked() > db.freeConn.Len()
という後続の条件が真であるためには、db.maxIdleConnsLocked()
が必然的に0より大きい値でなければならないため、冗長でした。
変更の背景
この変更の背景には、Go言語のランタイムとコンパイラの最適化に関する考慮があります。コミットメッセージに明記されているように、db.maxIdleConnsLocked()
メソッドの呼び出しは、当時(2013年)のGoコンパイラではインライン化されず、さらに内部で switch
ステートメントを含んでいたため、比較的高価な操作でした。
冗長な条件 db.maxIdleConnsLocked() > 0
を削除することで、この高価なメソッド呼び出しを1回減らすことができます。これにより、putConnDBLocked
関数が実行される際のCPUサイクルを節約し、データベース接続の解放およびプールへの返却処理の効率を向上させることが目的です。特に、高負荷なシステムで頻繁にデータベース接続が取得・解放される場合、このような小さな最適化が全体のスループットに寄与する可能性があります。
コミットメッセージに示されているアセンブリコードの比較は、この最適化が実際に生成される機械語レベルで命令数を削減していることを明確に示しています。
前提知識の解説
Go言語の database/sql
パッケージ
database/sql
パッケージは、Go言語でSQLデータベースを操作するための汎用的なインターフェースを提供します。このパッケージ自体は特定のデータベースドライバーを含まず、データベース固有の機能は外部のドライバーパッケージによって提供されます。主な機能は以下の通りです。
sql.DB
: データベースへの接続プールを管理する構造体です。複数のゴルーチンから安全に利用できるように設計されており、接続のオープン、クローズ、アイドル接続の管理などを自動的に行います。- 接続プール: データベースへの接続はコストの高い操作であるため、
database/sql
は接続プールを内部に持ち、一度確立した接続を再利用します。これにより、接続の確立・切断のオーバーヘッドを削減し、接続の確立・切断のオーバーヘッドを削減し、アプリケーションのパフォーマンスを向上させます。 maxIdleConns
: アイドル状態(使用されていない)でプールに保持される接続の最大数を設定します。この設定は、接続の再利用効率に直接影響します。freeConn
:sql.DB
構造体内部で、現在アイドル状態にある接続を保持するためのリスト(または同様のデータ構造)です。
Goコンパイラの最適化とインライン化
Goコンパイラは、生成されるバイナリのパフォーマンスを向上させるために様々な最適化を行います。その一つが「インライン化(inlining)」です。
- インライン化: 関数呼び出しのオーバーヘッド(スタックフレームの作成、引数の渡し、戻り値の処理など)を避けるために、呼び出し元のコードに関数本体のコードを直接埋め込む最適化手法です。これにより、実行時のパフォーマンスが向上しますが、バイナリサイズが増加する可能性があります。
- インライン化の制約: Goコンパイラは、関数のサイズ、複雑さ、再帰の有無など、様々な要因に基づいてインライン化を行うかどうかを決定します。特に、
switch
ステートメントのような制御フローが複雑な関数は、インライン化されにくい傾向があります。
アセンブリコードの分析
アセンブリコードは、CPUが直接実行する機械語命令を人間が読める形式で表現したものです。このコミットメッセージに含まれるアセンブリコードは、Goプログラムがコンパイルされた後の低レベルな動作を示しています。
cmp
: 比較命令。2つのオペランドを比較し、フラグレジスタを設定します。jne
/jle
/jge
: 条件分岐命令。フラグレジスタの状態に基づいて、指定されたアドレスにジャンプします。mov
: データ転送命令。レジスタやメモリ間でデータを移動します。callq
: 関数呼び出し命令。指定されたアドレスの関数を実行し、戻りアドレスをスタックにプッシュします。
アセンブリコードを比較することで、Goのソースコードの変更が、最終的な実行ファイルにどのような影響を与え、命令数がどのように変化したかを正確に把握することができます。
技術的詳細
このコミットの技術的詳細なポイントは、論理的な冗長性の排除と、それがもたらすパフォーマンス上のメリットにあります。
変更前の条件式は以下の通りでした。
err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len()
ここで注目すべきは、db.maxIdleConnsLocked() > 0
と db.maxIdleConnsLocked() > db.freeConn.Len()
の2つの条件です。
もし db.maxIdleConnsLocked() > db.freeConn.Len()
が真であるならば、db.maxIdleConnsLocked()
は db.freeConn.Len()
よりも大きい値である必要があります。db.freeConn.Len()
は接続プールの現在のアイドル接続数であり、これは非負の値(0以上)です。したがって、db.maxIdleConnsLocked()
が db.freeConn.Len()
よりも大きいということは、db.maxIdleConnsLocked()
が必然的に0よりも大きいことを意味します。
つまり、db.maxIdleConnsLocked() > db.freeConn.Len()
が真である限り、db.maxIdleConnsLocked() > 0
も常に真となります。このため、db.maxIdleConnsLocked() > 0
の条件は冗長であり、評価する必要がありません。
この冗長な条件を削除することで、db.maxIdleConnsLocked()
メソッドの呼び出しが1回削減されます。コミットメッセージによると、このメソッドは当時インライン化されず、内部に switch
ステートメントを含んでいたため、呼び出しコストが無視できないものでした。インライン化されない関数呼び出しは、スタックフレームのセットアップ、引数のプッシュ、レジスタの保存・復元など、一定のオーバーヘッドを伴います。
コミットメッセージに示されたアセンブリコードの比較は、この最適化の効果を具体的に示しています。
Before:
43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
43c379: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
変更前は maxIdleConnsLocked
が2回呼び出されています。
After:
43c360: callq 43aec0 <database/sql.(*DB).maxIdleConnsLocked>
変更後は maxIdleConnsLocked
が1回しか呼び出されていません。
このアセンブリコードの比較から、冗長な条件の削除によって、実際に maxIdleConnsLocked
メソッドの呼び出しが1回減少し、それに伴う命令数も削減されていることが確認できます。これにより、putConnDBLocked
関数が実行される際のCPUサイクルが節約され、全体的なパフォーマンスが向上します。これは、特にデータベース接続のライフサイクルが頻繁に発生するような高負荷なアプリケーションにおいて、顕著な効果をもたらす可能性があります。
コアとなるコードの変更箇所
変更は src/pkg/database/sql/sql.go
ファイルの1箇所のみです。
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -791,791,7 +791,7 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
req <- dc
return true
- } else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() {
+ } else if err == nil && !db.closed && db.maxIdleConnsLocked() > db.freeConn.Len() {
dc.listElem = db.freeConn.PushFront(dc)
return true
}
具体的には、putConnDBLocked
関数の else if
条件式から db.maxIdleConnsLocked() > 0
という部分が削除されました。
コアとなるコードの解説
putConnDBLocked
関数は、データベース接続(dc *driverConn
)をプールに戻す際に呼び出される内部関数です。この関数は、接続が再利用可能かどうか、またアイドル接続の最大数を超えていないかなどを判断します。
変更前のコードでは、接続をアイドルプールに戻す条件として、以下の4つを論理積(AND)で結合していました。
err == nil
: 接続がエラーなく正常にクローズされたか、または使用後にエラーが発生しなかったか。!db.closed
: データベース接続プール自体が閉じられていないか。db.maxIdleConnsLocked() > 0
: アイドル接続の最大数が0より大きいか(つまり、アイドル接続を許可しているか)。db.maxIdleConnsLocked() > db.freeConn.Len()
: 現在のアイドル接続数が、設定された最大アイドル接続数よりも少ないか。
このコミットでは、3番目の条件 db.maxIdleConnsLocked() > 0
が削除されました。
その理由は、4番目の条件 db.maxIdleConnsLocked() > db.freeConn.Len()
が真である場合、db.maxIdleConnsLocked()
は必ず db.freeConn.Len()
よりも大きい値であるため、自動的に db.maxIdleConnsLocked()
が0より大きいことが保証されるからです。db.freeConn.Len()
はアイドル接続の数を表し、常に0以上の値を取ります。したがって、db.maxIdleConnsLocked()
が db.freeConn.Len()
より大きいということは、db.maxIdleConnsLocked()
が負の値であることはありえず、必然的に正の値であると判断できます。
この変更により、db.maxIdleConnsLocked()
メソッドの呼び出しが1回削減され、特にこのメソッドがインライン化されず、内部に switch
ステートメントを持つためにコストが高いという当時の状況において、パフォーマンスの向上が期待されました。コードの論理的な簡潔さも向上しています。
関連リンク
- Go言語
database/sql
パッケージのドキュメント: https://pkg.go.dev/database/sql - Go言語の接続プールに関する議論(一般的な情報): https://go.dev/doc/database/sql-connections
参考にした情報源リンク
- Go CL 14656044: https://golang.org/cl/14656044
- Go言語のインライン化に関する一般的な情報(Goのバージョンによって挙動は異なる可能性があります): https://go.dev/blog/go1.10 (Go 1.10のブログ記事ですが、インライン化の概念について触れられています)
- アセンブリ言語の基礎知識(一般的な情報源)
- Go言語の
database/sql
パッケージのソースコード (コミット当時のバージョン) - Go言語のコンパイラ最適化に関する一般的な情報