代表对代表性能过分是一个坏主意吗?

时间:2009-08-13 00:33:37

标签: c# .net performance delegates lambda

请考虑以下代码:

if (IsDebuggingEnabled) { 
   instance.Log(GetDetailedDebugInfo()); 
}

GetDetailedDebugInfo()可能是一种昂贵的方法,所以我们只想在调试模式下运行时调用它。

现在,更清洁的替代方案是编写类似这样的代码:

instance.Log(() => GetDetailedDebugInfo());

其中.Log()的定义如下:

public void Log(Func<string> getMessage)
{
    if (IsDebuggingEnabled) 
    {
        LogInternal(getMessage.Invoke());
    }
}

我关注的是性能,初步测试没有显示第二种情况特别昂贵,但如果负载增加,我不想遇到任何意外。

哦,请不要建议条件编译,因为它不适用于这种情况。

(P.S。:我直接在StackOverflow中编写代码问一个问题textarea所以不要怪我,如果有微小的错误而且它没有编译,你明白了这一点:)

7 个答案:

答案 0 :(得分:5)

不,它不应该有不好的表现。毕竟,只有在性能不是最重要的调试模式下才会调用它。实际上,您可以删除lambda并只传递方法名称以消除不必要的中间匿名方法的开销。

请注意,如果要在Debug版本中执行此操作,可以在日志方法中添加[Conditional("DEBUG")]属性。

答案 1 :(得分:3)

性能有所不同。它的重要性取决于你的其余代码,所以我建议在开始优化之前进行性能分析。

对你的第一个例子说:

if (IsDebuggingEnabled) 
{ 
    instance.Log(GetDetailedDebugInfo()); 
}

如果IsDebuggingEnabled是静态只读,那么检查将被丢弃,因为它知道它永远不会改变。这意味着如果IsDebuggingEnabled为false,上面的示例将没有性能影响,因为在JIT完成后代码将消失。

instance.Log(() => GetDetailedDebugInfo());

public void Log(Func<string> getMessage)
{
    if (IsDebuggingEnabled) 
    {
        LogInternal(getMessage.Invoke());
    }
}

每次调用该方法时都会调用该方法。哪个会慢一些。

但在花费时间进行微观优化之前,您应该对应用程序进行概要分析或运行一些性能测试,以确保这实际上是您应用程序的瓶颈。

答案 2 :(得分:3)

我希望在这种情况下提供一些关于性能的文档,但似乎我得到的是关于如何改进我的代码的建议......似乎没有人读过我的P.S. - 没有分数。

所以我写了一个简单的测试用例:

    public static bool IsDebuggingEnabled { get; set; }


    static void Main(string[] args)
    {
        for (int j = 0; j <= 10; j++)
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i <= 15000; i++)
            {
                Log(GetDebugMessage);
                if (i % 1000 == 0) IsDebuggingEnabled = !IsDebuggingEnabled;
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }

        Console.ReadLine();
        for (int j = 0; j <= 10; j++)
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i <= 15000; i++)
            {
                if (IsDebuggingEnabled) GetDebugMessage();
                if (i % 1000 == 0) IsDebuggingEnabled = !IsDebuggingEnabled;
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
        Console.ReadLine();
    }

    public static string GetDebugMessage()
    {
        StringBuilder sb = new StringBuilder(100);
        Random rnd = new Random();
        for (int i = 0; i < 100; i++)
        {
            sb.Append(rnd.Next(100, 150));
        }
        return sb.ToString();
    }

    public static void Log(Func<string> getMessage)
    {
        if (IsDebuggingEnabled)
        {
            getMessage();
        }
    }

两个版本之间的时间似乎完全相同。 我在第一种情况下得到145 ms,在第二种情况下得到145 ms

看起来我回答了自己的问题。

答案 3 :(得分:1)

你也可以这样做:

// no need for a lambda
instance.Log(GetDetailedDebugInfo)

// Using these instance methods on the logger
public void Log(Func<string> detailsProvider)
{
    if (!DebuggingEnabled)
        return;

    this.LogImpl(detailsProvider());
}

public void Log(string message)
{
    if (!DebuggingEnabled)
        return;

    this.LogImpl(message);
}

protected virtual void LogImpl(string message)
{
    ....
}

答案 4 :(得分:0)

直接调用getMessage委托,而不是在其上调用Invoke。

if(IsDebuggingEnabled)
{
  LogInternal(getMessage());
}

您还应该在getMessage上添加空检查。

答案 5 :(得分:0)

标准答案:

  • 如果你必须这样做,你必须这样做。
  • 循环10 ^ 9次,看秒表,&amp;它告诉你需要多少纳秒。
  • 如果你的计划很大,你可能在其他地方遇到更大的问题。

答案 6 :(得分:-3)

我相信代理人创建了一个新线程,所以你可能会对它提高性能。 为什么不设置像Dav建议的测试运行,并密切关注应用程序生成的线程数,您可以使用Process Explorer。

坚持下去!我已经纠正了!代表只在您使用'BeginInvoke'时使用线程...所以我的上述注释不适用于您使用它们的方式。