.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倍に上昇?ウルトラマンが自ら反応

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

ブログ    
ブログ    
ブログ    

推薦する

ライブチャットとチャットボット: どちらの顧客サービス方法が優れているのでしょうか?

[[267030]] [51CTO.com クイック翻訳] ビジネスの世界は大きな変化を遂げてきま...

2019年、AIバブルは崩壊寸前

[[256693]]中国工業情報化部傘下の中国情報通信研究院によると、2018年上半期の世界の人工知...

Alibaba のエンジニアは、ナレッジ グラフ データ構築の「難題」にどのように取り組んでいるのでしょうか?

[[233069]]アリ姉の紹介:「トマト」を検索すると、その栄養価やカロリーがわかるだけでなく、...

Reddit ユーザーが「泣く」: 私はアルゴリズム エンジニアではなく、「パラメータ調整者」です

[[387580]]まず最初に質問させてください。あなたは自分が「スイッチャー」だと思いますか、それ...

画像はさまざまな方法で変更できます。NVIDIAはGANを使用して高精度のディテールレタッチを実現

[[436122]] EditGAN は、複雑かつ高精度な画像編集効果を実現しながらも、高い画像品質...

...

自然言語処理の究極の方向性: 自然言語処理におけるディープラーニングの 5 つの利点

[[206924]]自然言語処理の分野では、ディープラーニングによって、より多くのデータが必要でも言...

Ele.meにおける人工知能の応用

[[212221]] Ele.meについてほとんどの人がテイクアウトを注文したことがあるでしょう。テ...

なぜ顔認識に嫌悪感を抱くのですか?

[[376016]] △ 2019年9月4日、ノースウェスタン工科大学の学生が顔認識装置を通じて図...

人工知能はビジネス開発にどのような影響を与えるのでしょうか?

[[403654]]人工知能はイノベーションを推進し、ビジネス開発を変えています。人工知能と機械学...

Alibaba iDSTのビジュアルコンピューティング責任者、Hua Xiansheng氏:アルゴリズムの利点は消えつつある

「テクノロジー研究と現実世界の問題を組み合わせ、現実の問題を解決して価値を生み出すことにますます興味...

AWS クラウド機械学習を使用したサーバーレスニュースデータパイプラインの構築

[[436699]] [51CTO.com クイック翻訳]アナリストとして、私はニュースや業界の最新...

劉厳紅が7日間で1000万人のフォロワーを獲得した背後で、スマートフィットネス業界が静かに台頭している

ジェイ・チョウの『本草綱目』のメロディーにのせて、劉恒紅の健康指導が再び始まった。 7日間でフォロワ...

スマートシティ技術の未来: AI、ビッグデータ、クラウド

世界の人口の半分以上が都市に惹かれています。成長、繁栄、雇用、機会、教育、娯楽の誘惑は、抵抗できない...

ジェネレーティブAIがソフトウェア配信を支援する方法

レイチェル・レイコック約 2 か月前、私は Thoughtworks の CTO になりました。それ...