Skip to content

Commit

Permalink
Merge pull request #1331 from future-architect/feature
Browse files Browse the repository at this point in the history
Go 1.23
  • Loading branch information
ma91n authored Jul 16, 2024
2 parents 5dd3293 + d140aec commit e5681d8
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
---
title: "Go 1.23リリース連載始まります&timeパッケージ"
date: 2024/07/16 00:00:00
postid: a
tag:
- Go
- Go1.23
- インデックス
category:
- Programming
thumbnail: /images/20240716a/thumbnail.png
author: 澁川喜規
lede: "Future Tech Blog恒例のGoリリース連載が始まります。本エントリーはインデックス記事&timeパッケージを散り上げます。"
---

<img src="/images/20240716a/top.png" alt="" width="800" height="511">

Future Tech Blog恒例のGoリリース連載が始まります。本エントリーはインデックス記事&timeパッケージを散り上げます。

| Date | Title | Author |
|:-:|:-:|:-:|
| 7/16 | インデックス & time | 澁川 |
| 7/17 | archive/tar | 真野隼記 |
| 7/18 | iter | 棚井龍之介 |
| 7/19 | unique, slices, maps | 武田大輝 |
| 7/22 | text/template | 辻大志郎 |
| 7/23 | path/filepath | 市川燿 |
| 7/24 | net, net/http | 大江聖太郎 |

# 1.23の更新内容の概要

Go 1.23のアップデートとしては以下のようなものがあります。多いので、Win/mac/LinuxのAMD/ARM関連以外は省略しています。リリースノートは[こちら](https://tip.golang.org/doc/go1.23
)です。RC1の時点で書いているため、まだギリギリ変更があるかもしれません。

* 言語
* [range-over-func(1.22連載で説明済み)](https://future-architect.github.io/articles/20240129a/)がオプションなしでビルドできるようになりました
* ツール
* Goチームにツール群が壊れていないかの統計情報をフィードバックするためのgo telemetryという機能が入りました。デフォルトでは送信しない、オプトインとなっています
* ``go env -changed`` を実行するとデフォルトから変更されたものだけを表示するようになりました
* ``go mod tidy -diff``で、go mod tidyをドライランして実行時の影響を事前表示できるようになりました
* ``go.mod````go.work``で、互換性がない変更を元に戻せるgodebugディレクティブが追加。もともと[GODEBUG環境変数](https://tip.golang.org/doc/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
* クッキー周りで諸々更新
* ファイルを返す系のハンドラでエラー時の挙動が変更
* 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()`が高速になったと書いている人がいました。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Since <a href="https://twitter.com/hashtag/Golang?src=hash&amp;ref_src=twsrc%5Etfw">#Golang</a> 1.23, please feel free to use the &quot;<a href="https://t.co/5fqruOBH6L">https://t.co/5fqruOBH6L</a>&quot; function. It is not slow any more.<br><br>For a long time prior to Go 1.23, it was not recommended to be used.<br><br>(This is a change which is not mentioned in the current Go 1.23 release note draft.) <a href="https://t.co/o4Ihxurr2K">https://t.co/o4Ihxurr2K</a></p>&mdash; zigo 101 - Zig + Go (@zigo_101) <a href="https://twitter.com/zigo_101/status/1804923767963148397?ref_src=twsrc%5Etfw">June 23, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

なお、僕が個人的に注目している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されないという問題がありました。

* [Golang <-time.After() is not garbage collected before expiry](https://medium.com/@oboturov/golang-time-after-is-not-garbage-collected-4cbc94740082)

問題になる例として出てくるのはループの中で大量の`time.NewTimer()`を使うと、GCするとメモリ消費が高止まりするみたいな事例が出てきます。対処法としては、同時に使わないのであれば1度作った`Timer``Reset()`を呼んで繰り返し使い回すようにする方法も出てきます。ただし、今までは1つバッファを持ったチャネルを内部で作っていたため、`Reset()`を正しく行うのが難しいという問題がありました。公式ドキュメントにもありますが、タイマーを作ったがその値を消費していない消費者がいた場合にチャネルにゴミが残ってしまい、再利用前にそのチャネルにたまった値をドレインしてからリセットしなければなりません。[公式ドキュメント](https://pkg.go.dev/[email protected]#Timer.Reset)にもサンプルと説明があります。

```go
if !t.Stop() {
<-t.C // ドレイン
}
t.Reset(d)
```

[調べようとしたらすでに詳しく書いてあるブログ](https://antonz.org/timer-reset/)がありました。Go 1.22と1.23での効率の良い書き方の比較のコードを引用します。

```go 1.22以前
// resetTimer はタイマーを停止し、ドレインを行なってからリセットする
func resetTimer(t *time.Timer, d time.Duration) {
if !t.Stop() {
select {
case <-t.C:
default:
}
}
t.Reset(d)
}

func consumer(ctx context.Context, in <-chan token) {
const timeout = time.Hour
timer := time.NewTimer(timeout)
for {
resetTimer(timer, timeout)
select {
case <-in:
// do stuff
case <-timer.C:
// log warning
case <-ctx.Done():
return
}
}
}
```

```go 1.23以降
func consumer(ctx context.Context, in <-chan token) {
const timeout = time.Hour
timer := time.NewTimer(timeout)
for {
timer.Reset(timeout)
select {
case <-in:
// do stuff
case <-timer.C:
// log warning
case <-ctx.Done():
return
}
}
}
```

チャネルがバッファなしになったことで、このドレイン処理が不要となり、いきなり`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です。


Binary file added source/images/20240716a/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Binary file added source/images/20240716a/top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e5681d8

Please sign in to comment.