Sqrt等の算術演算と掛け算の処理負荷について

たまたま気になる記事を見つけて流石に mulとsqrtの負荷が同じは無いだろうということで検証してみました
(まさかりOKということなので)
はてなで一度上げましたがせっかくなのでQiitaを使ってみました

SinとかSqrt等の算術演算と掛け算の処理負荷を比べてみた
[https://qiita.com/geekdrums/items/1a6e7480f23c9ffbcfc0](https://qiita.com/geekdrums/items/1a6e7480f23c9ffbcfc0)

---
2/22 更新
#### コメントで指摘があったのでJITレベルで正しいコードに変更しました

- 計算に使用する変数をstatic化
- forありなしの結果が別実行だったので同時に出力するように

### 新しい結果
for文あり
**Mult=4959us Sqrt=12772us**
**SqrtはMulの2.575倍**

for文なし
**Mult=789us Sqrt=8602us**
**SqrtはMulの10.901倍**

---



~~結論から言うと
for文込み
**Mult=4863us Sqrt=13664us**
**SqrtはMulの2.810倍**
for文負荷抜き
**Mult=446us Sqrt=9460us**
**SqrtはMulの21.198倍**~~

新たなまさかり歓迎ですmm


## 気になった点
- UnityのProfiler精度が10usなため誤差が大きいがそれに対しループ数が少ない
- 倍精度積の負荷を見ているがfor文で倍精度の加算も行われている
- そもそもReleaseビルドだと最適化されて中身消えそう
- for文の負荷とか気になる


## 環境
Unity2018.3.2 .Net4.X
MacBook Pro 2.9 GHz Intel Core i7

## 検証コード
今回気になったところは乗算と平方根が同じ負荷というところなので、そこに絞って計測しました

```cs
public class MathTest : MonoBehaviour
{
    public const int count = 1000000;
    public const int loop = 100;

    public static double f;

    IEnumerator Start()
    {
        yield return new WaitForSeconds(1f);
        Test();
    }

    static void Test()
    {
        long loopTime = 0L;
        long multTime = 0L;
        long sqrtTime = 0L;

        for (int i = 0; i < loop; i++)
        {
            loopTime += Loop();
            multTime += Mult();
            sqrtTime += Sqrt();
        }

        Debug.Log($"{f}");

        Debug.Log($"Loop={loopTime} Mult={multTime} Sqrt={sqrtTime}");

        Debug.Log("for文あり");
        Debug.Log($"Mult={multTime / loop / 10L}us Sqrt={sqrtTime / loop / 10L}us");
        Debug.Log($"SqrtはMulの{(double) sqrtTime / multTime:F3}倍");

        Debug.Log("for文なし");
        multTime -= loopTime;
        sqrtTime -= loopTime;
        Debug.Log($"Mult={multTime / loop / 10L}us Sqrt={sqrtTime / loop / 10L}us");
        Debug.Log($"SqrtはMulの{(double) sqrtTime / multTime:F3}倍");
    }

    static long Loop()
    {
        f = 1.0;
        var sw = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            f = f;
        }

        sw.Stop();
        return sw.ElapsedTicks;
    }

    static long Mult()
    {
        f = 1.00001;
        var sw = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            f *= f;
        }

        sw.Stop();
        return sw.ElapsedTicks;
    }

    static long Sqrt()
    {
        f = 100000.0;
        var sw = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            f = System.Math.Sqrt(f);
        }

        sw.Stop();
        return sw.ElapsedTicks;
    }
}

```

~~ILはこんな感じになります
[SharpLab](https://sharplab.io/#v2:C4LglgNgPgAgDAAhgRgNwFgBQMDMSBMCAwggN5YKVJ4oBsSALAgLICGYAdgBQCUZFVAL4DKuBAGMA9hwDOwBJ3lSArh3kBeBMjg7dGTFWoTpchWoQRJkgA4JN2nfpGMWyiMF7PyBw1QAmksoARhAApggAZnZaAHS6cGjOhhGSAE4IXIoK0XCo2QA8xqrAeWAA1GU8SVTevnWRCABUmhH69QjCPpSdzjBMAMoAjqkeVV3844YBwWEN9vFwcW31KemZ5mA5pQiFKmqlFWPtte1UUZooMWzAABYxQyNcETzLdZ2GnYJAA==)~~

修正後のJITはこちらです
[SharpLab](https://sharplab.io/#v2:C4LghgzgtgPgAgBgARwIwG4CwAoOBmFAJiQGEkBvHJalAgYwHsA7CYJASybcYFcukAvElQJRYrNhq0kjFm05sANgwYAHQcNESqNfClQA2JABMGPAEaKApkgBmEqXrRGFHbZN0FnKACxIAsmCcABQAlDrUlB5S1AAqVqxhDjQAvhFI6d5wfvGJ4dEU6VLKTADmSMpqsexQNkIIADLJMRXM5VA8isDVtRqNzTEl5RAAjgBO3TV1SP04RTS2DGNIwex96BxIADytahvsANQH+S2R8y2Vqj02B0INKqpJ5zEdXddItwGdwE8Fp0ijCbvT4AZXGP1CAykaWwOAA9HD/gARKzmHilAB091KwQARIsxoBw00AQgyAKIZcZD4YjTii0ZjscEACS4/zfATkV6TXqIy5IREiBopHgQJBgibswFcmw8h58zSC4UUiQI5Go9FYhg45li4CAewZWYpAHYM5GCpgs1lCAPB70RnOuIAAYngUoBYBSVc2wKppavpmrxBMJgCsGQDqDO6/jQ7VMkABaISXa5QmiS96x3ZXKbK6ktWnqhnMg3AdmR7lpuUCoUinUS61RmVqMuNCthr3Zn0arW4nX6zrG01mSxWS3J2tIYtWR3Ot2UgowzKGVplJD3NRhdJRf62DSoDEIRPUAkrVxrer7bYyMxcfZHE6ndf/BYaezPWfh6hwADsMya6RfjnnQy+LpVwKO8Wk3IRtzEBAMGeA9Vn4Y8ZlPHZeEvDhr2eUD703AAqIQn1fJBf1OD8vwGYi33/NpRXBYCpCwqRwM0KCdz3OwlkPBD1k2FCL2AK9jkw55GI0NAMUCYAAAsMR1YJbGnf4KJiUjZhnHAUiAA)

```
Mult()
L0017: fmul qword [0x27e016a0]
```
```
Sqrt()
L0017: fsqrt
```
差分がここだけになったのでおそらく抜けは無いと思います

ついでにiOS用に出力したil2cppのコードは以下のようになっていて特に抜けはなさそうです

```
// f *= f;
double L_1 = ((MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_StaticFields*)il2cpp_codegen_static_fields_for(MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_il2cpp_TypeInfo_var))->get_f_6();
double L_2 = ((MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_StaticFields*)il2cpp_codegen_static_fields_for(MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_il2cpp_TypeInfo_var))->get_f_6();
((MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_StaticFields*)il2cpp_codegen_static_fields_for(MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_il2cpp_TypeInfo_var))->set_f_6(((double)il2cpp_codegen_multiply((double)L_1, (double)L_2)));

// f = System.Math.Sqrt(f);
double L_1 = ((MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_StaticFields*)il2cpp_codegen_static_fields_for(MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_il2cpp_TypeInfo_var))->get_f_6();
IL2CPP_RUNTIME_CLASS_INIT(Math_tFB388E53C7FDC6FCCF9A19ABF5A4E521FBD52E19_il2cpp_TypeInfo_var);
double L_2 = sqrt(L_1);
((MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_StaticFields*)il2cpp_codegen_static_fields_for(MathTest_tA4D864CB9DFCED1A10B65A009CDBB2F4A99381F9_il2cpp_TypeInfo_var))->set_f_6(L_2);

```


気をつけた点は

- 計測にメソッドを通さない
- ticksでの計測
- 十分CPUに同一負荷がかけられるように計測の平均値算出

ここまで回すと割と結果が安定しました

~~Mult=4863us Sqrt=13664us
SqrtはMulの2.810倍~~
~~Mult=446us Sqrt=9460us
SqrtはMulの21.198倍~~

for文あり
**Mult=4959us Sqrt=12772us**
**SqrtはMulの<font color="Red">2.575</font>**

for文なし
**Mult=789us Sqrt=8602us**
**SqrtはMulの<font color="Red">10.901倍</font>**

ちゃんと処理を見直しましたが割としっくり来る結果になってよかったです