Embed presentation
Download as PDF, PPTX














![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);](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-15-2048.jpg&f=jpg&w=240)

![フィールドに複数の名前を付けられる[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,}](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-17-2048.jpg&f=jpg&w=240)

![RuntimeType.GenericCache を使ってるそこまでは良いのだけど、キャッシュの持ち方がよくないulong[] / string[] を持つだけで、それ以上のトリックがない都度 Reflection している箇所もEnum.GetName / Enum.IsDefined などなどキャッシュとは何だったのか...中途半端なキャッシュ](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-19-2048.jpg&f=jpg&w=240)




![Static Type Cachingpublic 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 型ごとに別の型扱い](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-24-2048.jpg&f=jpg&w=240)


![[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](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-27-2048.jpg&f=jpg&w=240)

![“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 == '+';}](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-29-2048.jpg&f=jpg&w=240)






![FastEnum が唯一勝てなかったメソッド引数が Enum なので呼び出すだけで box 化して超遅そうだが…自作したものより 200 倍以上高速でアロケーションもない// 特殊な最適化がかかってる (たぶん[Intrinsic]public bool HasFlag(Enum flag){}Enum.HasFlag の脅威的性能](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-36-2048.jpg&f=jpg&w=240)
![ReadOnlyCollection は foreach が超遅いStruct Enumerator を利用した配列の読み取り専用 wrapper で解決https://ufcpp.net/blog/2018/12/howtoenumerate/ReadOnlyArraypublic 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> { /* 省略 */ }// 以下略...}](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-37-2048.jpg&f=jpg&w=240)




Event : .NET Conf in Tokyo 2019Date : 2019/10/27.NET Core 3.0 がリリースされさらなるパフォーマンスチューニングが行われましたが、Enum は依然として遅いままです。FastEnum はその苦痛を緩和する世界最速の Enum Utility ライブラリです。このドキュメントでは FastEnum の中で使われているテクニックや .NET Core 標準が遅い理由などについて解説し、速いコードを書くための一助となるようにします。














![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);](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-15-2048.jpg&f=jpg&w=240)

![フィールドに複数の名前を付けられる[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,}](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-17-2048.jpg&f=jpg&w=240)

![RuntimeType.GenericCache を使ってるそこまでは良いのだけど、キャッシュの持ち方がよくないulong[] / string[] を持つだけで、それ以上のトリックがない都度 Reflection している箇所もEnum.GetName / Enum.IsDefined などなどキャッシュとは何だったのか...中途半端なキャッシュ](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-19-2048.jpg&f=jpg&w=240)




![Static Type Cachingpublic 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 型ごとに別の型扱い](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-24-2048.jpg&f=jpg&w=240)


![[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](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-27-2048.jpg&f=jpg&w=240)

![“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 == '+';}](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-29-2048.jpg&f=jpg&w=240)






![FastEnum が唯一勝てなかったメソッド引数が Enum なので呼び出すだけで box 化して超遅そうだが…自作したものより 200 倍以上高速でアロケーションもない// 特殊な最適化がかかってる (たぶん[Intrinsic]public bool HasFlag(Enum flag){}Enum.HasFlag の脅威的性能](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-36-2048.jpg&f=jpg&w=240)
![ReadOnlyCollection は foreach が超遅いStruct Enumerator を利用した配列の読み取り専用 wrapper で解決https://ufcpp.net/blog/2018/12/howtoenumerate/ReadOnlyArraypublic 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> { /* 省略 */ }// 以下略...}](/image.pl?url=https%3a%2f%2fimage.slidesharecdn.com%2finsidefastenum-191027074406%2f75%2fInside-FastEnum-37-2048.jpg&f=jpg&w=240)


