ベクトルプログラミング入門を読んでunsafeの沼に足を踏み入れかけたが _unsafeはUnsafeでセーフ_

3.5K Views

February 01, 24

スライド概要

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

ベクトルプログラミング入 門を読んでunsafeの沼に 足を踏み入れかけたが unsafeはUnsafeでセーフ 松井 敏(binnmti)

2.

よーわからん  今回勉強会にあたって何を話すか悩ん だが、ちょうど直近読書会で読み始め た本の内容が自分が知らない分野で理 解度も低かったため、敢えてこの内容 で発表することに。あんま分かってな いけれどこれを機に学んだので是非 ツッコミを 2

3.

ベクトルプログラミング入門はどんな 本? ベクトルプログラミング入門は SIMD(Single Instruction/Multiple Data)をC#で使う ための方法が書いてある本  System.Runtime.Intrinsics(イントリンシック)の命 令、Avxなどを使って1回の命令で256ビットを処理 するプログラムが書ける  float(32ビット)型の配列なら256/32で8個まとめて 扱える。流石に常に8倍速くなる…わけではないが、 2倍から状況によっては8倍以上速くなることもあ る  3

4.

unsafe  System.Runtime.Intrinsicsの命令を扱う際 にunsafe、fixed、stackallocなどが出てく る  自分はC#歴はそこそこあるが、書いたこ とのないキーワードは未だにある。その 一つがunsafe  正直業務でパフォーマンスが理由で unsafeを使ったことはない 4

5.

そもそも そもそもunsafeを使いたい理由っ て何だっけ? unsafe=ポインタを使いたいからみ たいな印象 Learnにもアンセーフコードとポイ ンタという内容がある 5

6.

ポインタは速い?  でも考えてみるとポインタだから速い というわけではない  ただの受け方の問題なのでコピーが発 生しない参照で受けても速度劣化はな いはず。。  余談:最近C++でもポインタはめっき り書かなくなった 6

7.

fixed そう考えるとポインタはunsafeを 使いたい理由とはならないはず。 ではfixedは?ポインタを使う時は fixedで受けている  byte[] bytes = [1, 2, 3];  fixed (byte* pointerToFirst = bytes) 7

8.

fixedステートメント  fixed ステートメントを使うと、ガベージ コレク ターによる移動可能変数の再配置を防ぎ、その変 数へのポインターを宣言することができます。 固 定 (またはピン留め) された変数のアドレスは、そ のステートメントの実行中に変わりません。 宣言 されたポインターは、対応する fixed ステートメ ント内でのみ使用できます。 宣言されたポイン ターは読み取り専用であり、変更できません。 8

9.

ガベージコレクション 色々書いてあるけれど、ポイント はGCが避けられること。++C++に もGCを避けることが速度最適化の コツと書いてある 9

10.

GCを避ける つまりunsafeを使いたい理由が速 度向上ならGCを避けることが基本 GCを避ける方法の1つがfixedを 使ってピン止めすること もう1つGCを避ける方法があって stackallocを使うこと 10

11.

stackalloc  stackallocはスタックにメモリを割り 当てられる。スタックに割り当てられ たメモリは、そのメソッドが戻るとき に自動的に破棄される  つまり参照型であってもヒープではな く、スタックからメモリを確保すれば GCは避けられる 11

12.

Span<T>  で、stackalloc。これはC#7.2から Span<T>構造体と併用すればunsafeを 使わなくても書けるようになった。  因みに本ではstackallocだけを使う所 もポインタで受けているのでunsafeは 使っている 12

13.

Span<T> Span<T>を使うべき5つの理由がとても詳しい。  Span<T>は低レベルプログラミングのために存在す る  パフォーマンスが良くアンマネージドヒープにも使 える  unsafeがいらず、読み取り専用のReadOnlySpan<T> がある。  List<T>でいいと思ったら使う必要はない。  13

14.

C++ 因みにC++でも20から同じ用途の spanが追加されていた alloca(標準ではない)。。でス タックからメモリ確保も可能 ただGCがないケースでどれぐらい パフォーマンス有利があるのか。。 14

15.

ref struct  Span<T>構造体の宣言を見るとref struct がある。これがミソ。  ref structの特徴はインスタンスをスタッ ク上にしか置けない  ref構造体をフィールドとして持てるのは ref構造体だけ  readonly ref structと書くことで読み取り 専用にできる 15

16.

Quiz ちょっとしたクイズ!var の型はど うなる? var i = stackalloc int[4]; var i = (type == a) ? stackalloc int[4] : new int[4]; var i = (stackalloc int[4]); 16

17.

計測 さらにC#はunsafeの方が速いという幻想にあるが、 unsafeにしなくても、低レベルの操作をする方法 がある。実際に計測してみたが、メモリアクセス にMarshalを使ったものが今も最速  この例はnewが20MB以上だったのでstackallocにし たらオーバーフローだったが1MBぐらいにしたら newよりも確実に速かった。[SkipLocalsInit]を使っ たらさらに少し速くなった  17

18.

unsafeを使わずSIMD 本にこそunsafeを書かないコードは乗ってないが、.NET 7 こそがC# SIMDプログラミングを始めるのに最適である理 由が特に詳しい。  unsafeを書かずに以下を駆使して書いている ReadOnlySpan<byte>,MemoryMarshal,Unsafe(AddByteOffse t),Vector256(IsHardwareAccelerated,LoadUnsafe, StoreUn safe)  Learnにも「NET 7 ではクロスプラットフォーム ハードウ ェアの組み込みが追加されました」と.NET 8のところに書 いてあるが、公式にこの辺のことが書いてある文章は見つ けられてない。。  18

19.

C++  .NET8から512ビット扱えるVector512<T> も使えるようになった。同じ書き方でさ らに倍に出来るかも?  半精度浮動小数点数(Half)で512ビットな ら32回動作出来る  ベクトルプログラミングの作者が書いた C#ではなさそうだが、512ビット専用の 本もある。 19

20.

まとめ  C#でunsafeを使いたい理由はパフォーマンス が大きな理由  GCを避けるにはstackallocを使ってspan<T>構 造体で受ければunsafeは必要ない。実際 stackalloc速い。  unsafeを使わなくてもMarshalクラスや Unsafeクラスは使えば速く出来る  という感じでunsafeは今日も書かず 20

21.

参考:引用  書籍  ベクトルプログラミング入門  512ビット・ベクトルプログラミング入門  Learn  アンセーフ コード、ポインター  fixed ステートメント  ++C++  unsafe  Span構造体  blog  Span<T>を使うべき5つの理由  .NET 7こそがC# SIMDプログラミングを始めるのに最適である理由  C#はunsafeの方が速いという幻想 21