.NET8 究極のパフォーマンス最適化 プリミティブ - DateTime

.NET8 究極のパフォーマンス最適化 プリミティブ - DateTime

序文

前回の記事では列挙型の最適化について説明しました。今回は時刻形式である DateTime の最適化について見ていきます。

概要

例として DateTime と DateTimeOffset を示します。 dotnet/runtime#84963 は、DateTime{Offset} フォーマットのさまざまな側面を改善します。

  • フォーマット ロジックには、フォールバックとして使用するための一般的なサポートがあり、任意のカスタム フォーマットをサポートしますが、最も一般的なフォーマット専用のルーチンもあり、それらを最適化および調整できます。非常に人気のある「r」(RFC1123 パターン)および「o」(ラウンドトリップ日付/時刻パターン)形式には、すでに専用ルーチンが存在します。この PR では、さまざまなドメインで頻繁に使用される、不変カルチャで使用されるデフォルトの形式(「G」)、"s" 形式(並べ替え可能な日付/時刻パターン)、および "u" 形式(ユニバーサルな並べ替え可能な日付/時刻パターン)用の専用ルーチンを追加します。
  • 「U」形式 (ユニバーサル完全日付/時刻パターン) の場合、実装では常に新しいインスタンスとインスタンスが割り当てられることになり、まれなフォールバックの場合にのみ必要な場合でも、割り当てが大量に発生します。これにより問題は修正され、本当に必要な場合にのみ割り当てられるようになります。日付時刻形式情報グレゴリオ暦
  • 専用のフォーマット ルーチンがない場合、フォーマットは、指定されたスパン バッファ (通常は から) で開始され、必要に応じてメモリとともに拡大する内部呼び出しに対して実行されます。フォーマットが完了すると、フォーマットをトリガーしたメソッドに応じて、ジェネレーターは宛先範囲または新しい文字列にコピーされます。ただし、ビルダーにターゲット範囲のシードのみを提供する場合は、ターゲット範囲のコピーを回避できます。次に、フォーマットが完了したときにビルダーにまだ初期スパンが含まれている場合 (そこから何も拡張されていない)、すべてのデータが収まることがわかり、すべてのデータがすでにそこにあるため、コピーをスキップできます。 ref structValueListBuilder<T>stackallocArrayPool

次の例は、いくつかの効果を示しています。

 // dotnet run -c Release -f net7.0 --filter "*" --runtimes net7.0 net8.0 using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using System.Globalization; BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args); [HideColumns("Error", "StdDev", "Median", "RatioSD")] [MemoryDiagnoser(displayGenColumns: false)] public class Tests { private readonly DateTime _dt = new DateTime(2023, 9, 1, 12, 34, 56); private readonly char[] _chars = new char[100]; [Params(null, "s", "u", "U", "G")] public string Format { get; set; } [Benchmark] public string DT_ToString() => _dt.ToString(Format); [Benchmark] public string DT_ToStringInvariant() => _dt.ToString(Format, CultureInfo.InvariantCulture); [Benchmark] public bool DT_TryFormat() => _dt.TryFormat(_chars, out _, Format); [Benchmark] public bool DT_TryFormatInvariant() => _dt.TryFormat(_chars, out _, Format, CultureInfo.InvariantCulture); }

パフォーマンステストは次のとおりです。

方法

ランタイム

形式

平均

比率

割り当て済み

割り当て比率

DT_文字列

.NET 7.0

?

166.64ナノ秒

1.00

64 B

1.00

DT_文字列

.NET 8.0

?

102.45ナノ秒

0.62

64 B

1.00








DT_ToString不変

.NET 7.0

?

161.94ナノ秒

1.00

64 B

1.00

DT_ToString不変

.NET 8.0

?

28.74ナノ秒

0.18

64 B

1.00








DT_TryFormat

.NET 7.0

?

151.52ナノ秒

1.00

該当なし

DT_TryFormat

.NET 8.0

?

78.57ナノ秒

0.52

該当なし








DT_TryFormatInvariant

.NET 7.0

?

140.35ナノ秒

1.00

該当なし

DT_TryFormatInvariant

.NET 8.0

?

18.26ナノ秒

0.13

該当なし








DT_文字列

.NET 7.0

162.86ナノ秒

1.00

64 B

1.00

DT_文字列

.NET 8.0

109.49ナノ秒

0.68

64 B

1.00








DT_ToString不変

.NET 7.0

162.20ナノ秒

1.00

64 B

1.00

DT_ToString不変

.NET 8.0

102.71ナノ秒

0.63

64 B

1.00








DT_TryFormat

.NET 7.0

148.32ナノ秒

1.00

該当なし

DT_TryFormat

.NET 8.0

83.60ナノ秒

0.57

該当なし








DT_TryFormatInvariant

.NET 7.0

145.05ナノ秒

1.00

該当なし

DT_TryFormatInvariant

.NET 8.0

79.77ナノ秒

0.55

該当なし








DT_文字列

.NET 7.0

s

186.44ナノ秒

1.00

64 B

1.00

DT_文字列

.NET 8.0

s

29.35ナノ秒

0.17

64 B

1.00








DT_ToString不変

.NET 7.0

s

182.15ナノ秒

1.00

64 B

1.00

DT_ToString不変

.NET 8.0

s

27.67ナノ秒

0.16

64 B

1.00








DT_TryFormat

.NET 7.0

s

165.08ナノ秒

1.00

該当なし

DT_TryFormat

.NET 8.0

s

15.53ナノ秒

0.09

該当なし








DT_TryFormatInvariant

.NET 7.0

s

155.24ナノ秒

1.00

該当なし

DT_TryFormatInvariant

.NET 8.0

s

15.50ナノ秒

0.10

該当なし








DT_文字列

.NET 7.0

あなた

184.71ナノ秒

1.00

64 B

1.00

DT_文字列

.NET 8.0

あなた

29.62ナノ秒

0.16

64 B

1.00








DT_ToString不変

.NET 7.0

あなた

184.01ナノ秒

1.00

64 B

1.00

DT_ToString不変

.NET 8.0

あなた

26.98ナノ秒

0.15

64 B

1.00








DT_TryFormat

.NET 7.0

あなた

171.73ナノ秒

1.00

該当なし

DT_TryFormat

.NET 8.0

あなた

16.08ナノ秒

0.09

該当なし








DT_TryFormatInvariant

.NET 7.0

あなた

158.42ナノ秒

1.00

該当なし

DT_TryFormatInvariant

.NET 8.0

あなた

15.58ナノ秒

0.10

該当なし








DT_文字列

.NET 7.0

あなた

1,622.28ナノ秒

1.00

1240年

1.00

DT_文字列

.NET 8.0

あなた

206.08ナノ秒

0.13

96 B

0.08








DT_ToString不変

.NET 7.0

あなた

1,567.92ナノ秒

1.00

1240年

1.00

DT_ToString不変

.NET 8.0

あなた

207.60ナノ秒

0.13

96 B

0.08








DT_TryFormat

.NET 7.0

あなた

1,590.27ナノ秒

1.00

1144年

1.00

DT_TryFormat

.NET 8.0

あなた

190.98ナノ秒

0.12

0.00








DT_TryFormatInvariant

.NET 7.0

あなた

1,560.00ナノ秒

1.00

1144年

1.00

DT_TryFormatInvariant

.NET 8.0

あなた

184.11ナノ秒

0.12

0.00

解析にも大きな改善が見られました。たとえば、カスタム書式文字列内の「ddd」(曜日の略称)、「dddd」(曜日の正式名称)、「MMM」(月の略称)、「MMMM」(月の正式名称)の処理が改善されました。これらは、RFC1123 形式の拡張定義(ddd、dd MMM yyyy HH':'mm':'ss 'GMT')など、さまざまな一般的な書式文字列に表示されます。汎用解析ルーチンが書式文字列でこれらに遭遇すると、提供された CultureInfo / DateTimeFormatInfo を参照して、その言語ロケールの関連する月と日の名前 (例: DateTimeFormatInfo.GetAbbreviatedMonthName) を取得し、次に各名前と入力テキストの大文字と小文字を区別しない比較を行う必要があります。これはコストがかかります。ただし、不変の言語ロケールを取得すれば、はるかに高速に実行できます。はるかに高速です。たとえば、「MMM」は月の略称を表します。次の3文字(uint m0 = span[0]、m1 = span[1]、m2 = span[2])を読み取り、それらがすべてASCIIであること((m0 | m1 | m2)<= 0x7F)を確認してから、前に説明したのと同じASCII大文字小文字のトリックを使用して、それらすべてを1つのuintにマージします((m0 << 16)|(m1 << 8)| m2 | 0x202020)。同じことを各月の名前に対して実行できます。これは、事前にわかっている不変のロケールの場合、検索全体が単一の数値スイッチになります。

 switch ((m0 << 16) | (m1 << 8) | m2 | 0x202020) { case 0x6a616e: /* 'jan' */ result = 1; break; case 0x666562: /* 'feb' */ result = 2; break; case 0x6d6172: /* 'mar' */ result = 3; break; case 0x617072: /* 'apr' */ result = 4; break; case 0x6d6179: /* 'may' */ result = 5; break; case 0x6a756e: /* 'jun' */ result = 6; break; case 0x6a756c: /* 'jul' */ result = 7; break; case 0x617567: /* 'aug' */ result = 8; break; case 0x736570: /* 'sep' */ result = 9; break; case 0x6f6374: /* 'oct' */ result = 10; break; case 0x6e6f76: /* 'nov' */ result = 11; break; case 0x646563: /* 'dec' */ result = 12; break; default: maxMatchStrLen = 0; break; // undo match assumption }

エレガントで、はるかに高速です。

 // dotnet run -c Release -f net7.0 --filter "*" --runtimes net7.0 net8.0 using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using System.Globalization; BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args); [HideColumns("Error", "StdDev", "Median", "RatioSD")] [MemoryDiagnoser(displayGenColumns: false)] public class Tests { private const string Format = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"; private readonly string _s = new DateTime(1955, 11, 5, 6, 0, 0, DateTimeKind.Utc).ToString(Format, CultureInfo.InvariantCulture); [Benchmark] public void ParseExact() => DateTimeOffset.ParseExact(_s, Format, CultureInfo.InvariantCulture, DateTimeStyles.AllowInnerWhite | DateTimeStyles.AssumeUniversal); }

パフォーマンス比較:

方法

ランタイム

平均値

比率

配布する

配分比率

パース正確

.NET 7.0

1,139.3ナノ秒

1.00

80 B

1.00

パース正確

.NET 8.0

318.6ナノ秒

0.28

0.00


<<:  GPT-4.5がリーク、3Dビデオをサポート、価格は6倍に上昇?ウルトラマンが自ら反応

>>:  建築環境における人工知能:その可能性を実現するためのステップ

ブログ    
ブログ    
ブログ    

推薦する

AppleはAI競争で遅れをとり、市場価値ランキングはAmazon、Google、Microsoftに追い抜かれる可能性も

米国現地時間9月8日木曜日、投資会社ニーダム・セキュリティーズは、アマゾン、グーグル、マイクロソフト...

デジタルヒューマンのための大規模モデル

ビッグモデルはソフトウェア業界全体を変えるでしょう。その代表的な製品の一つがデジタルヒューマンです。...

プロジェクトを始めたいけれど、どこから始めればいいのか分からないですか?興味深いオープンソースの機械学習プロジェクト7つを試してみる

プロジェクトを実行することが機械学習を学ぶ唯一の方法であり、興味深く価値のあるプロジェクトを見つける...

高齢者介護の問題がますます顕著になり、人工知能が大きな注目を集めている

[[431999]]新しい世代が古い世代に取って代わると、古い世代はどこへ行くのでしょうか。今日、2...

Microsoft が機械学習モデルを簡単に作成できる Lobe デスクトップ アプリケーションをリリース

なお、Lobe はインターネット接続やログインを必要とせず、現在は機械学習モデルの出力のみ可能である...

宇宙全体が巨大なニューラルネットワークなのだろうか?科学者はこう説明する

[[385301]]核となる考え方は、次のように簡単にまとめることができます。ニューラル ネットワー...

...

2020年の人工知能市場の現状と発展の見通し

最近、教育部、国家発展改革委員会、財政部は共同で「『双一流』建設大学における人工知能分野の学科統合の...

未来 | 人工知能が人間社会を変える24の方法

今こそ、AI の将来を本当に理解するときです。 AI を取り巻く不安は雇用の減少など多岐にわたります...

...

新たな勢力が市場に参入、コンパイラーの巨匠クリス・ラトナーが自らのビジネスを始める

「私たちのチームは、世界最大かつ最先端のテクノロジー企業出身者で構成されています。世界クラスの才能あ...

自然言語処理: 人工知能の重要な要素

自然言語処理 (NLP) により、コンピューターは人間の言語のニュアンスを理解できるようになります。...

...