14


2

オブジェクトをダブルに変換する最速の方法は?

オブジェクトをダブルに変換する最速の方法は何ですか? 私は今、次のコードを読んでいます:

var d = double.TryParse(o.ToString(), out d);  // o is the Object...

最初の考えは、これを次のように書き換えることでした

var d = Convert.ToDouble(o);

しかし、それは実際には速いでしょうか?

編集:*プロファイルの実行に加えて(ちなみに、 JetBrains dotTrace *をすべての開発者に強くお勧めします)、Reflectorを実行しました。コード):

if (o is IConvertible)
{
    d = ((IConvertible)o).ToDouble(null);
}
else
{
    d = 0d;
}

元のコード `double.TryParse()`は140ミリ秒で実行されました。 新しいコードは34msで実行されます。 これが私がとるべき最適化パスであることはほぼ確実ですが、それを行う前に、誰かが私の「最適化された」コードに問題を見つけますか? ご意見ありがとうございます。

5 Answer


13


これに時間を費やす意味を理解するために、これらのすべてをなんとかしなければなりません。 しかし、私はここで判断するわけではありません:

だから、あなたのコードはこれです:

if (o is IConvertible)
{
    d = ((IConvertible)o).ToDouble(null);
}
else
{
    d = 0d;
}

これでいいのかな?

IConvertible convert = o as IConvertible;

if (convert != null)
{
  d = convert.ToDouble(null);
}
else
{
  d = 0d;
}

ダブルキャストを保存します。


5


次の方法を試しました。

  • double.TryParse

  • double.Parse

  • Convert.ToDouble

私は以下のコードを使いました。

public static void Main()
{
    string text = "3.14";
    var timer = new Stopwatch();
    timer.Start();
    for (int i = 0; i < 10000000; i++)
    {
        double d;
        d = Convert.ToDouble(text);
        //double.TryParse(text, out d);
        //d = double.Parse(text);
    }
    timer.Stop();
    Console.WriteLine("Time=" + timer.Elapsed.ToString());
    Console.ReadLine();
}

私のマシンでこれらの結果を見ました。 3つの異なる実行を平均しました。

  • double.TryParse = 4.45秒

  • double.Parse = 4.45秒

  • Convert.ToDouble = 4.75秒

もちろん、変換可能な文字列を使用しました。 文字列が変換可能でない場合、 `double.TryParse`がロングショットで最速になると確信しています。


3


System.Diagnostics.Stopwatchを使用して小さなテストアプリを作成し、どれがより高速になるかを確認します。 私はこれが価値のある違いをもたらさないと主張するだろうが。 私は純粋に読みやすくするために「Convert.ToDouble」に行きます。


1


最初に、どちらが速いかを本当に知りたい場合は、(テストするデータを使用して)簡単なテストを作成し、各オプションの時間を計る必要があります。 「o」が何であるか(またはそうである可能性が高い)を知らなければ、判断するのは非常に困難です。 私はあなたが多くの違いを見ないだろうと思う。

第二に、コードの「非常に」タイムクリティカルな部分でこのコードを呼び出し、起動するのに何千回も呼び出さない限り、それが本当に重要であるとは思わない。 適切でクリーンなコードを記述し、_then_最適化します。


0


oがどのようなものであるかに応じて、あなたがしようとしている可能性のあるいくつかの異なることがあります。 かもしれない

a)ボックス化されたダブルで、ボックスを解除するだけです:

object o = 53.2;
double d = (double)o;

b)使用可能なdoubleへの変換(IConvertible.ToDouble()を実装)を持つ他の型、値、または参照。

object o = 53.2M; // a System.Decimal
double d = Convert.ToDouble(o);

or

c)doubleとして解析できるデフォルトの文字列表現を持つもの

object o = "53.2";
double d;
bool convertedOK = double.TryParse(o.ToString(), out d);

オプションcは、ある意味では最長の方法です。オブジェクトを取得して、その文字列表現を要求し、その文字列を解析してdoubleを取得しようとしています。 これを行う必要がない場合、これは不格好であり、40,000の呼び出しの例では、40,000の文字列を作成して破棄します…​

オブジェクトに常にdoubleへの変換を実装するものが含まれていることがわかっている場合は、すべてスキップしてオプションbに進むことができます。 オブジェクトがボックス化されたダブルであることがわかっている場合は、最も単純なオプション(a)を使用して、ボックス化を解除します。

あなたが本当に何をするのか分からないなら、これらの線に沿った何かがあなたのために働くかもしれませんか?

double d = (o is double) ? (double)o
    : (o is IConvertible) ? (o as IConvertible).ToDouble(null)
    : double.Parse(o.ToString());

(注:oにIConvertibleを実装しているもののdoubleに変換できないものが含まれている場合、またはその文字列表現をdoubleとして解析できない場合、これは機能しません)

相対的な速度については何も言いませんでしたが、文字列に変換してから解析するよりもボックス化解除が大幅に速くない場合は驚かれることでしょう(オプティマイザーが非常に賢くなければ)。

NETストップウォッチを使用したLINQPadでの簡単なテストでは、大きな違いが示唆されています。
IEnumerable myData = new List() { "53.2", 53.2M, 53.2D };
const int iterations = 10000000;
var sw = new Stopwatch();
var results = new List();

foreach (var o in myData)
{
    sw.Reset();
    sw.Start();

    for (var i=0; i < iterations; i++)
    {
        double d = (o is double) ? (double)o
            : (o is IConvertible) ? (o as IConvertible).ToDouble(null)
            : double.Parse(o.ToString());
    }

    sw.Stop();

    results.Add($"{o.GetType()}: {iterations} iterations took {sw.ElapsedMilliseconds}ms");
}

results.Dump();

私のPCで次の結果が得られます

System.String: 10000000 iterations took 1329ms
System.Decimal: 10000000 iterations took 402ms
System.Double: 10000000 iterations took 38ms