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
【Unite Tokyo 2019】Understanding C# Struct All Things
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
PPTX
C#で速度を極めるいろは
PDF
【Unity】 Behavior TreeでAIを作る
PDF
C# ゲームプログラミングはホントにメモリのことに無頓着でいいの?
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
PDF
【CEDEC2018】CPUを使い切れ! Entity Component System(通称ECS) が切り開く新しいプログラミング
今日からできる!簡単 .NET 高速化 Tips
【Unite Tokyo 2019】Understanding C# Struct All Things
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
C#で速度を極めるいろは
【Unity】 Behavior TreeでAIを作る
C# ゲームプログラミングはホントにメモリのことに無頓着でいいの?
ARM CPUにおけるSIMDを用いた高速計算入門
【CEDEC2018】CPUを使い切れ! Entity Component System(通称ECS) が切り開く新しいプログラミング

What's hot (20)

PDF
ヒストリア HelixCore(Perforce) 運用レギュレーションドキュメント
PDF
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
PPTX
UniRxでMV(R)Pパターン をやってみた
PPTX
Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
PDF
20分くらいでわかった気分になれるC++20コルーチン
PDF
ゲーム開発者のための C++11/C++14
PDF
.NET Core 3.0時代のメモリ管理
PDF
Unity開発で使える設計の話+Zenjectの紹介
PDF
Unityでパフォーマンスの良いUIを作る為のTips
PDF
Observableで非同期処理
PDF
目grep入門 +解説
PPTX
イベント駆動プログラミングとI/O多重化
PDF
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
PPTX
C# 8.0 非同期ストリーム
PPTX
大規模ゲーム開発における build 高速化と安定化
PPTX
未来のプログラミング技術をUnityで -UniRx-
PDF
プログラムを高速化する話
PDF
明日使えないすごいビット演算
PDF
UE4ディープラーニングってやつでなんとかして!環境構築編(Python3+TensorFlow)
PDF
例外設計における大罪
ヒストリア HelixCore(Perforce) 運用レギュレーションドキュメント
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
UniRxでMV(R)Pパターン をやってみた
Unity 2018-2019を見据えたDeNAのUnity開発のこれから [DeNA TechCon 2019]
20分くらいでわかった気分になれるC++20コルーチン
ゲーム開発者のための C++11/C++14
.NET Core 3.0時代のメモリ管理
Unity開発で使える設計の話+Zenjectの紹介
Unityでパフォーマンスの良いUIを作る為のTips
Observableで非同期処理
目grep入門 +解説
イベント駆動プログラミングとI/O多重化
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
C# 8.0 非同期ストリーム
大規模ゲーム開発における build 高速化と安定化
未来のプログラミング技術をUnityで -UniRx-
プログラムを高速化する話
明日使えないすごいビット演算
UE4ディープラーニングってやつでなんとかして!環境構築編(Python3+TensorFlow)
例外設計における大罪
Ad

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

PDF
パターンでわかる! .NET Coreの非同期処理
PDF
20201127 .NET 5
PPTX
AngulaとElixirの新しい関係
PDF
Hello Dark-Side C# (Part. 1)
PDF
アドテク×Scala×パフォーマンスチューニング
PDF
Scala + Finagleの魅力
PPTX
C#メタプログラミング概略 in 2021
PDF
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
PPTX
STARC RTL設計スタイルガイドによるVerilog HDL並列記述の補強
PDF
Node.jsアプリの開発をモダン化するために取り組んできたこと
PDF
エキ Py 読書会02 2010/9/7
PDF
VSCodeで始めるAzure Static Web Apps開発
PDF
エキ Py 読書会02 2章後半
PDF
【Unity道場京都スペシャル4】Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
PDF
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
PDF
JavaでWebサービスを作り続けるための戦略と戦術 JJUG-CCC-2018-Spring-g1
PDF
仮想通貨取引所 bitbank の IaC の導入と実践
PDF
Pact言語によるセキュアなスマートコントラクト開発
PDF
[Japan Tech summit 2017] DEP 005
PPTX
Starc verilog hdl2013d
パターンでわかる! .NET Coreの非同期処理
20201127 .NET 5
AngulaとElixirの新しい関係
Hello Dark-Side C# (Part. 1)
アドテク×Scala×パフォーマンスチューニング
Scala + Finagleの魅力
C#メタプログラミング概略 in 2021
泥臭い運用から、プログラマブルインフラ構築(に行きたい)
STARC RTL設計スタイルガイドによるVerilog HDL並列記述の補強
Node.jsアプリの開発をモダン化するために取り組んできたこと
エキ Py 読書会02 2010/9/7
VSCodeで始めるAzure Static Web Apps開発
エキ Py 読書会02 2章後半
【Unity道場京都スペシャル4】Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
JavaでWebサービスを作り続けるための戦略と戦術 JJUG-CCC-2018-Spring-g1
仮想通貨取引所 bitbank の IaC の導入と実践
Pact言語によるセキュアなスマートコントラクト開発
[Japan Tech summit 2017] DEP 005
Starc verilog hdl2013d
Ad

最速C# 7.x