SlideShare a Scribd company logo
.NET Conf in Tokyo 2019
鈴木 孝明
Inside FastEnum
Name
鈴木 孝明 a.k.a @xin9le
Work
Application Engineer
Award
Microsoft MVP
for Developer Technologies
Web Site
https://xin9le.net
About
Remote Worker @福井
空港がない
陸の孤島
台風 19 号
北陸新幹線
復旧感謝!
速さは正義
という話をしました
https://www.slideshare.net/xin9le/dotnetperformancetips-170268354
狂気に満ちた世界 (その壱
https://www.slideshare.net/neuecc/cedec-2018-c-c
狂気に満ちた世界 (その弐
https://learning.unity3d.jp/3305/
.NET Core is SUPER fast !!
メモリ消費量
-92%
スループット
+42%
超絶長編記事も公開
Inside FastEnum
Enum is too slow…
Inside FastEnum
The world fastest Enum (FastEnum)
https://github.com/xin9le/FastEnum
System.Enum とほぼ同様の使用感
var values = FastEnum.GetValues<Fruits>();
var values = Enum.GetValues(typeof(Fruits)) as Fruits[];
var names = FastEnum.GetNames<Fruits>();
var names = Enum.GetNames(typeof(Fruits));
var parse = FastEnum.Parse<Fruits>("Apple");
var parse = Enum.Parse<Fruits>("Apple");
var defined = FastEnum.IsDefined(Fruits.Lemon);
var defined = Enum.IsDefined(typeof(Fruits), Fruits.Lemon);
フィールドの値/名前などを一纏めに
値/名前のペアって案外使うのに、ペアを作る処理は地味に面倒
Member<T>
// フィールド情報
public sealed class Member<T>
where T : struct, Enum
{
public T Value { get; }
public string Name { get; }
public FieldInfo FieldInfo { get; }
public EnumMemberAttribute EnumMemberAttribute { get; }
}
// 超高速で簡単な取得
var xs = FastEnum.GetMembers<Fruits>();
var x1 = FastEnum.GetMember(Fruits.Apple);
var x2 = Fruits.Apple.ToMember();
フィールドに複数の名前を付けられる
[EnumMember] だと AllowMultiple = false なので不便
Grani 社内で使われていた便利機能のひとつ
Label 属性
// インデックス指定で取得できる
var a = Fruits.Apple.GetLabel(0); // 🍎
var b = Fruits.Apple.GetLabel(1); // りんご
// こんな定義があったとして
public enum Fruits
{
[Label("🍎", 0)]
[Label(“りんご", 1)]
Apple,
}
速くなったと言われる .NET Core 3.0 なのに、なぜ!
Why standard enum is slow?
RuntimeType.GenericCache を使ってる
そこまでは良いのだけど、キャッシュの持ち方がよくない
ulong[] / string[] を持つだけで、それ以上のトリックがない
都度 Reflection している箇所も
Enum.GetName / Enum.IsDefined などなど
キャッシュとは何だったのか...
中途半端なキャッシュ
ほぼ必ず object 型を経由する
Non Generics な Array とか Enum.ToObject とかを普通に使う
ulong で持っていたものを有効活用しないとか超クール
Parse<T> / TryParse<T>はまとも
System.Enum で Generics 版が使えるときは積極的に使う
※ .NET Core に限る
※ .NET Framework の実装は残念過ぎる
Box / Unbox の雨霰
.NET Core is SUPER fast ??
Inside FastEnum
高速化のアプローチとちょっとした工夫
Why FastEnum is fast?
Static Type Caching
public static class FastEnum
{
public static IReadOnlyList<T> GetValues<T>()
where T : struct, Enum
=> Cache<T>.Values; // キャッシュを直接参照
// 静的 Generics 型のフィールドにキャッシュを持つ
private static class Cache<T>
where T : struct, Enum
{
public static readonly T[] Values;
static Cache()
=> Values = (T[])Enum.GetValues(typeof(T));
}
}
T 型ごとに
別の型扱い
呼び出しコスト ≒ 0
型をキーにした辞書から Lookup するよりもずっと速い
BenchmarkDotNet が「計測できない」と音を上げるレベル
静的コンストラクタで事前計算
静的コンストラクタはスレッドセーフが保証されている
ロックフリーにできるので、呼び出しコスト低減に大きく寄与
Static Type Caching
Non Generics API は box 化を避けられない
Generics API だけでも大抵のケースに耐えられる
Generics API のみをサポート
// .NET Core (Box 化する)
var a = Enum.GetValues(typeof(Fruits));
var b = Enum.GetName(typeof(Fruits), Fruits.Banana);
var c = (Fruits)Enum.Parse(typeof(Fruits), "Apple");
var d = Enum.IsDefined(typeof(Fruits), Fruits.Lemon);
// FastEnum (一切 Box 化しない)
var a = FastEnum.GetValues<Fruits>();
var b = FastEnum.GetName(Fruits.Banana);
var c = FastEnum.Parse<Fruits>("Apple");
var d = FastEnum.IsDefined(Fruits.Lemon);
[Flags] な列挙型を Parse する機能
カンマが入っているかどうかを算出するコストが大きい
滅多に使わない機能を落として大多数を救う方が理に適っている
カンマ区切り文字列は非サポート
// こんな定義があるとき
[Flags]
enum Fruits
{
Apple = 1,
Lemon = 2,
Melon = 4,
}
// System.Enum はカンマ区切り文字を Parse できる
var value = Enum.Parse<Fruits>("Apple, Melon");
Console.WriteLine((int)value); // 5
列挙型の値の連続性を利用
値が連続しているなら最大/最小範囲に入っているかを比較
辞書の ContainsKey をするより範囲チェックの方が何倍も高速
IsDefined<T>(value); の最適化
// 連続した値の列挙型
enum Fruits
{
Apple = 1,
Lemon, // 2
Melon, // 3
Banana, // 4
}
// こんな感じのイメージで判定すると速い
// 連続しているかどうかは事前に判定してキャッシュ
public static bool IsDefined<T>(T value)
where T : struct, Enum
=> IsContinuous
? MinValue <= value && value <= MaxValue
: Values.ContainsKey(value);
“123”, “Apple” をどう判別/解析するか
「1 文字目が数字か +/-」の場合は値で解析する
Parse<T>(“value/name”); の最適化
public static bool TryParse<T>(string value, out T result)
where T : struct, Enum
{
// フィールド名は数字/特殊記号で始められない仕様を利用
return IsNumeric(value[0])
? TryParseByValue(value, out result)
: TryParseByName(value, out result);
static bool IsNumeric(char c)
=> char.IsDigit(c) || c == '-' || c == '+';
}
string.Equals(OrdinalIgnoreCase) が最速
StringComparison.InvariantCultureIgnoreCase より 4 倍速い
大小無視の文字列比較
キーの特殊化で EqualityComparer を廃止
GetHashCode(); と Equals(); を直呼び出しにするハック
http://engineering.grani.jp/entry/2017/07/28/145035
温もりティ溢れる手作り辞書
// 正味たった 2 行だけの違い
// 最終的にこの差がベンチマークに効いてくる
var hash = EqualityComparer<int>.Default.GetHashCode(key);
var hash = key.GetHashCode(); // faster
if (EqualityComparer<TKey>.Default.Equals(next.Key, key))
if (next.Key == key) // faster
他にもある細々したテクニックとかポイント
Other tips and tricks
列挙型を等値/大小で比較する
// 具象型の場合はできるけれど...
public static void Compare(Fruits left, Fruits right)
{
var a = left == right; // OK
var b = left <= right; // OK
}
// Enum 制約をしていても Generics 型だとできない!
public static void Compare<T>(T left, T right)
where T : struct, Enum
{
var a = left == right; // コンパイルエラー
var b = left <= right; // コンパイルエラー
var c = EqualityComparer<T>.Default.Equals(left, right); // OK
}
なんとか
解決したい
System.Runtime.CompilerServices.Unsafe
unsafe オプションを使わずに unsafe なことをする悪い子😈
Unsafe.As を使った強制型変換
public static void Compare<T>(T left, T right)
where T : struct, Enum
{
// ref を使った型変換 = 同一メモリ領域を別の型として参照
ref var l = ref Unsafe.As<T, int>(ref left);
ref var r = ref Unsafe.As<T, int>(ref right);
var a = l == r; // OK
var b = l <= r; // OK
}
UnderlyingType の TryParse に委譲
同一メモリ領域を参照した強制型変換なので、できちゃう😈
Unsafe.As のちょっとズルい使い方
internal sealed class Int32Operation<T> : IUnderlyingOperation<T>
where T : struct, Enum
{
public bool TryParse(string text, out T result)
{
// x に書き込めば result にも書き込まれたことになる
result = default;
ref var x = ref Unsafe.As<T, int>(ref result);
return int.TryParse(text, out x);
}
}
FastEnum が唯一勝てなかったメソッド
引数が Enum なので呼び出すだけで box 化して超遅そうだが…
自作したものより 200 倍以上高速でアロケーションもない
// 特殊な最適化がかかってる (たぶん
[Intrinsic]
public bool HasFlag(Enum flag)
{}
Enum.HasFlag の脅威的性能
ReadOnlyCollection は foreach が超遅い
Struct Enumerator を利用した配列の読み取り専用 wrapper で解決
https://ufcpp.net/blog/2018/12/howtoenumerate/
ReadOnlyArray
public sealed class ReadOnlyArray<T> : IReadOnlyList<T>
{
private readonly T[] source;
public ReadOnlyArray(T[] source) => this.source = source;
public Enumerator GetEnumerator() => new Enumerator(this.source);
public struct Enumerator : IEnumerator<T> { /* 省略 */ }
// 以下略...
}
今日、これだけは持って帰りましょう!
Conclusion
速さは正義
ちょっとした工夫で案外メチャクチャ速くなる!
速いコードを書くひとつの参考になれば幸いです
本当は FastEnum は存在しない方が良い
標準が速ければ最高だし、そうあってほしい
.NET Core 頑張れ
FastEnum is SUPER fast !!
Enjoy high performance programming using C#!!
Thank you

More Related Content

PPTX
冬のLock free祭り safe
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
PDF
【Unite Tokyo 2019】Understanding C# Struct All Things
PDF
C/C++プログラマのための開発ツール
PDF
ネットワーク ゲームにおけるTCPとUDPの使い分け
PDF
CODE BLUE 2014 : バグハンターの愉しみ by キヌガワマサト Masato Kinugawa
PPTX
C# 8.0 非同期ストリーム
PPTX
スマホゲームのチート手法とその対策 [DeNA TechCon 2019]
冬のLock free祭り safe
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
【Unite Tokyo 2019】Understanding C# Struct All Things
C/C++プログラマのための開発ツール
ネットワーク ゲームにおけるTCPとUDPの使い分け
CODE BLUE 2014 : バグハンターの愉しみ by キヌガワマサト Masato Kinugawa
C# 8.0 非同期ストリーム
スマホゲームのチート手法とその対策 [DeNA TechCon 2019]

What's hot (20)

PDF
目grep入門 +解説
PDF
マルチコアとネットワークスタックの高速化技法
PPTX
C#で速度を極めるいろは
PDF
CTF for ビギナーズ ネットワーク講習資料
PPT
Glibc malloc internal
PDF
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
PDF
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
PDF
ARM Trusted FirmwareのBL31を単体で使う!
PDF
今日からできる!簡単 .NET 高速化 Tips
PPTX
最速C# 7.x
PDF
Scapyで作る・解析するパケット
PDF
C++ マルチスレッド 入門
PDF
C++ マルチスレッドプログラミング
PDF
WebAssembly向け多倍長演算の実装
PPTX
本当は恐ろしい分散システムの話
PDF
いまさら聞けないarmを使ったNEONの基礎と活用事例
ODP
Format string Attack
PDF
DSIRNLP #3 LZ4 の速さの秘密に迫ってみる
PPTX
async/await のしくみ
目grep入門 +解説
マルチコアとネットワークスタックの高速化技法
C#で速度を極めるいろは
CTF for ビギナーズ ネットワーク講習資料
Glibc malloc internal
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
ARM Trusted FirmwareのBL31を単体で使う!
今日からできる!簡単 .NET 高速化 Tips
最速C# 7.x
Scapyで作る・解析するパケット
C++ マルチスレッド 入門
C++ マルチスレッドプログラミング
WebAssembly向け多倍長演算の実装
本当は恐ろしい分散システムの話
いまさら聞けないarmを使ったNEONの基礎と活用事例
Format string Attack
DSIRNLP #3 LZ4 の速さの秘密に迫ってみる
async/await のしくみ
Ad

Similar to Inside FastEnum (20)

PPT
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
PPTX
C++ tips 3 カンマ演算子編
PDF
新しい並列for構文のご提案
PPTX
.NET Core 2.x 時代の C#
PDF
async/await不要論
PDF
Unity2015_No10_~UGUI&Audio~
PDF
Clrh 20140906 lt
PDF
Swift 2.0 で変わったところ「前編」 #cswift
PDF
Async design with Unity3D
PPT
2008.10.18 L4u Tech Talk
ODP
Buffer overflow
PPTX
CTF(Capture the Flag)って何?
PDF
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
PDF
Enumはデキる子 ~ case .Success(let value): ~
PDF
コンテナセキュリティにおける権限制御(OCHaCafe5 #3 Kubernetes のセキュリティ 発表資料)
PDF
Kanazawa.js.Next
PDF
Rust Error Handling
PDF
研究生のためのC++ no.2
PDF
Python physicalcomputing
PDF
Pfi Seminar 2010 1 7
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
C++ tips 3 カンマ演算子編
新しい並列for構文のご提案
.NET Core 2.x 時代の C#
async/await不要論
Unity2015_No10_~UGUI&Audio~
Clrh 20140906 lt
Swift 2.0 で変わったところ「前編」 #cswift
Async design with Unity3D
2008.10.18 L4u Tech Talk
Buffer overflow
CTF(Capture the Flag)って何?
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
Enumはデキる子 ~ case .Success(let value): ~
コンテナセキュリティにおける権限制御(OCHaCafe5 #3 Kubernetes のセキュリティ 発表資料)
Kanazawa.js.Next
Rust Error Handling
研究生のためのC++ no.2
Python physicalcomputing
Pfi Seminar 2010 1 7
Ad

More from Takaaki Suzuki (20)

PDF
5 分で学ぶ Interpolated String Handler
PDF
C# における Redis 徹底活用
PDF
30min Serverless xTuber
PPTX
Tetris Algorithm
PPTX
C# 7 New Features
PPTX
Live Coding で学ぶ C# 7
PPTX
C# 7 Current Status
PPTX
4 Colors Othello’s Algorithm
PPTX
Sharing Deep Dive
PDF
4 Colors Othello’s Algorithm @仙台 IT 文化祭 2017
PDF
DeclarativeSql
PDF
Sevens Algorithm
PDF
Friendly
PDF
WPF Interoperability
PDF
Universal Appとは? -デバイスに依存しないアプリケーション開発-
PDF
酒の肴はC# vNext
PDF
Async History in .NET
PDF
SignalR Tune-up
PDF
Twilioと.NET
PDF
Programmer's Brain
5 分で学ぶ Interpolated String Handler
C# における Redis 徹底活用
30min Serverless xTuber
Tetris Algorithm
C# 7 New Features
Live Coding で学ぶ C# 7
C# 7 Current Status
4 Colors Othello’s Algorithm
Sharing Deep Dive
4 Colors Othello’s Algorithm @仙台 IT 文化祭 2017
DeclarativeSql
Sevens Algorithm
Friendly
WPF Interoperability
Universal Appとは? -デバイスに依存しないアプリケーション開発-
酒の肴はC# vNext
Async History in .NET
SignalR Tune-up
Twilioと.NET
Programmer's Brain

Inside FastEnum