Movatterモバイル変換


[0]ホーム

URL:


信之 岩永, profile picture
Uploaded by信之 岩永
PPTX, PDF5,975 views

C# 7.2 with .NET Core 2.1

わんくま同盟 東京勉強会 #110 にて登壇http://www.wankuma.com/seminar/20180324tokyo110/

Embed presentation

Downloaded 15 times
C# 7.2with .NET Core 2.1岩永 信之
今日の話• .NET Core 2.xでのパフォーマンス改善の話• C# 7.x• .NET Core 2.xのパフォーマンス改善の流れに同調• 去年リリースされたけど、.NET Core 2.1が出てからが本番
.NET Core 2.1• https://blogs.msdn.microsoft.com/dotnet/2018/02/27/announcing-net-core-2-1-preview-1/• Global Tools• Build Performance Improvements• Minor-Version Roll-forward• Sockets Performance and HTTP Managed Handler• Span<T>, Memory<T> and friends ←今日の話これ• Windows Compatibility Pack
C# 7.2• http://ufcpp.net/study/csharp/cheatsheet/ap_ver7_2/• 数字区切り• 非末尾名前付き引数• private protected• ref readonly/ref拡張メソッド/ref演算子• ref構造体/Span<T>対応 ←今日の話これ
前振り:C#/.NETのパフォーマンス
ベンチマーク• 英語で「language benchmark」とかでググると…• こんな感じのサイトが
ベンチマーク(注意点)• 英語で「language benchmark」とかでググると…• https://benchmarksgame.alioth.debian.org/• 注意点• 言語をまたいだベンチマークは、言語の差だけじゃなくて「どのくらい本気の人が参加してるか」による• 実際、C#はちょっと前までいまいちなコードでだいぶ遅かった• レギュレーション難しい• C++は環境依存な関数を容赦なく使ってたりすることが
ベンチマーク(現状)おおむねC, C++ > Rust > Java, Swift, C#, Go > 関数型言語 >>> スクリプト言語横並び
ベンチマーク(現状からいえること)C#は….NET Core 2.0でちょっと速くなった• C, C++基準で2~3倍遅い• ここを縮めたいという要望は昔から多い• 特定CPU向け最適化• GC対策• スクリプト言語基準で3~5倍速い• にもかかわらず「Node.jsと比べてそんなに速くない」と思われてる• 文字列処理の速度が大差ない• 言語的な差よりも、内部のHTTP処理とかの方がネックになったりする
要するに、こんな要望になる特定CPU向け最適化Web高速化GC削減UTF-8Hardware Intrinsics参照と構造体活用Span<T>バイナリ処理C# 7.0, 7.1.NET Core 2.1以降.NET Core 2.1
ということで今日のテーマ• Span<T> (.NET Core 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化
• Span<T> (.NET Core 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化 軽い方から先に
例えばこんなリファクタリング(1)• uint比較• 効果• 比較が1回減る• 1命令と言えど、ループで頻繁に呼ばれる処理なので塵も積もるif ((uint)x < length)元 後if (0 < x && x < length)
例えばこんなリファクタリング(2)• Common Path Optimization (よく通る場所だけ優先最適化)• 効果• よく通る場所だけはインライン化が効いて速くなる• (長かったり、throw文があるとインライン化されない)if (9割方true)短い処理else長い処理をメソッド抽出();if (9割方true)短い処理else長い処理元 後
Q & A (1)• Q. 「この手の細かい改善で5%くらい速くなります」といわれて、やる?• A. 5%でもやりたい人という人は「世の中にはいる」• こういうものは、限られた人数では最適化されづらい• コミュニティ貢献が多いらしい• ほんと細かい積み重ねはオープンソース強い
Q & A (2)• Q. いまさら?• A 1. .NET Frameworkからの移行で手一杯だったから…• A 2. ほんと、いまさら• 前述のとおり、こういうのはコミュニティ貢献が結構大きい• 「いまさら」というなら、オープン化のタイミングが…• 方針転換• 最適化はコンパイラーの仕事ではないか → ホットパスは手作業してでも高速化
• Span<T> (.NET Core 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化2.1から外れちゃったんで…おまけ程度に
C# 対 C言語• 遅いのはガベージ コレクション(GC)?• → No• スタックの方が速いけどC#でもスタックを主体にしたコードは書ける• そのための機能も年々増えてる(C# 7の構造体活用)• 根本的に(Cでも)ヒープが必要なら、むしろGCは無茶苦茶速い• じゃあ、あと、何が遅いのか?• → 特定CPU向け最適化(CPU専用命令)• 一番犠牲にしてるのはポータビリティ• 要するに、速くしたければ#ifdefだらけ
CPU専用命令の例• 例: SIMD• single instruction multiple data• Vector命令とか言われたり• 例えばSSE(Pentium IIIの頃に導入された原始的なやつ)ですら• 128ビットレジスターを持ってて• float×4の四則演算を1命令でできる• 単純に考えて個別に4回計算する場合の4倍速い• オーバーヘッドの分を差し引いても倍は速い単一 命令で 複数 データ を一気に処理
Hardware Instrinsics• 専用命令の使い方• 普通のコードを書いて、コンパイラーの最適化に任せる?• 案外、その手のコードを生成してくれない• コンパイラーが特殊な関数を用意• 「この関数呼び出しは、この命令に置き換える」みたいなコンパイルを行う#include <immintrin.h>__m128 c = _mm_mul_ps(a, b)コンパイラーによって提供されるライブラリCPU専用命令に置き換わる
Hardware Instrinsics• 専用命令の使い方• 普通のコードを書いて、コンパイラーの最適化に任せる?• 案外、その手のコードを生成してくれない• コンパイラーが特殊な関数を用意• 「この関数呼び出しは、この命令に置き換える」みたいなコンパイルを行う#include <immintrin.h>__m128 c = _mm_mul_ps(a, b)こういう関数のことをintrinsicsという• Intrinsic: 内在的な• Hardware Intrinsics: 特定のCPUが内在的に持っている機能
C#でHardware Intrinsics (1)• パッケージ参照• .NET Core 2.1 preview 1までは、標準に組み込まれる予定だった• 2.1リリースには間に合わず、現状は• パッケージ参照が必要• 2.1リリース時点では「プレビュー」なパッケージになる予定
C#でHardware Intrinsics (2)• コードの書き方using System.Runtime.Intrinsics;using System.Runtime.Intrinsics.X86;result = Sse.Multiply(Sse.Shuffle(a, a, 0x00), Sse.LoadVector128(presult = Sse.Add(result, Sse.Multiply(Sse.Shuffle(a, a, 0x55), Sseresult = Sse.Add(result, Sse.Multiply(Sse.Shuffle(a, a, 0xaa), Sseresult = Sse.Add(result, Sse.Multiply(Sse.Shuffle(a, a, 0xff), Sse・・・命令セットの名前のクラス命令に対応するメソッド
もちろんCPU依存…• 当然、C言語だと#ifだらけになる• C#でもifだらけになるif (Sse.IsSupported){return SseAdd(a, b);}else{return Add(a, b);}この命令セットを使える環境の時だけtrueJIT時定数(falseの側は最適化で完全に消える)使える場合と使えない場合の2つの実装が必要かなり速くできるけど、「ifだらけ」「2重実装」の覚悟が必要
デモ• 行列の積和でベンチマーク• SIMD命令を使うか使わないかでどのくらい速度差が出るか• https://github.com/ufcpp/UfcppSample/tree/master/Demo/2018/SpanPerformance/MatrixBenchmark
• Span<T> (.NET Core 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化本題。というか今日の話題で2.1の話題なのはこれだけに
C#対スクリプト言語• ベンチマーク上は3~5倍は速いはずなのに• Webサーバーとして使うとなんかそこまで差がつかない?• 3~5倍速いのは主に数値計算とか• 文字列処理の速度が大差ない• 言語的な差よりも、内部のHTTP処理(C#やスクリプト言語ではなく、ネイティブで書かれてる)とかの方がネックになったりする• WebがUTF-8で、C#のstringがUTF-16なのが問題に
Webでの文字列処理の例• 例えばこんな感じのHTTP GETをしたとして• (アップローダーからJSONをダウンロード)• idとnameの値だけを取り出そうとしたとき… ちなみに、こういうJSON
文字列処理の現状• Stream + stringでの処理… 7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7DStream.Read(byte[] buffer, int offset, int count)Encoding.GetString(byte[] bytes)… 22 00 69 00 64 00 22 00 3A 00 20 00 31 00 2C 00 0D 00 0A 00 20 00 20 00 22 00 6E 00 61 00 6D 00 65 00 22 00 3A 00 20 00 …string.Substring string.Substring31 00122 00 3D D8 66 DC 3C D8 FD DF 22 00"👦🏽"UTF-8UTF-16
文字列処理をどうしたいか(1)• Nativeヒープを直接参照… 7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …IPipeReader.TryRead(out ReadResult result)OwnedBuffer参照管理のための小さいクラス
文字列処理をどうしたいか(2)• UTF-8のままで処理… 7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …OwnedBuffer Utf8Stringnew Utf8String(buffer.Span)
文字列処理をどうしたいか(3)• 部分参照でコピーを作らない… 7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …OwnedBuffer Utf8Stringnew Utf8String(buffer.Span)SubstringUtf8StringUtf8String1 "👦🏽"スタックしか使わないSubstringもコピーを作らない直接intにParseすればヒープ不要ToString時に初めてヒープ確保
何が必要か• メモリの一部分を参照• Nativeヒープを直接• stringの一部分• 参照を前提にしたI/O• UTF-8を直接読み書きSpan<T>型PipelinesUtf8String.NET Core 2.1その後依存関係C# 7.0~7.2• ref戻り値• ref安全ルールSpan<T>型PipelinesUtf8String今ここ
Span<T>: 「範囲」を参照• 論理的には、参照+長さ… 7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …1struct Span<T>{ref T Pointer;int Length;}どこから何要素例 ここから1バイト ここから10バイト" "
Span<T>: いろんなところ参照• Managedヒープも、Nativeヒープも、スタックも参照できるSpan<byte> span = new byte[N];Span<byte> span = stackalloc byte[N];var p = Marshal.AllocHGlobal(N);Span<byte> span = new Span<byte>((byte*)p, N);普通の配列 = Managedヒープstackalloc = スタックP/Invokeやunsafe = Nativeヒープ同じ型で参照できる= 同じロジックで扱える
C# 7.2: Span安全ルール• スタック(ローカル変数)の参照を外に返してはいけないref int Ok(ref int x){return ref x;}ref int Ng(){int x;return ref x;}Span<int> Ok(Span<int> x){return x;}Span<int> Ng(){Span<int> x = stackalloc int[1];return x;}ref安全ルール(C# 7.0) Span<T>※安全ルール(C# 7.2)ローカル変数(メソッドを抜けると消える)消えるものの参照(返そうとするとエラー)※ 正確にはSpan<T>専用ではなくて、ref構造体に対して掛かる制限
C# 7.2: 安全なstackalloc• 例: 頻出数字の検索static int MostFrequentDigit(string s){var digits = new int[10];foreach (var c in s){var d = c - '0';if ((uint)d < 10) ++digits[d];}var max = 0;for (int i = 1; i < 10; i++)if (digits[max] < digits[i]) max = i;return max;}0~9の文字の頻度をカウントするための一時バッファー配列のせいでヒープ確保(あんまり好ましくない)
C# 7.2: 安全なstackalloc• 例: 頻出数字の検索static int MostFrequentDigit(string s){Span<int> digits = stackalloc int[10];foreach (var c in s){var d = c - '0';if ((uint)d < 10) ++digits[d];}var max = 0;for (int i = 1; i < 10; i++)if (digits[max] < digits[i]) max = i;return max;}一時バッファーをnewからstackallocに変更ヒープ確保がなくなる(速い)unsafe不要実際、安全• 範囲チェックあり• 外に返せないルールあり
デモ• Substring• ヒープ確保をなくせば無条件に速くなるというものでもない• GCはほんとに速い• Span<T>があるとだいぶ速い• https://github.com/ufcpp/UfcppSample/tree/master/Demo/2018/SpanPerformance/SubstringBenchmark
デモ• Split → Join• string.Splitやstring.Joinは一時的なオブジェクトを確保しまくる• Span<T>でヒープ確保なくSplit, Join• (ただし、使い勝手はあまり良くない)• https://github.com/ufcpp/UfcppSample/tree/master/Demo/2018/SpanPerformance/StringManipulation
まとめ• 2.0~2.1、細かい最適化だらけ• 2.0で実際あったのをちょっと紹介• Hardware Intrinsics• 2.1から外れちゃったけど• 2.1時点で、プレビュー公開• Span/UTF-8• GC速いけど、「ヒープなし」にはさすがに敵わない• そのためのref、Span<T>• C# 7.2で関連機能すでに入ってる/.NET Core 2.1からが本番

Recommended

PPTX
C# 8.0 Preview in Visual Studio 2019 (16.0)
PPTX
C# 8.0 非同期ストリーム
PPTX
C# 9.0 / .NET 5.0
PPTX
C#や.NET Frameworkがやっていること
PDF
今日からできる!簡単 .NET 高速化 Tips
PDF
Inside FastEnum
PPTX
.NET Core 2.x 時代の C#
PDF
Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京
 
PDF
Hello Dark-Side C# (Part. 1)
PPTX
最速C# 7.x
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
KEY
Objc lambda
PDF
SEH on mingw32
PPTX
プログラミング .NET Framework 第4版
PDF
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
PDF
知って得するC#
PPTX
async/await のしくみ
PDF
Boost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
PPTX
C#とILとネイティブと
PDF
動的なILの生成と編集
PPTX
C#/.NETがやっていること 第二版
PPTX
Orange Cube 自社フレームワーク 2015/3
PPTX
C#言語機能の作り方
PDF
基礎からのCode Contracts
PDF
C++でCプリプロセッサを作ったり速くしたりしたお話
PPTX
Deep Dive C# 6.0
PDF
今からでも遅くないC#開発
PPTX
Sharing Deep Dive
PDF
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
PPTX
C# design note sep 2014

More Related Content

PPTX
C# 8.0 Preview in Visual Studio 2019 (16.0)
PPTX
C# 8.0 非同期ストリーム
PPTX
C# 9.0 / .NET 5.0
PPTX
C#や.NET Frameworkがやっていること
PDF
今日からできる!簡単 .NET 高速化 Tips
PDF
Inside FastEnum
PPTX
.NET Core 2.x 時代の C#
PDF
Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 非同期ストリーム
C# 9.0 / .NET 5.0
C#や.NET Frameworkがやっていること
今日からできる!簡単 .NET 高速化 Tips
Inside FastEnum
.NET Core 2.x 時代の C#
Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京
 

What's hot

PDF
Hello Dark-Side C# (Part. 1)
PPTX
最速C# 7.x
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
KEY
Objc lambda
PDF
SEH on mingw32
PPTX
プログラミング .NET Framework 第4版
PDF
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
PDF
知って得するC#
PPTX
async/await のしくみ
PDF
Boost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
PPTX
C#とILとネイティブと
PDF
動的なILの生成と編集
PPTX
C#/.NETがやっていること 第二版
PPTX
Orange Cube 自社フレームワーク 2015/3
PPTX
C#言語機能の作り方
PDF
基礎からのCode Contracts
PDF
C++でCプリプロセッサを作ったり速くしたりしたお話
PPTX
Deep Dive C# 6.0
PDF
今からでも遅くないC#開発
PPTX
Sharing Deep Dive
Hello Dark-Side C# (Part. 1)
最速C# 7.x
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Objc lambda
SEH on mingw32
プログラミング .NET Framework 第4版
Observable Everywhere - Rxの原則とUniRxにみるデータソースの見つけ方
知って得するC#
async/await のしくみ
Boost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
C#とILとネイティブと
動的なILの生成と編集
C#/.NETがやっていること 第二版
Orange Cube 自社フレームワーク 2015/3
C#言語機能の作り方
基礎からのCode Contracts
C++でCプリプロセッサを作ったり速くしたりしたお話
Deep Dive C# 6.0
今からでも遅くないC#開発
Sharing Deep Dive

Similar to C# 7.2 with .NET Core 2.1

PDF
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
PPTX
C# design note sep 2014
PDF
C#の新機能勉強会 ~ C#7、8の新機能を活用して速く安全なプログラムを書こう~
PDF
C#勉強会 ~ C#9の新機能 ~
PPTX
C# 8
PDF
.NET Core 3.0時代のメモリ管理
PPTX
.NET Compiler Platform
PDF
CLRの基礎 - プログラミング .NET Framework 第3版 読書会
PPTX
(ゲームじゃない方の)switchで遊びたい話
PPTX
ネイティブコードを語る
PDF
C#勉強会
PDF
New Features in C# 10/11
PDF
Unity2015_No10_~UGUI&Audio~
 
PPTX
Live Coding で学ぶ C# 7
PDF
これからの「言語」の話をしよう ―― 未来を生きるためのツール
PDF
20141129-dotNet2015
PDF
プログラミングNet framework3のお題
PPTX
C#6.0の新機能紹介
PPTX
C# 7・8 の復習
PPTX
C# 7 Current Status
[TL06] 日本の第一人者が C# の現状と今後を徹底解説! 「この素晴らしい C# に祝福を!」
C# design note sep 2014
C#の新機能勉強会 ~ C#7、8の新機能を活用して速く安全なプログラムを書こう~
C#勉強会 ~ C#9の新機能 ~
C# 8
.NET Core 3.0時代のメモリ管理
.NET Compiler Platform
CLRの基礎 - プログラミング .NET Framework 第3版 読書会
(ゲームじゃない方の)switchで遊びたい話
ネイティブコードを語る
C#勉強会
New Features in C# 10/11
Unity2015_No10_~UGUI&Audio~
 
Live Coding で学ぶ C# 7
これからの「言語」の話をしよう ―― 未来を生きるためのツール
20141129-dotNet2015
プログラミングNet framework3のお題
C#6.0の新機能紹介
C# 7・8 の復習
C# 7 Current Status

More from 信之 岩永

PPTX
広がる .Net
PPTX
Unityで使える C# 6.0~と .NET 4.6
PPTX
Unicode文字列処理
PPTX
今から始める、Windows 10&新.NETへの移行戦略
PPTX
今から始める、Windows 10&新.NETへの移行戦略
PPTX
非同期処理の基礎
PPTX
Coding Interview
PPTX
C# コンパイラーの書き換え作業の話
PPTX
C# 8.0 null許容参照型
PPTX
C#マスコット(公開用)
PPTX
Modern .NET
PPTX
.NET vNext
PPTX
Code Contracts in .NET 4
PPTX
Anders Hejlsberg Q & A
PPTX
それっぽく、適当に
PPTX
YouTube ライブ配信するようになった話
広がる .Net
Unityで使える C# 6.0~と .NET 4.6
Unicode文字列処理
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略
非同期処理の基礎
Coding Interview
C# コンパイラーの書き換え作業の話
C# 8.0 null許容参照型
C#マスコット(公開用)
Modern .NET
.NET vNext
Code Contracts in .NET 4
Anders Hejlsberg Q & A
それっぽく、適当に
YouTube ライブ配信するようになった話

C# 7.2 with .NET Core 2.1

  • 1.
    C# 7.2with .NETCore 2.1岩永 信之
  • 2.
    今日の話• .NET Core2.xでのパフォーマンス改善の話• C# 7.x• .NET Core 2.xのパフォーマンス改善の流れに同調• 去年リリースされたけど、.NET Core 2.1が出てからが本番
  • 3.
    .NET Core 2.1•https://blogs.msdn.microsoft.com/dotnet/2018/02/27/announcing-net-core-2-1-preview-1/• Global Tools• Build Performance Improvements• Minor-Version Roll-forward• Sockets Performance and HTTP Managed Handler• Span<T>, Memory<T> and friends ←今日の話これ• Windows Compatibility Pack
  • 4.
    C# 7.2• http://ufcpp.net/study/csharp/cheatsheet/ap_ver7_2/•数字区切り• 非末尾名前付き引数• private protected• ref readonly/ref拡張メソッド/ref演算子• ref構造体/Span<T>対応 ←今日の話これ
  • 5.
  • 6.
  • 7.
    ベンチマーク(注意点)• 英語で「language benchmark」とかでググると…•https://benchmarksgame.alioth.debian.org/• 注意点• 言語をまたいだベンチマークは、言語の差だけじゃなくて「どのくらい本気の人が参加してるか」による• 実際、C#はちょっと前までいまいちなコードでだいぶ遅かった• レギュレーション難しい• C++は環境依存な関数を容赦なく使ってたりすることが
  • 8.
    ベンチマーク(現状)おおむねC, C++ >Rust > Java, Swift, C#, Go > 関数型言語 >>> スクリプト言語横並び
  • 9.
    ベンチマーク(現状からいえること)C#は….NET Core 2.0でちょっと速くなった•C, C++基準で2~3倍遅い• ここを縮めたいという要望は昔から多い• 特定CPU向け最適化• GC対策• スクリプト言語基準で3~5倍速い• にもかかわらず「Node.jsと比べてそんなに速くない」と思われてる• 文字列処理の速度が大差ない• 言語的な差よりも、内部のHTTP処理とかの方がネックになったりする
  • 10.
  • 11.
    ということで今日のテーマ• Span<T> (.NETCore 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化
  • 12.
    • Span<T> (.NETCore 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化 軽い方から先に
  • 13.
    例えばこんなリファクタリング(1)• uint比較• 効果•比較が1回減る• 1命令と言えど、ループで頻繁に呼ばれる処理なので塵も積もるif ((uint)x < length)元 後if (0 < x && x < length)
  • 14.
    例えばこんなリファクタリング(2)• Common PathOptimization (よく通る場所だけ優先最適化)• 効果• よく通る場所だけはインライン化が効いて速くなる• (長かったり、throw文があるとインライン化されない)if (9割方true)短い処理else長い処理をメソッド抽出();if (9割方true)短い処理else長い処理元 後
  • 15.
    Q & A(1)• Q. 「この手の細かい改善で5%くらい速くなります」といわれて、やる?• A. 5%でもやりたい人という人は「世の中にはいる」• こういうものは、限られた人数では最適化されづらい• コミュニティ貢献が多いらしい• ほんと細かい積み重ねはオープンソース強い
  • 16.
    Q & A(2)• Q. いまさら?• A 1. .NET Frameworkからの移行で手一杯だったから…• A 2. ほんと、いまさら• 前述のとおり、こういうのはコミュニティ貢献が結構大きい• 「いまさら」というなら、オープン化のタイミングが…• 方針転換• 最適化はコンパイラーの仕事ではないか → ホットパスは手作業してでも高速化
  • 17.
    • Span<T> (.NETCore 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化2.1から外れちゃったんで…おまけ程度に
  • 18.
    C# 対 C言語•遅いのはガベージ コレクション(GC)?• → No• スタックの方が速いけどC#でもスタックを主体にしたコードは書ける• そのための機能も年々増えてる(C# 7の構造体活用)• 根本的に(Cでも)ヒープが必要なら、むしろGCは無茶苦茶速い• じゃあ、あと、何が遅いのか?• → 特定CPU向け最適化(CPU専用命令)• 一番犠牲にしてるのはポータビリティ• 要するに、速くしたければ#ifdefだらけ
  • 19.
    CPU専用命令の例• 例: SIMD•single instruction multiple data• Vector命令とか言われたり• 例えばSSE(Pentium IIIの頃に導入された原始的なやつ)ですら• 128ビットレジスターを持ってて• float×4の四則演算を1命令でできる• 単純に考えて個別に4回計算する場合の4倍速い• オーバーヘッドの分を差し引いても倍は速い単一 命令で 複数 データ を一気に処理
  • 20.
    Hardware Instrinsics• 専用命令の使い方•普通のコードを書いて、コンパイラーの最適化に任せる?• 案外、その手のコードを生成してくれない• コンパイラーが特殊な関数を用意• 「この関数呼び出しは、この命令に置き換える」みたいなコンパイルを行う#include <immintrin.h>__m128 c = _mm_mul_ps(a, b)コンパイラーによって提供されるライブラリCPU専用命令に置き換わる
  • 21.
    Hardware Instrinsics• 専用命令の使い方•普通のコードを書いて、コンパイラーの最適化に任せる?• 案外、その手のコードを生成してくれない• コンパイラーが特殊な関数を用意• 「この関数呼び出しは、この命令に置き換える」みたいなコンパイルを行う#include <immintrin.h>__m128 c = _mm_mul_ps(a, b)こういう関数のことをintrinsicsという• Intrinsic: 内在的な• Hardware Intrinsics: 特定のCPUが内在的に持っている機能
  • 22.
    C#でHardware Intrinsics (1)•パッケージ参照• .NET Core 2.1 preview 1までは、標準に組み込まれる予定だった• 2.1リリースには間に合わず、現状は• パッケージ参照が必要• 2.1リリース時点では「プレビュー」なパッケージになる予定
  • 23.
    C#でHardware Intrinsics (2)•コードの書き方using System.Runtime.Intrinsics;using System.Runtime.Intrinsics.X86;result = Sse.Multiply(Sse.Shuffle(a, a, 0x00), Sse.LoadVector128(presult = Sse.Add(result, Sse.Multiply(Sse.Shuffle(a, a, 0x55), Sseresult = Sse.Add(result, Sse.Multiply(Sse.Shuffle(a, a, 0xaa), Sseresult = Sse.Add(result, Sse.Multiply(Sse.Shuffle(a, a, 0xff), Sse・・・命令セットの名前のクラス命令に対応するメソッド
  • 24.
    もちろんCPU依存…• 当然、C言語だと#ifだらけになる• C#でもifだらけになるif(Sse.IsSupported){return SseAdd(a, b);}else{return Add(a, b);}この命令セットを使える環境の時だけtrueJIT時定数(falseの側は最適化で完全に消える)使える場合と使えない場合の2つの実装が必要かなり速くできるけど、「ifだらけ」「2重実装」の覚悟が必要
  • 25.
    デモ• 行列の積和でベンチマーク• SIMD命令を使うか使わないかでどのくらい速度差が出るか•https://github.com/ufcpp/UfcppSample/tree/master/Demo/2018/SpanPerformance/MatrixBenchmark
  • 26.
    • Span<T> (.NETCore 2.1)• Hardware Intrinsics (.NET Core 2.1より後)• おまけ: .NET Core 2.0での細かい最適化本題。というか今日の話題で2.1の話題なのはこれだけに
  • 27.
    C#対スクリプト言語• ベンチマーク上は3~5倍は速いはずなのに• Webサーバーとして使うとなんかそこまで差がつかない?•3~5倍速いのは主に数値計算とか• 文字列処理の速度が大差ない• 言語的な差よりも、内部のHTTP処理(C#やスクリプト言語ではなく、ネイティブで書かれてる)とかの方がネックになったりする• WebがUTF-8で、C#のstringがUTF-16なのが問題に
  • 28.
    Webでの文字列処理の例• 例えばこんな感じのHTTP GETをしたとして•(アップローダーからJSONをダウンロード)• idとnameの値だけを取り出そうとしたとき… ちなみに、こういうJSON
  • 29.
    文字列処理の現状• Stream +stringでの処理… 7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7DStream.Read(byte[] buffer, int offset, int count)Encoding.GetString(byte[] bytes)… 22 00 69 00 64 00 22 00 3A 00 20 00 31 00 2C 00 0D 00 0A 00 20 00 20 00 22 00 6E 00 61 00 6D 00 65 00 22 00 3A 00 20 00 …string.Substring string.Substring31 00122 00 3D D8 66 DC 3C D8 FD DF 22 00"👦🏽"UTF-8UTF-16
  • 30.
    文字列処理をどうしたいか(1)• Nativeヒープを直接参照… 7B0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …IPipeReader.TryRead(out ReadResult result)OwnedBuffer参照管理のための小さいクラス
  • 31.
    文字列処理をどうしたいか(2)• UTF-8のままで処理… 7B0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …OwnedBuffer Utf8Stringnew Utf8String(buffer.Span)
  • 32.
    文字列処理をどうしたいか(3)• 部分参照でコピーを作らない… 7B0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …OwnedBuffer Utf8Stringnew Utf8String(buffer.Span)SubstringUtf8StringUtf8String1 "👦🏽"スタックしか使わないSubstringもコピーを作らない直接intにParseすればヒープ不要ToString時に初めてヒープ確保
  • 33.
    何が必要か• メモリの一部分を参照• Nativeヒープを直接•stringの一部分• 参照を前提にしたI/O• UTF-8を直接読み書きSpan<T>型PipelinesUtf8String.NET Core 2.1その後依存関係C# 7.0~7.2• ref戻り値• ref安全ルールSpan<T>型PipelinesUtf8String今ここ
  • 34.
    Span<T>: 「範囲」を参照• 論理的には、参照+長さ…7B 0D 0A 20 20 22 69 64 22 3A 20 31 2C 0D 0A 20 20 22 6E 61 6D 65 22 3A 20 22 F0 9F 91 A6 F0 9F 8F BD 22 0D 0A 7D …1struct Span<T>{ref T Pointer;int Length;}どこから何要素例 ここから1バイト ここから10バイト" "
  • 35.
    Span<T>: いろんなところ参照• Managedヒープも、Nativeヒープも、スタックも参照できるSpan<byte>span = new byte[N];Span<byte> span = stackalloc byte[N];var p = Marshal.AllocHGlobal(N);Span<byte> span = new Span<byte>((byte*)p, N);普通の配列 = Managedヒープstackalloc = スタックP/Invokeやunsafe = Nativeヒープ同じ型で参照できる= 同じロジックで扱える
  • 36.
    C# 7.2: Span安全ルール•スタック(ローカル変数)の参照を外に返してはいけないref int Ok(ref int x){return ref x;}ref int Ng(){int x;return ref x;}Span<int> Ok(Span<int> x){return x;}Span<int> Ng(){Span<int> x = stackalloc int[1];return x;}ref安全ルール(C# 7.0) Span<T>※安全ルール(C# 7.2)ローカル変数(メソッドを抜けると消える)消えるものの参照(返そうとするとエラー)※ 正確にはSpan<T>専用ではなくて、ref構造体に対して掛かる制限
  • 37.
    C# 7.2: 安全なstackalloc•例: 頻出数字の検索static int MostFrequentDigit(string s){var digits = new int[10];foreach (var c in s){var d = c - '0';if ((uint)d < 10) ++digits[d];}var max = 0;for (int i = 1; i < 10; i++)if (digits[max] < digits[i]) max = i;return max;}0~9の文字の頻度をカウントするための一時バッファー配列のせいでヒープ確保(あんまり好ましくない)
  • 38.
    C# 7.2: 安全なstackalloc•例: 頻出数字の検索static int MostFrequentDigit(string s){Span<int> digits = stackalloc int[10];foreach (var c in s){var d = c - '0';if ((uint)d < 10) ++digits[d];}var max = 0;for (int i = 1; i < 10; i++)if (digits[max] < digits[i]) max = i;return max;}一時バッファーをnewからstackallocに変更ヒープ確保がなくなる(速い)unsafe不要実際、安全• 範囲チェックあり• 外に返せないルールあり
  • 39.
    デモ• Substring• ヒープ確保をなくせば無条件に速くなるというものでもない•GCはほんとに速い• Span<T>があるとだいぶ速い• https://github.com/ufcpp/UfcppSample/tree/master/Demo/2018/SpanPerformance/SubstringBenchmark
  • 40.
    デモ• Split →Join• string.Splitやstring.Joinは一時的なオブジェクトを確保しまくる• Span<T>でヒープ確保なくSplit, Join• (ただし、使い勝手はあまり良くない)• https://github.com/ufcpp/UfcppSample/tree/master/Demo/2018/SpanPerformance/StringManipulation
  • 41.
    まとめ• 2.0~2.1、細かい最適化だらけ• 2.0で実際あったのをちょっと紹介•Hardware Intrinsics• 2.1から外れちゃったけど• 2.1時点で、プレビュー公開• Span/UTF-8• GC速いけど、「ヒープなし」にはさすがに敵わない• そのためのref、Span<T>• C# 7.2で関連機能すでに入ってる/.NET Core 2.1からが本番

Editor's Notes

  • #8 最近日本でも「並列処理の言語比較」で見事に炎上したやつがいたけども。まあ、言語比較とか手を出すの危険。
  • #10 数値計算処理とかではほんとスクリプト言語は遅い
  • #23 「おっ、2.1に入ってんじゃーん」とか思って喜んでスライド作ってたら、「間に合わない。一度外す」って話になった…
  • #29 適当なアップローダーに上げたJSONファイルをダウンロードした結果をfiddlerで覗いたもの

[8]ページ先頭

©2009-2025 Movatter.jp