SlideShare a Scribd company logo
最速C#7.x
2018/08/06
株式会社コアコンセプトテクノロジー
HQ事業部 山本礼貴
アジェンダ
 前提
 C#7.xで変わったstructと引数
 どうしたら速くなるか
 C#7.xでrefを使いこなす
 すべてはスタックを使いこなすため
 まとめ
前提 ここで扱う話と扱わない
話について
前提(1)
 C#7.3前提
 メジャーバージョン(7.0)に拘るのは残念なこと
 言語開発がアジャイル化しているので、最新版に触れるのは
重要
前提(2)
 高速化とは(原則)
 コピーを減らすこと(今回の要点1)
 GCを極力動かさないこと(今回の要点2)
 キャッシュが効きやすいコードを書くこと
 ロジックの簡素化
 ループの簡素化
 e.t.c…(これらは別の機会に)
 並列化すること
 これは別の機会に
前提(3)
 安全なコードでなければならない
 unsafeコードやポインタは使わない
 一般に危険とされるコードは使わない
 これまで通り、C#を使う人にとってC#は安全な言語
前提(4)
readonly なフィールドは必要
 変数の保護が無かったらC言語と変わらない。
 フィールドの保護のためにアクセシビリティやプロパティが
あった
 変数を保護できない言語はチームで扱うと難易度が高いのは
自明
 だからunsafeでポインタを使うのは、C#ではメンテナンス性
を犠牲にする行為として扱われる
 ref を使うことすらデザインとして非推奨だった
 速度を犠牲にするのかという問題とは常に向き合う必要があった
C#7.xで
変わった
structと
引数
構造体とスタックの扱い
方をエンジニアが細かく
制御できるようになった
しかし、無条件に速くは
ならない
readonlyがstruct全体の属性と
して使用できる
 言語的に構造体全体のImmutable<不変性>を保証する書き
方ができるようになった
 readonly struct と書くと、それはImmutableとなる
 例えばthis代入はコンパイルエラーになるようになった
 readonly struct Foo{
public readonly int _x;
public Foo(x) => _x = x;
public void Update(int x) => this = new Foo(x);
}
 structではエラーとならない(従来のC#では readonly フィー
ルドは書き換え可能だったということ)
でもreadonlyってたしか…
 ref で渡せないよね?
 readonly のものは参照渡しをすることが出来ない
 readonly int x;
foo(ref x); // コンパイルエラー
 値の保護と引き換えに値渡し(コピー)しかできないということ
 コピーとはそのまま処理時間の増加を意味する
 内部を書き換えるメソッド呼んでも、書き換えできない
 readonly Rect rect;
rect.Offset(3, 3); // メソッドは呼ばれるけど変更されない
(defensive copyというやつ)
Defensive Copyとは?
 readonly の値型フィールドのメソッド、プロパティが使用
される場合、フィールドの値を保護するためにスタック上
にコピーを作る仕組み
 readonly は安全のために必要なので、やめるわけにいいかな
い(なぜなら以下のようなコードが実行可能だから)
 class Foo {
readonly Rect _rect = new Rect();
void Process() { _rect.Offset(2, 2); }
}
 Foo.Process() 実行時に _rect を守るには暗黙的にコピーするし
かない。
 コピーとはそのまま処理時間の増加を意味する
in引数が追加された
 readonly 属性を持つものを参照渡しできる
 readonly ref という名前も検討されていたが out との対として
in の名称が採用された
 メソッド側だけ in を書けば、呼び出し側は意識せずに使える
 変数、フィールド、プロパティなど何でも渡せる
 readonlyのフィールドでも渡せる(もしかして速くなった?)
inパラメータ万能?速い?
 ref を書かないでも値型を参照渡ししてくれる
 値型をreadonly参照で受け取る
 参照してくれる
 readonly のフィールドも渡せる
 参照してくれる
 プロパティを渡せる
 スタックに1回コピーを作って、それを参照してくれる(横着を認
めてくれる)
 何故か遅い
 ベンチマークをしたら値で渡すより遅いことが多い
またもや defensive copy
 in で参照を受け取るとそのパラメータは readonly 属性を持
つ
 メソッド/プロパティを使うとdefensive copyでスタックにコ
ピーを生成する
 値をそのまま渡した場合より悪い結果に
 標準.NET Frameworkのstructは全部プロパティにすら触れない有
様
 コピーとはそのまま処理時間の増加を意味する
 もう、どうすりゃいいの?(←C#7.2を弄り始めたころの自分)
どうしたら
速くなるか
解決編
readonly struct + inパラメータ
 in にしてまともに機能するのは readonly structのみ
 値だけではなく型自体を readonly にするという仕様が追加さ
れて、初めて defensive copy が無くなった
 this への代入もできなくなっている!
 スタック上に値を保持できる
 ついでにコールスタック経路にあるオブジェクトはGCによって移
動されない
 これだけで「コピーをしない」「GCをなるべく動かさない」
という原則の2つに則る
readonly struct の上手な使い方
 16バイト超から readonly struct(64bitの場合)
 不変の値型としてnew できるのでフィールドをgetter/setterで
ラッピングしない
 静的解析でケチがつくので、場所を絞ってCA1051警告を抑止する
 最適化が爆速で効く(後程ベンチマークあり)
 標準の構造体のImmutable型を自作(ex. Rect -> ImmutableRect)
 implicitで自動変換可能にしておく
 自作のメソッドは in ImmutableXxx の形で受ける
 呼び出し前で1回暗黙キャストがかかるが、2-3回程度メンバーに
アクセスするだけでお釣りが来るほど高速
 内部ではひたすら in で渡す
ベンチマーク
Processの引数の受け止め方によって速度がどのように変わるでしょ
うか。
in引数にしたり、自作のImmutableRectに暗黙のキャストをしたり、
最初からImmutableRectで扱ったりして、所要時間を比較します。
https://github.com/nenaaki/ReadonlyStructBenchmark
ベンチマークの強い味方
 .NET Frameworkでベンチマークを書く際は、
BenchmarkDotNetがおすすめ。
ベンチマーク結果
元の形 引数
修飾
読取り元 ImmutableRect
キャスト
平均値
(us)
Rect Property 327.52
Rect in Property 325.94
Rect Property あり 352.24
Rect in Property あり 319.82
ImmutableRect Property 234.00
ImmutableRect in Property 218.57
Rect Local var 172.55
Rect in Local var 285.42
ImmutableRect in Local var 101.10
Rect
に
つ
い
て
は
キ
ャ
ス
ト
し
て
も
速
い
Defensivecopy
の
せ
い
で
遅
い
C#7.xで
refを使いこ
なす
refを使うと何が良いか
 値のコピーをせずに参照で処理できる
 GCの動作頻度を下げることができる
 stackalloc / Span<T> の合わせ技で(後述)
ref戻り値/refローカル変数
 コレクション系を高速化できる可能性がある
 「値の取得→書き換え→書き戻し」の流れを「参照の取得→
書き換え」に変えることができる
 foreachなどを高速化する。ループの都度コピーが生じない
 注意点
 値がコピーされないので高速になるが、中身を書き換えるとコレ
クションが変化することに注意。
 HashCodeが変化するので、HashSetやDictionaryには禁止
 安全性を考慮すると、readonly struct のみに適用するのが
良い
 非readonlyでも限定的なところでは有用なのでちょっと紹介
ベンチマーク
メソッド名 実行時間(us)
UpdateRectRefArray 1.245 (配列より速い?)
UpdateRectArray 1.279
AsRef<T>
public readonly struct ReferenceArray<T>
{
private readonly T[] _array;
public ReferenceArray(T[] array) => _array = array;
public ReferenceArrayEnumerator<T> GetEnumerator() => new ReferenceArrayEnumerator<T>(_array);
public struct ReferenceArrayEnumerator<T>
{
private int _index;
private readonly T[] _array;
public ReferenceArrayEnumerator(T[] array) => (_index, _array) = (-1, array);
public ref T Current => ref _array[_index];
public bool MoveNext() => ++_index < _array.Length;
}
}
public static class ReferenceArrayExtensions
{
public static ReferenceArray<T> AsRef<T>(this T[] array) => new ReferenceArray<T>(array);
}
参考:
https://ufcpp.net/study/csharp/sp_ref.html
IEnumerable<T>やIEnamurator<T>
を実装する必要は実はない
GetEnumeratorとMoveNextとCurrentがあれば
foreach できる
すべては
スタックを
使いこなす
ため
stackalloc/Span<T>
 これから深い闇を生みそうな機能
 .NET のスタック領域は割と小さいのだが、StackOverflowを正しく
処理できるエンジニアは世に少ないと思う
 10000くらい処理対象があるときに、100~1000刻みくらいの
処理をTaskに分割して、タスク内ではスタック中心で処理する。
 TaskのオーバーヘッドもGCのオーバーヘッドも減らせる。
 Span<T>は.NET Core 2.1まではやや遅い
 ベンチマークはこのサイトにありますので参考に
https://blog.meilcli.net/2018/06/cstackallocstruct.html
 これも ref をどれだけ使いこなせるか勝負
 どんどんC++組んでる気分になっていく
ref struct
 ヒープに配置できない構造体
 つまりGCが動かないスタイルでコード記述が可能
 Span<T>とstackallocのために作られたもの
 一時的な情報を処理するために速度だけを追求した作りが
望ましい。そして外部からは常に ref で扱う。
その他小ネタ
 ValueTupleを使うとヒープを使わないのでGCの動作頻度を下
げることが出来る
 小さなclassはstruct版も作ると良い
 readonly structとユーティリティメソッドの集合という構成にして
おくとかなり良い
 引数に this をではなく ref this を使うと拡張メソッドも高速になる
 毎秒に数万~数億回呼ぶようなコアなところを先に手がけよう
 VisualStudioの分析ツールを活用
 必ずUTとベンチマークを作ること
 BenchmarkDotNet(ベンチマークはこれ1つでいい:無料)
 AxoCover(コードカバレッジ検査用のプラグイン:無料)
まとめ C++に近づいていくなぁ
速くするためには(遅くしないためには)
 標準ライブラリに対してはC#7.xの拡張は効果が薄い
 Defensive copy を発生させない状況のみで効果があるため
 基盤コードをゴリゴリとスクラッチするときに、絶大な威
力を発揮する
 readonly structを作る(徹底的に in 引数とする)
 コレクションの再実装(戻り値を ref 化したい)
 Span<T>と ref 変数を使いこなす
 参照(ref/in)はC++の参照と同じなので、C#7.xの拡張は
「C++で速いシチュエーション」をC#でも同じように動作
させるための拡張としてイメージすると良い
リンク
 このスライドのベンチマークソースコード
https://github.com/nenaaki/ReadonlyStructBenchmark
 AsRef()の参考コード(++C++; // 未確認飛行 C)
https://ufcpp.net/study/csharp/sp_ref.html
 Span<T>のベンチマーク(滅入るんるん)
https://blog.meilcli.net/2018/06/cstackallocstruct.html
ご清聴ありがとうございました

More Related Content

PDF
今日からできる!簡単 .NET 高速化 Tips
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
PDF
【Unite Tokyo 2019】Understanding C# Struct All Things
PDF
規格書で読むC++11のスレッド
PDF
C++ マルチスレッド 入門
PPTX
Msを16倍出し抜くwpf開発1回目
PDF
できる!並列・並行プログラミング
PPTX
C#で速度を極めるいろは
今日からできる!簡単 .NET 高速化 Tips
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
【Unite Tokyo 2019】Understanding C# Struct All Things
規格書で読むC++11のスレッド
C++ マルチスレッド 入門
Msを16倍出し抜くwpf開発1回目
できる!並列・並行プログラミング
C#で速度を極めるいろは

What's hot (20)

PDF
テスト文字列に「うんこ」と入れるな
PPTX
冬のLock free祭り safe
PPTX
C# 8.0 非同期ストリーム
PDF
FridaによるAndroidアプリの動的解析とフッキングの基礎
PDF
JIT のコードを読んでみた
PPTX
C# 8.0 null許容参照型
PDF
ワタシはSingletonがキライだ
PDF
ゲーム開発者のための C++11/C++14
PPTX
PPTX
C#や.NET Frameworkがやっていること
PPTX
C#とILとネイティブと
PDF
暗号技術の実装と数学
PDF
すごい constexpr たのしくレイトレ!
PDF
Constexpr 中3女子テクニック
PDF
Marp Tutorial
PDF
C#でわかる こわくないMonad
PDF
【Unite 2018 Tokyo】60fpsのその先へ!スマホの物量限界に挑んだSTG「アカとブルー」の開発設計
PDF
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
PDF
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
PDF
ELFの動的リンク
テスト文字列に「うんこ」と入れるな
冬のLock free祭り safe
C# 8.0 非同期ストリーム
FridaによるAndroidアプリの動的解析とフッキングの基礎
JIT のコードを読んでみた
C# 8.0 null許容参照型
ワタシはSingletonがキライだ
ゲーム開発者のための C++11/C++14
C#や.NET Frameworkがやっていること
C#とILとネイティブと
暗号技術の実装と数学
すごい constexpr たのしくレイトレ!
Constexpr 中3女子テクニック
Marp Tutorial
C#でわかる こわくないMonad
【Unite 2018 Tokyo】60fpsのその先へ!スマホの物量限界に挑んだSTG「アカとブルー」の開発設計
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
ELFの動的リンク
Ad

Similar to 最速C# 7.x (20)

PDF
C#の新機能勉強会 ~ C#7、8の新機能を活用して速く安全なプログラムを書こう~
PPTX
.NET Core 2.x 時代の C#
PPTX
C# 8.0 Preview in Visual Studio 2019 (16.0)
PPTX
C# 7・8 の復習
PPTX
Deep Dive C# 6.0
PPTX
復習も兼ねて!C#6.0-7.0
PPTX
Live Coding で学ぶ C# 7
PPTX
C# 7.2 with .NET Core 2.1
PDF
20141129-dotNet2015
PPTX
C# 7.2 の新機能
PPTX
.NET Core 3.0 で使える C# 8
PDF
C#勉強会
PPTX
C# 9.0 / .NET 5.0
PDF
C#勉強会 ~ C#9の新機能 ~
PPTX
C# 7 Current Status
PDF
Inside FastEnum
PPTX
Visual basic14 の話
PPTX
PPTX
C#6.0の新機能紹介
PDF
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
C#の新機能勉強会 ~ C#7、8の新機能を活用して速く安全なプログラムを書こう~
.NET Core 2.x 時代の C#
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 7・8 の復習
Deep Dive C# 6.0
復習も兼ねて!C#6.0-7.0
Live Coding で学ぶ C# 7
C# 7.2 with .NET Core 2.1
20141129-dotNet2015
C# 7.2 の新機能
.NET Core 3.0 で使える C# 8
C#勉強会
C# 9.0 / .NET 5.0
C#勉強会 ~ C#9の新機能 ~
C# 7 Current Status
Inside FastEnum
Visual basic14 の話
C#6.0の新機能紹介
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
Ad

最速C# 7.x