21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SinとかSqrt等の算術演算の速度の件

Last updated at Posted at 2019-02-21

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

上記記事で気になった点がありましたので、自分も試してみました。

気になったのは以下の三つの点です。

  • UnityのProfilerのコストがかかっている
    -- (Profilerを動かしている最中は、Profilerにて記録されている関数の呼び出し時に計測コードが挟まっているため、関数呼び出しが遅くなっている)

  • 計測したい処理と比べて、ループ自体のコストがおおきい
    -- (例えば掛け算一つだと、多分ループ自体の方が処理コストが高い)

  • コンパイラによる簡単な最適化がかかるだけで処理が消えてしまいそう
    -- (計算結果を使うようにしてあげないと処理の中身が空になったりしうる)

ということで、上記点を考慮して修正した簡単な検証コードを作成し計測しました。

関数 時間(ミリ秒) Multとの倍率
Mult 255ms 1.00倍
Sin 2587ms 10.15倍
Sqrt 821ms 3.22倍
Atan 2902ms 11.38倍
Exp 2131ms 8.36倍
Pow 3532ms 13.85倍
Pow2 3546ms 13.91倍

以下計測コード
Quiitaのテーブルが出力されるようにしています
(2019/02/22 10:35 少し気になる点があったので修正。計測結果は大差なし)

public class MathTest
{
    // using文を使って範囲を計測できるようにした時間計測クラス
    class Stopwatch : System.IDisposable
    {
        static double baseTime = -1;

        System.Diagnostics.Stopwatch sw;
        string memberName;

        public Stopwatch([System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
        {
            this.memberName = memberName;
            sw = new System.Diagnostics.Stopwatch();
            sw.Start();
        }

        public void Dispose()
        {
            sw.Stop();
            if (baseTime < 0) baseTime = sw.ElapsedMilliseconds;
            Debug.Log($"| {memberName} |  {sw.ElapsedMilliseconds}ms | {sw.ElapsedMilliseconds / baseTime:F2}倍 |");
        }
    }

    // 最適化で消えにくいようにpublic static変数で指定しておく
    public static int count = 10000000;
    public static double firstValue = 0.9999254686; // 掛け続けてもInfinityにならない値に修正

    double Mult()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = f * f; // ループ文の影響を薄くするため、とりあえず8回手で複製する
                f = f * f; // 計算結果が最適化で消えないよう過去の値を使う
                f = f * f;
                f = f * f;
                f = f * f;
                f = f * f;
                f = f * f;
                f = f * f;
            }

        return f;
    }

    double Sin()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = Math.Sin(f);
                f = Math.Sin(f);
                f = Math.Sin(f);
                f = Math.Sin(f);
                f = Math.Sin(f);
                f = Math.Sin(f);
                f = Math.Sin(f);
                f = Math.Sin(f);
            }

        return f;
    }

    double Sqrt()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
                f = Math.Sqrt(f);
            }

        return f;
    }

    double Pow()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
                f = Math.Pow(f, i);
            }

        return f;
    }

    double Pow2()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
                f = Math.Pow(f, 2);
            }

        return f;
    }

    double Atan()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = Math.Atan(f);
                f = Math.Atan(f);
                f = Math.Atan(f);
                f = Math.Atan(f);
                f = Math.Atan(f);
                f = Math.Atan(f);
                f = Math.Atan(f);
                f = Math.Atan(f);
            }

        return f;
    }

    double Exp()
    {
        double f = firstValue;

        using (new Stopwatch())
            for (var i = 1; i < count; i++)
            {
                f = Math.Exp(f);
                f = Math.Exp(f);
                f = Math.Exp(f);
                f = Math.Exp(f);
                f = Math.Exp(f);
                f = Math.Exp(f);
                f = Math.Exp(f);
                f = Math.Exp(f);
            }

        return f;
    }

    public void MainTest()
    {
        double ret = Mult()
            + Sin()
            + Sqrt()
            + Atan()
            + Exp() // Expは計算途中でInfinityとなるのがちょっと問題かも
            + Pow()
            + Pow2();

        Debug.Log(ret); // インライン展開されて消えることがないようにちゃんと戻り値を使う
    }
}

21
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?