aokomoriuta's blog

青子守歌のブログ

株式会社はてなに入社しました

株式会社はてなに入社しました

株式会社はてなに入社しました - hitode909の日記

C#はunsafeの方が速いという幻想

数日前に話題になったこの辺の話。

espresso3389.hatenablog.com
qiita.com

C++よりC#が速いかどうかというのはとりあえず置いておきましょう。
しかし、大元ネタが「unsafe使うと1.2倍速くなります!」と言ってますね。

よく聞きますよ、「C#で速度出したかったらunsafeにしなさい」って。
しかし本当にそうなのでしょうか?その謎を解明するため、我々探検隊はジャングルの奥地へ(ry

なおこの記事のタイトルは以前の『OpenCLやる前にSIMD使い切れっていう幻想』と合わせています。興味がある方はそちらもどうぞ。

結論

unsafeにしなくても速くできる!!

f:id:aokomoriuta:20160505130347j:plain

コードと環境は以下の通り。

連休中で出先でやってるのでマシンスペックが低いのはご容赦ください(誰かまともなやつでやってみた結果ください・・・)

ご覧のとおり、unsafeを使ったアンマネージドなtest2より、マネージドの世界のまま書いたtestManagedBitmapの方が1.5倍ぐらい速いです。
もちろん、速いコードを書くにはそれなりの知識と技術が必要で、誰でもどんな風に書いてもマネージドで速くなるわけではありません(だから仕事になるんですが)。
「誰でもぱっと乗せたらぱっと速い」というのはとても大事です(だからGPGPUが流行りました)。でもunsafeは「ぱっと速くする」とお手軽さを出すにはリスクが大きすぎます。せっかく安全で手軽なマネージドの世界で閉じていた部分に無理やりアンマネージドを許可するというのは、とても危険ですし(だからunsafeって名前なんです)、気にすべきことが増えすぎます。
それなら、同じ高速化したいのなら、マネージドの世界で閉じたまま、どうやったら速くできるのかを追求するほうが安全だし健全だし貴重な技術になります。
今回みたいにマネージドで書いたほうが実は速くなるならなおさらです。

「とてつもなく速くなる」とか「どうしても互換性のために仕方なく」という特殊な事情がないなら、やはりunsafeは使う必要はないし使うべきではないですね。

続きを読む

株式会社はてなに入社しました

株式会社はてなに入社しました

株式会社はてなに入社しました - hitode909の日記

(ご報告遅くなって申し訳ありませんでした・・・)

MPS法における圧力ポアソン方程式の境界条件

MPS法やISPH法では圧力のポアソン方程式
\displaystyle \nabla^2 p = S(ここでSは生成項)
が出てくるのですが、色々なところの「MPS法実装してみました!」記事を見てみるとこのポアソン方程式の特に境界条件の扱い方が間違っているのが散見される(&よく「なんか上手く動かないんだけどどうしたらいい?」と聞かれる)ので、まとめておきます(別に新規性のあることじゃないし)。

続きを読む

Re: Re: OpenCLやる前にSIMD使い切れっていう幻想

d.hatena.ne.jp

なんだか知らない間にぜんっぜん関係ないtweetを拾われて勝手に憤られてた上に、全然まともに計算していないもの*1を出されてたので、ちゃんと動くようにして測りなおしました。

計算機は前回と同じものを使いますが、手元からVS2013がなくなってVS2015になってしまったので、Visual Studioのバージョンだけ違います(System.Numerics.Vectorsが必要ですが、4.1.0を使ってます)。
結果は以下の通り。

  • C#(なにもしない)
    • 5.5秒ぐらい
  • C#(System.Numerics.Vector<double>)
    • 4.5秒ぐらい

ということで、1秒ぐらい縮まりました。

FX-8350だとSIMD幅が128bitと判定されてSystem.Numerics.Vector<double>.Countが2になっていました(※先の記事の通りAVX2を使えば256bitできます)。
なので、2倍弱ぐらいは期待したのですが、結果は1.2倍で、ちょっと微妙な感じです。
関数呼び出しのコストがこんなに取られてるのか・・・よく分かりません。

私は最近まともなC#を書けてないので、まともなC#erさんに解明を期待します。

追記 (09/21 11:30)

すいません、昨日眠い状態でコード書いてたので、最後にコミットせずにpushして安心して寝てしまってました・・・。多分昨日の時点ではSystem.Numerics.Vector<double>を使っても全然速くないどころか遅かったと思います。
正しいのはさっきコミットしました。これで、4.5秒ぐらい(x1.2ぐらい)です。

追記 (09/21 12:30)

構造体もちゃんとサイズを切り分けたら、速くなりました。

  • C#(なにもしない)
    • 5.5秒ぐらい
  • C#(System.Numerics.Vector<double>)
    • 2.5秒ぐらい

なぜか2倍以上出ている・・・?まぁとりあえず速くなったので良しとします。
多分これが一番早いと思います(フラグ)。

追記 (09/21 23:00)

という話を聞いて、確かに手元のSurface Pro 3(OS:Win10、CPU:i3 4020Y)で試したところ、256bit SIMDになってくれました。

  • C#(なにもしない)
    • 5.5秒ぐらい
  • C#(System.Numerics.Vector<double>)
    • 2.2秒ぐらい

CPUが違うので単純比較できませんが・・・。
実行可能ファイルも置いておくので、もしお時間のある方は、OSとCPUの情報を添えて、実行時間とSIMD長を教えてくれると嬉しいです。

追記 (10/17 15:00)

↓のコメントにある通りAkio Takahashiさんから[https://bitbucket.org/aokomoriuta/simd/pull-requests/1:title=pull request」もらってました。
全部は取り込みませんでしたが、一部を取り込んだところ、以下のようになりました。

  • C#(なにもしない)
    • 5.5秒ぐらい
  • C#(System.Numerics.Vector<double>)
    • 2.5秒ぐらい
  • C#(今回)
    • 1.3秒ぐらい

以前の記事C++SIMD(AVX2)が1.3秒ぐらいだったので、同等ぐらいには速くなったようです。
ちょっと使い方に工夫が要るのかもしれませんが、C++と同じぐらいの性能がでるのであれば、C#には

  • アライン考えなくていい
  • CPUに合わせて勝手にSIMD長変えてくれる(逆にこれは欠点でもある?)
  • 最初から演算子オーバーロードされてるのでintrinsic書かなくていい

という長所が3点あります。

積極的に使えるタイミングでは使っていきたいところですね。

*1:4倍近く加速されたとのことでしたが、System.Numerics.Vector<T>が何かをちゃんと理解できておらず、System.Numerics.Vector.Countを一切考慮できていない、結果的に半分しか計算できていない粗末なものだった(例えばxSimd[0][2]とかアクセスしたら、SIMD長が足りなくて例外吐いて死ぬ)

Radeon Fury Xを使ってみた

@Tommy6AMD Radeon R9 Fury Xを貸してくれたので、例のまたaokomoriuta.hateblo.jp
を使って測ってみました。

ソースコード前と同じ場所にあります。普通にmakeすれば動くはずです。

結果、

Fury X (Fiji)

Normal: 44979[ms]
Simd  : 38869[ms]
OpenMP: 8threads
Normal: 12062[ms]
Simd  : 10243[ms]
OpenCL: 730[ms]

290X (Hawaii)

Normal: 70199[ms]
Simd  : 61474[ms]
OpenMP: 8threads
Normal: 19010[ms]
Simd  : 16672[ms]
OpenCL: 743[ms]


FuryXの方が10[ms]ですが速いですね!!
・・・誤差な気がする。
まぁ最適化もなにもしてないからなぁという気はしますが。

どっちかというと、Sandy-Bridge(2600K)よりIvy-Biridge(3770K)が1.5倍ぐらい速いのが気になるんですが・・・ほんとにこんなもんですかね?

AMD APP SDKのOpenCLをCPUで動かしたらOpenMPより倍速い?

以前のaokomoriuta.hateblo.jp

をちょっと修正して
CPUで動かすようにしたので、手元にあった環境で動かしてみました。

$cd opencl_etc/cpu
$make
$./cpu
= OpenCL on CPU =
Normal: 7785[ms]
OpenMP: 8threads
Normal: 2124[ms]
OpenCL: 1413[ms]
== Platform : 0x7fde975d4670 ==
Name    : AMD Accelerated Parallel Processing
Vendor  : Advanced Micro Devices, Inc.
Version : OpenCL 2.0 AMD-APP (1598.5)
== Device : 0x1642b10 ==
Name                                             : Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz
Vendor                                           : GenuineIntel (ID:4098)
Version                                          : OpenCL 1.2 AMD-APP (1598.5)
Driver version                                   : 1598.5 (sse2,avx)
OpenCL C version                                 : OpenCL C 1.2

OpenMPより倍速いだって・・・!?
なんか信じられないのでもうちょっと調べてみるけどあり得るのか・・・。

6/7追記

gccOpenMPが裏でpthreadを使っていて、OpenCLはTBBを使っていて、つまりpthreadよりTBBが速いのでは?」と言われたので、純AMD環境でVisualStudioでやってみました。

  • OS: Windows7 Professional
  • CPU: AMD FX 8350
  • AMD APP SDK: 3.0 Beta
  • VisualStudio: 2013
= OpenCL on CPU =
Normal: 13274[ms]
OpenMP: 8threads
Normal: 2372[ms]
OpenCL: 859[ms]
== Platform : 000007FED1817B60 ==
Name    : AMD Accelerated Parallel Processing
Vendor  : Advanced Micro Devices, Inc.
Version : OpenCL 2.0 AMD-APP (1642.5)
== Device : 0000000002EB1220 ==
Name                                             : AMD FX(tm)-8350 Eight-Core Processor
Vendor                                           : AuthenticAMD (ID:4098)
Version                                          : OpenCL 1.2 AMD-APP (1642.5)
Driver version                                   : 1642.5 (sse2,avx,fma4)
OpenCL C version                                 : OpenCL C 1.2

1スレッド版が激遅なのはなんなのかって感じですがOpenCL版は更に速かったです。
うーん・・・なんなんでしょう。