【问题标题】:Fastest Way for Converting an Object to Double?将对象转换为双精度的最快方法?
【发布时间】:2011-03-11 04:28:39
【问题描述】:

将对象转换为双精度对象的最快方法是什么?我现在在一段代码中,上面写着:

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()在140ms内执行。新代码在 34 毫秒内执行。我几乎可以肯定这是我应该采取的优化路径,但在我这样做之前,有没有人看到我的“优化”代码有任何问题?提前感谢您的反馈!

【问题讨论】:

  • o 实际上是双精度还是字符串?
  • o 应该是双精度数还是可以转换为双精度数的对象(例如字符串或其他)
  • 可能很简单,但你不能使用演员表吗?
  • @Chris,o 通常为 double 或 null。非常非常罕见,它可能是一个真正的字符串,但我必须将这些情况排除在代码块之外。
  • @Michael,o 通常是 IConvertible,不知道是否有帮助。

标签: c# performance .net-3.5


【解决方案1】:

使用 System.Diagnostics.Stopwatch 创建一个小型测试应用,看看哪个更快。尽管我认为这不会产生有价值的差异。我会选择Convert.ToDouble 纯粹是为了可读性。

【讨论】:

  • 好建议,我会去的。一点:我进入了项目的性能优化阶段,因此可读性将逐渐被弃用,取而代之的是纯粹的性能(这就是我们所处的位置......叹息!)
  • @code4life - 优化性能通常很好,但不要在不完全了解需要优化的情况下进行过早的微优化,它最终可能会引入更难阅读、更容易出错的代码,同时浪费你的时间。
【解决方案2】:

首先,如果您真的想知道哪个更快,您应该编写一个快速测试(使用您希望处理的数据)并为每个选项计时。在不知道o 是(或可能是)什么的情况下很难判断。我怀疑你不会看到太大的不同。

其次,除非您在代码的非常时间关键部分调用这段代码,并且调用它数千次来启动,否则我怀疑它真的很重要。编写好的、干净的代码,然后优化。

【讨论】:

  • 当您优化时,总是基于分析器进行优化。
  • 它很干净......但代码在一次特定迭代中被调用超过 40,000 次。我正在分析代码,执行需要 140 毫秒,这在我正在处理的项目中代表了很大的性能滞后(信不信由你!)。我想让它比这更快。
【解决方案3】:

我尝试了以下方法。

  • 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 将是最快的。

【讨论】:

  • 你的意思是秒还是毫秒?
  • @code4life - 他的意思可能是秒,因为他在那段时间内完成了 10,000,000 次转化。
  • 感谢步法!顺便说一句,我做了一些更多的分析和双重分析。TryParse 对对象并不高效,纯粹因为它只支持字符串......因此需要先将对象转换为字符串然后传递给方法,所以需要 2 次调用。分析表明它的性能很差。但在仅处理字符串时绝对优于(不幸的是不是我的情况)
【解决方案4】:

你必须做很多这样的事情才能有意义地花时间在这上面。不过,我不是来评判的:

所以,你的代码是这样的:

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;
}

为您节省双重演员。

【讨论】:

  • 40,000 次调用这个函数......这就是原因!
  • 很好,看看不做双重演员是否会更好。看不出它会怎样。
  • 感谢您的推荐。我会接受它,它是有道理的。
  • 每隔一段时间对我来说都是如此!
  • d = o 是 IConvertible 值吗? value.ToDouble(null) : 0d;对于oneliners
【解决方案5】:

您可以尝试做几种不同的事情,这取决于 o 是什么类型的事情。可能是

a) 一个装箱的双精度,而您只想拆箱:

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

b) 一些其他类型、值或引用,它们可以转换为您想要使用的双精度(实现 IConvertible.ToDouble())

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

c) 具有可以被解析为双精度的默认字符串表示形式的东西

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

从某种意义上说,选项 c 是最长的路程;您正在获取您的对象,询问其字符串表示形式,然后尝试解析该字符串以获得双精度。如果您不需要这样做,这很笨拙,在您的 40,000 次调用示例中,它将创建和丢弃 40,000 个字符串...

如果您知道您的对象将始终包含实现双精度转换的内容,您可以跳过所有这些并选择选项 b。如果您知道您的对象只是一个装箱的双精度对象,请选择最简单的选项 (a) 将其拆箱。

如果你真的不知道 o 会是什么,也许这些方面的东西对你有用?

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

(注意:如果 o 包含实现 IConvertible 但不能转换为双精度的内容,或者如果它的字符串表示不能被解析为双精度,这将不起作用)

我没有提到相对速度,但如果拆箱并不比转换为字符串然后解析快得多(除非优化器非常聪明),我会感到惊讶。

使用 .NET 秒表在 LINQPad 中进行的快速测试表明存在很大差异。

IEnumerable<object> myData = new List<object>() { "53.2", 53.2M, 53.2D };
const int iterations = 10000000;
var sw = new Stopwatch();
var results = new List<string>();

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();

在我的电脑上给出以下结果

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

【讨论】:

  • 信息来自非 .NET 源(通过 COM),因此不能选择拆箱...
猜你喜欢
  • 2015-06-12
  • 1970-01-01
  • 2015-11-24
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
相关资源
最近更新 更多