前置き
この記事は以下の記事について独自検証をしたものです
「SinとかSqrt等の算術演算と掛け算の処理負荷を比べてみた」
https://qiita.com/geekdrums/items/1a6e7480f23c9ffbcfc0
マサカリウェルカムな元記事作者さんに感謝を
結論
元記事のコードはたぶん最適化でいろいろ消し飛んでる
こちらで計測した結果:
Mult | Sin | Sqrt | Atan | Exp | Pow | Pow2 |
---|---|---|---|---|---|---|
1倍 | 24.39倍 | 1.20倍 | 35.23倍 | 23.58倍 | 41.82倍 | 31.84倍 |
Windows10でC#コンソールアプリで計測、C#のMath関数を利用、コードは記事最下部
動機
さすがにSqrtがMultより早いのはおかしくない?
デコンパイルしてみた
世の中にはC#バイナリをソースコードに戻してくれるソフトがある
その1つILSpyにかけてみることにする
結果
はい
対策
C#の最適化対策はよくわからないので
とりあえず結果が使われるかもしれないとコンパイラを疑心暗鬼に陥らせてみる
全ての計算がfの最終値に影響するようにしたのと
fをpublicメンバに代入した
でILSpy
いけてそう
他にもildasmで関数呼び出し自体が消えていないかとかも見たけど成果なかったので省略
追記
Q:sqrtは掛け算割り算なしで行けるから早くても普通では?
A:さすがに掛け算1回よりは遅いでしょ
Q:Mult以外は前の計算結果使ってないぞ!最適化されてるんじゃないのか!
A:使わなくても問題ないコード吐かれたからそうしてる
Sinのデコンパイル結果
ついでに機械語レイヤー
ちゃんとfsin命令込みでループしているのがわかる
(0x01580CC1にfsin命令があり、ループのjmp先はそれよりも前の0x01580CBF、ステップ実行でも確認済み)
最終的な実行結果
実行回数と実行にかかった時間をミリ秒単位で出力しただけ
具体的に何倍速かもう1回貼っときますね
Mult | Sin | Sqrt | Atan | Exp | Pow | Pow2 |
---|---|---|---|---|---|---|
1倍 | 24.39倍 | 1.20倍 | 35.23倍 | 23.58倍 | 41.82倍 | 31.84倍 |
おわりに
unityは多分計測結果に関係ないだろうという慢心の元外した
IL弄ってfへ代入も排除した厳密計測したかったけどC#力が足らない
記事ももっと見栄えよくしたかったけどqiita力が足らない
自分はC#あまり書かないのでミスあるだろうけど、見つけた人はこの記事みたいに検証記事かいて、どうぞ
最後に検証に使ったコード置いときます
using System;
using System.Linq;
public class MathTest {
public double count;
public double step = 1;
public MathTest(double count = 1000.0) {
this.count = count;
}
public double trash;
private void Mult() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = f * i;
}
trash = f;
}
private void Sin() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = Math.Sin(i);
}
trash = f;
}
private void Sqrt() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = Math.Sqrt(i);
}
trash = f;
}
private void Pow() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = Math.Pow(i, i);
}
trash = f;
}
private void Pow2() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = Math.Pow(i, 2);
}
trash = f;
}
private void Atan() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = Math.Atan(i);
}
trash = f;
}
private void Exp() {
double f = 1.0;
for (double i = 0; i < count; i += step) {
f = Math.Exp(i);
}
trash = f;
}
public long[] run() {
return new Action[] { Mult, Sin, Sqrt, Atan, Exp, Pow, Pow2 }.Select(act => {
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
act();
sw.Stop();
return sw.ElapsedMilliseconds;
}).ToArray();
}
}
class Program {
static void Main(string[] args) {
new MathTest(100.0).run(); // メモリキャッシュとか対策にちょっと回す
Console.WriteLine("計測開始(単位はms)");
Console.WriteLine("Mult, Sin, Sqrt, Atan, Exp, Pow, Pow2");
foreach (double count in new double[] { 100_000.0, 1_000_000.0, 10_000_000.0, 100_000_000.0 }) {
Console.WriteLine((int)count);
Console.WriteLine(String.Join(", ", new MathTest(count).run()));
}
Console.ReadLine();
}
}