Future Tech Blog恒例のGoリリース連載が始まります。本エントリーはインデックス記事&timeパッケージを散り上げます。
Date | Title | Author |
---|---|---|
7/16 | インデックス & time | 澁川 |
7/17 | archive/tar | 真野隼記 |
7/18 | range over funcとiterパッケージ | 棚井龍之介 |
7/19 | unique, slices, maps | 武田大輝 |
7/22 | text/template | 辻大志郎 |
7/23 | os.CopyFS & path/filepath | 市川燿 |
7/24 | keep-alive,Cookie | 大江聖太郎 |
7/25 | Go Telemetry | 棚井龍之介 |
1.23の更新内容の概要
Go 1.23のアップデートとしては以下のようなものがあります。多いので、Win/mac/LinuxのAMD/ARM関連以外は省略しています。リリースノートはこちらです。RC1の時点で書いているため、まだギリギリ変更があるかもしれません。
- 言語
- range-over-func(1.22連載で説明済み)がオプションなしでビルドできるようになりました
- ツール
- Goチームにツール群が壊れていないかの統計情報をフィードバックするためのgo telemetryという機能が入りました。デフォルトでは送信しない、オプトインとなっています
go env -changed
を実行するとデフォルトから変更されたものだけを表示するようになりましたgo mod tidy -diff
で、go mod tidyをドライランして実行時の影響を事前表示できるようになりましたgo.mod
とgo.work
で、互換性がない変更を元に戻せるgodebugディレクティブが追加。もともとGODEBUG環境変数で設定していたものをプロジェクトに記述しておけるように- go vetが、go.modのバージョン情報を見て、まだ導入されていないはずのシンボルがあった時に警告を出せるように・・・(だが試しても動かず)
- CGO_LDFLAGSが長すぎるとエラーが出る問題が解消
- クラッシュ時に生成される不完全なトレースデータもトレースできるように
- コンパイラが最適化されてプロファイルを使う最適化が2倍(100%)時間がかかっていたのが1桁%になり、効率もintel系CPUでループの最適化で1〜1.5%の効率改善
//go:linkname
ディレクティブを使い、//go:linkname
でマークされていない標準ライブラリの内部のシンボルが参照できるように
- ライブラリ
time
の更新- 後述
unique
パッケージの追加- 重複を排除してメモリ効率を上げるのに役立つパッケージ
- イテレータ関連の更新
- iterパッケージの追加、slices/mapsに新しい関数の追加
- C言語の構造体のメモリレイアウトを扱う
structs
パッケージの追加 archive/tar
- Gname/Uname取得をオーバーライド可能に
crypto/tls
- Client Helloの暗号化ドラフト仕様に対応
- QUICConnにセッション再開状態を報告するイベントが追加
- 安全ではない3DES暗号スイートがデフォルトのリストから削除(ただし戻せる手段は提供されている)
- ポスト量子キー交換メカニズムX25519Kyber768Draft00がデフォルトで有効に
crypto/x509
- CreateCertificateRequest, CreateRevocationListが、Go 1.16以降のCreateCertificateと同じく、署名者の公開鍵を使用して生成された署名を検証するように
- 安全ではないsha1署名を復活させるGODEBUGオプションのx509sha1=1が1.24で削除される方針に決定
- ParseOIDがドットエンコードされたASN.1オブジェクト識別子文字列を解析します
database/sql
- driver.Valuerが返したエラーがDB.Query, DB.Exec, DB.QueryRowでラップして返されるようになり、よりきめ細やかなエラーハンドリングが可能に
math/rand/v2
- Uint関数が実装された
- ChaCha8.Readメソッドが追加された
net
- KeepAlive周りが更新
- タイムアウトでDNSErrorが発生した場合は、context.DeadlineExceededをラップするため、DNSがタイムアウトしたかどうか判断できるように
net/http
- Cookie周りで諸々更新
- ファイルを返す系のハンドラでエラー時の挙動が変更
net/netip
- Addrが、==、Addr.Compare(), reflect.DeepEqual()の3つの比較で結果が同じになるようになった
os
- WindowsのSymlinkの扱いが改善(Stat系関数で)
- os.CopyFSでファイルシステムコピー
- WindowsでReadlink()の挙動が変化
path/filepath
- Localize()が追加
- WindowsでEvalSymlinks()の挙動が変化
reflect
- Overflow状態のTypeが追加
- NewAt()に近いがスライス専用のSliceAt()関数追加
- 値を反復するシーケンス作成だったり、イテレータが回るかどうかの判定メソッドなどが追加
runtime/debug
- SetCrashOutput()でクラッシュ時にレポートファイルを生成できるようにパスを指定する。
runtime/pprof
- alloc、mutex、block、threadcreate、goroutineのプロファイルの最大のスタックの深さが128フレームと4倍に
runtime/trace
- キャッチされていないパニックでプログラムがクラッシュした場合に、トレースデータが破損しないようにフラッシュするように
slices
- Repeat()が追加
sync
- 組み込み関数のclear()と同じようにマップを空にする、sync.Map.Clear()メソッドが追加
sync/atomic
- 入力された値のビット単位のAND/ORを適用し、古い値を返すAnd/Or演算子が追加
syscall
- WindowsでWSAENOPROTOOPTとGetsockoptInt()が追加
testing/fstest
- TestFSでエラー詳細を分析できるように、構造化エラーをアンラップできるように変更
text/template
- else withアクションがサポートされ、一部のユースケースでシンプルに書けるようになった
time
- タイムゾーンオフセットが範囲外の場合、Parse()とParseInLocation()がエラーを返すようになった
unicode/utf16
- RuneLen()関数の動作が変わり、UTF-16として不正な文字があった場合に-1を返すようになった
あと、このリリースノートにはないですが、strings.Compare()
が高速になったと書いている人がいました。
Since #Golang 1.23, please feel free to use the "https://t.co/5fqruOBH6L" function. It is not slow any more.
— zigo 101 - Zig + Go (@zigo_101) June 23, 2024
For a long time prior to Go 1.23, it was not recommended to be used.
(This is a change which is not mentioned in the current Go 1.23 release note draft.) https://t.co/o4Ihxurr2K
なお、僕が個人的に注目しているHTTP/3とQUIC対応ですが、1.22のときにはまだついていたinternalがはずれて、リリース予定の場所のgolang.org/x/net/quicができました。まだコミットはゆっくりです。まだ時間がかかりそうです。
https://github.com/golang/net/tree/master/quic
とはいえ、Goのアプリケーションサーバーが直接HTTP/3を喋れないといけないかというとそういうことはあまりないです。基本的にはロードバランサーやCDN、WAFを置いて、その後ろでアプリケーションを動かす構成がほとんどかと思います。その終端のサービスさえHTTP/3に対応していれば十分にメリットが享受できます。むしろ、それ以外はメリットが少ないというか、ネットワーク品質の高いクラウド内や構内のネットワークではパケロスはほとんど起きないでしょうし、そうなるとHTTP/3のメリットのちょっと荒れたネットワークでもパフォーマンスが落ちにくいというところは得られません。でもPure Goでサポートされるのはロマンがあるので、注目はし続けます。
timeパッケージ
timeパッケージでは以下の2つの修正がありました。
- Timer、TickerはStop()を呼ばなくてもGCの対象になるようになった
- Timer、Tickerのチャネルがバッファなしになった
Tickerは大量に作ることはないのであまり問題はないと思いますし、普通にリクエストを受けて処理をするようなコードだったら問題になることはほとんどないでしょうが、GoではTimerが作られたら、それが終了するまでGCされないという問題がありました。
問題になる例として出てくるのはループの中で大量のtime.NewTimer()
を使うと、GCするとメモリ消費が高止まりするみたいな事例が出てきます。対処法としては、同時に使わないのであれば1度作ったTimer
のReset()
を呼んで繰り返し使い回すようにする方法も出てきます。ただし、今までは1つバッファを持ったチャネルを内部で作っていたため、Reset()
を正しく行うのが難しいという問題がありました。公式ドキュメントにもありますが、タイマーを作ったがその値を消費していない消費者がいた場合にチャネルにゴミが残ってしまい、再利用前にそのチャネルにたまった値をドレインしてからリセットしなければなりません。公式ドキュメントにもサンプルと説明があります。
if !t.Stop() { |
調べようとしたらすでに詳しく書いてあるブログがありました。Go 1.22と1.23での効率の良い書き方の比較のコードを引用します。
// resetTimer はタイマーを停止し、ドレインを行なってからリセットする |
func consumer(ctx context.Context, in <-chan token) { |
チャネルがバッファなしになったことで、このドレイン処理が不要となり、いきなりReset()
が呼べるようになります。便利ですね。このブログではReset()
を呼ばずにtimer.After()
を使うのもクリーンでいいよと書かれています(ただし、タイマーインスタンスは大量に作るのでGCはされるものの、メモリは使う)。
「そんな大量のNewTimer()
を呼ぶようなコード書いてないし、Timer
のReset()
なんか呼ぶような場面ないよ」という方もいると思います。僕もそうでした。もしやと思ってcontext
パッケージのコードを読むと、context.WithDeadlineCause()
(context.WithTimeout()
とかもみんな最終的にこれを呼ぶ)の中で、time.AfterFunc()
を呼んでおり、time.AfterFunc()
はtime.NewTimer()
と同じような内部実装になっていました。
ようするに、データベースや外部API呼び出しなど、I/Oが発生するようなコードで毎回丁寧にcontext.WithTimeout()
を使ってきちんとタイムアウトでエラーになるような丁寧な暮らしをしているGo開発者は全員ひっかかっていた、ということになります。なお、タイマーが作成される箇所はcontext
の内部で、直接触れないため、Reset()
による回避もできなかったと。それが今回の修正でGCされるようになったということで、丁寧な人には恩恵があるアップデートのようです。
明日は真野さんのでarchive/tarです。