代码之家  ›  专栏  ›  技术社区  ›  andreialecu

过度使用委托是否对性能不利?[复制品]

  •  4
  • andreialecu  · 技术社区  · 15 年前

    这个问题已经有了答案:

    请考虑以下代码:

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

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

    现在,更清洁的选择是编写类似这样的代码:

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

    其中定义了.log(),例如:

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

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

    哦,请不要建议条件编译,因为它不适用于本例。

    (P.S.:我直接在stackoverflow中编写了代码,请在文本区提问,因此如果有细微的错误并且没有编译,请不要怪我,你会明白的:)

    7 回复  |  直到 15 年前
        1
  •  5
  •   Mehrdad Afshari    15 年前

    不,它不应该有坏的表现。毕竟,只有在性能不处于最前沿的调试模式下才能调用它。实际上,你可以 移除lambda并传递方法名 移除不必要的中间匿名方法的开销。

    请注意,如果要在调试生成中执行此操作,可以添加 [Conditional("DEBUG")] 属性设置为Log方法。

        2
  •  3
  •   trampster    15 年前

    性能有差别。它的重要性将取决于代码的其余部分,因此我建议在开始优化之前进行分析。

    第一个例子就是这样说:

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

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

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

    每次调用instance.log时都将调用该方法。会变慢。

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

        3
  •  3
  •   andreialecu    15 年前

    我希望在这种情况下获得一些关于性能的文档,但似乎我得到的只是关于如何改进代码的建议…好像没人读过我的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毫秒,第二种情况下我有145毫秒。

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

        4
  •  1
  •   Sam Harwell    15 年前

    您也可以这样做:

    // 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)
    {
        ....
    }
    
        5
  •  0
  •   Mike Dunlavey    15 年前

    标准答案:

    • 如果你要做,你就要做。
    • 循环10^9次,看秒表,它告诉你需要多少纳秒。
    • 如果你的项目很大,你在其他地方可能会遇到更大的问题。
        6
  •  0
  •   Mehmet Aras    15 年前

    直接调用GetMessage委托,而不是对其调用Invoke。

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

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

        7
  •  -3
  •   Chris    15 年前

    我相信代理会创建一个新的线程,所以您对它的看法可能是正确的,可以提高性能。 为什么不按照DAV的建议设置一个测试运行,并密切关注应用程序生成的线程数,您可以使用ProcessExplorer来实现这一点。

    坚持!我被纠正了!委托仅在使用“BeginInvoke”时使用线程…所以我上面的评论不适用于你使用它们的方式。