Go の高速 strftime
f , err := strftime . New ( `.... pattern ...` )
if err := f . Format ( buf , time . Now ()); err != nil {
log . Println ( err . Error ())
}
このライブラリの目標は次のとおりです
パターンと時間を取得してフォーマットします。この関数は、関数が呼び出されるたびにパターンを再コンパイルするユーティリティ関数です。同じパターンを複数回フォーマットすることが事前にわかっている場合は、 New
使用してStrftime
オブジェクトを作成し、それを再利用することを検討してください。
パターンを取得して、新しいStrftime
オブジェクトを作成します。
このStrftime
オブジェクトの作成に使用されるパターン文字列を返します。
プリコンパイルされたパターンに従って時刻をフォーマットし、結果を指定されたio.Writer
に書き込みます。
プリコンパイルされたパターンに従って時刻をフォーマットし、結果の文字列を返します。
パターン | 説明 |
---|---|
%A | 完全な曜日名の国別表現 |
%a | 短縮された平日の国別表現 |
%B | 完全な月名の各国表現 |
%b | 月の略称の国別表記 |
%C | (年 / 100) を 10 進数で表します。 1 桁の前にゼロが付きます |
%c | 時刻と日付の国別表現 |
%D | %m/%d/%y に相当 |
%d | 10 進数で表した日付 (01 ~ 31) |
%e | 10 進数 (1 ~ 31) で表した月の日。 1 桁の前には空白が付きます |
%F | %Y-%m-%d に相当 |
%H | 時 (24 時間時計) を 10 進数 (00 ~ 23) で表します。 |
%h | %bと同じ |
%私 | 時 (12 時間制) を 10 進数 (01 ~ 12) で表します。 |
%j | 10 進数で表した日付 (001 ~ 366) |
%k | 時 (24 時間時計) を 10 進数 (0 ~ 23) で表します。 1 桁の前には空白が付きます |
%l | 時 (12 時間時計) を 10 進数 (1 ~ 12) で表します。 1 桁の前には空白が付きます |
%M | 分を 10 進数で表します (00 ~ 59) |
%m | 10 進数で表した月 (01 ~ 12) |
%n | 改行 |
%p | 必要に応じて、「メリディエム前」(午前)または「メリディエム後」(午後)のいずれかの国別表現。 |
%R | %H:%M に相当 |
%r | %I:%M:%S %p と同等 |
%S | 秒は 10 進数 (00 ~ 60) |
%T | %H:%M:%S と同等 |
%t | タブ |
%U | 年の週番号 (日曜日が週の最初の日) を 10 進数で表します (00 ~ 53)。 |
%u | 平日 (週の最初の月曜日) を 10 進数 (1 ~ 7) で表します。 |
%V | その年の週番号 (月曜日が週の最初の日) を 10 進数で表します (01 ~ 53)。 |
%v | %e-%b-%Y と同等 |
%W | 年の週番号 (月曜日が週の最初の日) を 10 進数で表します (00 ~ 53)。 |
%w | 10 進数 (0 ~ 6) で表した平日 (週の最初の日は日曜日) |
%X | 当時の国民代表 |
%x | 日付の国別表現 |
%Y | 世紀を 10 進数で表した年 |
%y | 10 進数で表した世紀のない年 (00 ~ 99) |
%Z | タイムゾーン名 |
%z | UTCからのタイムゾーンオフセット |
%% | 「%」 |
このライブラリは通常、POSIX に準拠しようとしますが、比較的広く使用されているものの POSIX 仕様には含まれていない追加の仕様が 1 つまたは 2 つ必要になる場合があります。
たとえば、POSIX ではミリ秒を出力する方法が指定されていませんが、一般的な実装では%f
または%L
使用してこれを実現できます。
これらのインスタンスの場合、カスタム仕様セットを使用するようにstrftime.Strftime
を構成できます。
ss := strftime.NewSpecificationSet()
ss.Set('L', ...) // provide implementation for `%L`
// pass this new specification set to the strftime instance
p, err := strftime.New(`%L`, strftime.WithSpecificationSet(ss))
p.Format(..., time.Now())
実装では、 Appender
インターフェイスを実装する必要があります。
type Appender interface {
Append([]byte, time.Time) []byte
}
ミリ秒の例や Unix タイムスタンプなどの一般的に使用される拡張機能については、ユーザーが次のいずれかを実行できるようにデフォルトの実装が提供されています。
// (1) Pass a specification byte and the Appender
// This allows you to pass arbitrary Appenders
p, err := strftime.New(
`%L`,
strftime.WithSpecification('L', strftime.Milliseconds),
)
// (2) Pass an option that knows to use strftime.Milliseconds
p, err := strftime.New(
`%L`,
strftime.WithMilliseconds('L'),
)
Unix タイムスタンプの場合も同様です。
// (1) Pass a specification byte and the Appender
// This allows you to pass arbitrary Appenders
p, err := strftime.New(
`%s`,
strftime.WithSpecification('s', strftime.UnixSeconds),
)
// (2) Pass an option that knows to use strftime.UnixSeconds
p, err := strftime.New(
`%s`,
strftime.WithUnixSeconds('s'),
)
共通の仕様が欠落している場合は、お気軽に PR を送信してください (ただし、それがどの程度「共通」であるかを必ず主張できるようにしてください)
Milliseconds
(関連オプション: WithMilliseconds
);
Microseconds
(関連オプション: WithMicroseconds
);
UnixSeconds
(関連オプション: WithUnixSeconds
)。
一部のライブラリが特定のプラットフォーム (特に高速バージョン) で cgo を使用していたため、次のベンチマークは個別に実行されました。
// On my OS X 10.14.6, 2.3 GHz Intel Core i5, 16GB memory.
// go version go1.13.4 darwin/amd64
hummingbird% go test -tags bench -benchmem -bench .
<snip>
BenchmarkTebeka-4 297471 3905 ns/op 257 B/op 20 allocs/op
BenchmarkJehiah-4 818444 1773 ns/op 256 B/op 17 allocs/op
BenchmarkFastly-4 2330794 550 ns/op 80 B/op 5 allocs/op
BenchmarkLestrrat-4 916365 1458 ns/op 80 B/op 2 allocs/op
BenchmarkLestrratCachedString-4 2527428 546 ns/op 128 B/op 2 allocs/op
BenchmarkLestrratCachedWriter-4 537422 2155 ns/op 192 B/op 3 allocs/op
PASS
ok github.com/lestrrat-go/strftime 25.618s
// On a host on Google Cloud Platform, machine-type: f1-micro (vCPU x 1, memory: 0.6GB)
// (Yes, I was being skimpy)
// Linux <snip> 4.9.0-11-amd64 #1 SMP Debian 4.9.189-3+deb9u1 (2019-09-20) x86_64 GNU/Linux
// go version go1.13.4 linux/amd64
hummingbird% go test -tags bench -benchmem -bench .
<snip>
BenchmarkTebeka 254997 4726 ns/op 256 B/op 20 allocs/op
BenchmarkJehiah 659289 1882 ns/op 256 B/op 17 allocs/op
BenchmarkFastly 389150 3044 ns/op 224 B/op 13 allocs/op
BenchmarkLestrrat 699069 1780 ns/op 80 B/op 2 allocs/op
BenchmarkLestrratCachedString 2081594 589 ns/op 128 B/op 2 allocs/op
BenchmarkLestrratCachedWriter 825763 1480 ns/op 192 B/op 3 allocs/op
PASS
ok github.com/lestrrat-go/strftime 11.355s
このライブラリは、フォーマット パターンを再利用できる場合、他のライブラリよりもはるかに高速です。
以下は、ベンチマーク結果の注釈付きリストです。 Strftime
オブジェクトを (再) 使用して文字列を生成するのが最も速いことがはっきりとわかります。 io.Writer
への書き込みは少し遅いように見えますが、文字列を生成する側がほぼ同じことを行っているため、これは純粋にio.Writer
への書き込みのオーバーヘッドであると考えられます。
インポートパス | スコア | 注記 |
---|---|---|
github.com/lestrrat-go/strftime | 3000000 | FormatString() 使用 (キャッシュ) |
github.com/fastly/go-utils/strftime | 2000000 | OS X 上の純粋な go バージョン |
github.com/lestrrat-go/strftime | 1000000 | Format() の使用 (キャッシュされません) |
github.com/jehiah/go-strftime | 1000000 | |
github.com/fastly/go-utils/strftime | 1000000 | Linux 上の cgo バージョン |
github.com/lestrrat-go/strftime | 500000 | Format() の使用 (キャッシュ) |
github.com/tebeka/strftime | 300000 |
ただし、パターンによっては、この速度が異なる場合があります。動作が遅いと思われる特定のパターンを見つけた場合は、パッチまたはテストを送信してください。
このベンチマークでは、比較したすべてのライブラリでサポートされている変換仕様のサブセットのみが使用されることにも注意してください。
今後パフォーマンスを比較する際に考慮すべき点は次のとおりです。
%specification
を処理しますか?