StackFrame 的性能如何? [英] How performant is StackFrame?

查看:19
本文介绍了StackFrame 的性能如何?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在考虑使用类似 StackFrame stackFrame = new StackFrame(1) 的东西来记录执行方法,但我不知道它的性能影响.堆栈跟踪是在每个方法调用中构建的,因此性能不应该成为问题,还是仅在需要时构建?您是否建议在性能非常重要的应用程序中使用它?如果是这样,这是否意味着我应该在发布时禁用它?

解决方案

一些背景

<小时>

我们有一个类似的功能,在 99% 的情况下都被禁用;我们使用的方法是:

public void DoSomething(){TraceCall(MethodBase.GetCurrentMethod().Name);//做一点事}public void TraceCall(string methodName){如果(!loggingEnabled){返回;}//日志...}TraceCall(MethodBase.GetCurrentMethod().Name)

这很简单,但无论是否启用跟踪,我们都会因使用反射查找方法名称而导致性能下降.

我们的选择是要么在每个方法中都需要更多的代码(并冒着简单错误或拒绝的风险),要么切换到使用 StackFrame 来确定调用方法 only 在记录时已启用.

选项 A:

public void DoSomething(){如果(日志记录已启用){TraceCall(MethodBase.GetCurrentMethod().Name);}//做一点事}public void TraceCall(string methodName){如果(!loggingEnabled){返回;}//日志...}

选项 B:

public void DoSomething(){跟踪调用();//做一点事}公共无效 TraceCall(){如果(!loggingEnabled){返回;}StackFrame stackFrame = new StackFrame(1);//日志...}

我们选择了选项 B.它比选项 A 提供了显着的性能改进禁用日志记录时,99% 的时间并且实施起来非常简单.>

这是迈克尔代码的一个改动,以显示这种方法的成本/收益

使用系统;使用 System.Diagnostics;使用 System.Reflection;命名空间控制台应用程序{课程计划{静态布尔跟踪调用;静态无效主(字符串 [] args){秒表开关;//暖身for (int i = 0; i <100000; i++){跟踪调用();}//调用 100K 次,跟踪 *disabled*,传递方法名称sw = 秒表.StartNew();跟踪调用 = 假;for (int i = 0; i <100000; i++){TraceCall(MethodBase.GetCurrentMethod());}sw.停止();Console.WriteLine("禁用跟踪,传递方法名称:{0}ms", sw.ElapsedMilliseconds);//调用 100K 次,跟踪*启用*,传递方法名称sw = 秒表.StartNew();跟踪调用 = 真;for (int i = 0; i <100000; i++){TraceCall(MethodBase.GetCurrentMethod());}sw.停止();Console.WriteLine("启用跟踪,传递方法名称:{0}ms", sw.ElapsedMilliseconds);//调用 100K 次,跟踪 *disabled*,确定方法名称sw = 秒表.StartNew();跟踪调用 = 假;for (int i = 0; i <100000; i++){跟踪调用();}Console.WriteLine("禁用跟踪,查找方法名称:{0}ms", sw.ElapsedMilliseconds);//调用 100K 次,跟踪*启用*,确定方法名称sw = 秒表.StartNew();跟踪调用 = 真;for (int i = 0; i <100000; i++){跟踪调用();}Console.WriteLine("启用跟踪,查找方法名称:{0}ms", sw.ElapsedMilliseconds);Console.ReadKey();}私有静态无效 TraceCall(){如果(跟踪调用){StackFrame stackFrame = new StackFrame(1);TraceCall(stackFrame.GetMethod().Name);}}私有静态无效 TraceCall(MethodBase 方法){如果(跟踪调用){TraceCall(method.Name);}}private static void TraceCall(string methodName){//写入日志}}}

结果:

跟踪禁用,传递方法名称:294ms启用跟踪,传递方法名称:298ms跟踪禁用,查找方法名称:0ms启用跟踪,查找方法名称:1230ms

I am considering using something like StackFrame stackFrame = new StackFrame(1) to log the executing method, but I don't know about its performance implications. Is the stack trace something that is build anyway with each method call so performance should not be a concern or is it something that is only build when asked for it? Do you recommend against it in an application where performance is very important? If so, does that mean I should disable it for the release?

解决方案

edit: Some background


We have a similar feature which is disabled 99% of the time; we were using an approach like:

public void DoSomething()
{
    TraceCall(MethodBase.GetCurrentMethod().Name);
    // Do Something
}

public void TraceCall(string methodName)
{
    if (!loggingEnabled) { return; }
    // Log...
}

TraceCall(MethodBase.GetCurrentMethod().Name)

It was simple, but regardless of whether or not tracing was enabled we were incurring the performance hit of using Reflection to lookup the method name.

Our options were to either require more code in every method (and risk simple mistakes or refusal) or to switch to using StackFrame to determine the calling method only when logging was enabled.

Option A:

public void DoSomething()
{
    if (loggingEnabled)
    {
        TraceCall(MethodBase.GetCurrentMethod().Name);
    }
    // Do Something
}

public void TraceCall(string methodName)
{
    if (!loggingEnabled) { return; }
    // Log...
}

Option B:

public void DoSomething()
{
    TraceCall();
    // Do Something
}

public void TraceCall()
{
    if (!loggingEnabled) { return; }
    StackFrame stackFrame = new StackFrame(1);
    // Log...
}

We opted for Option B. It offers significant performance improvements over Option A when logging is disabled, 99% of the time and is very simple to implement.

Here's an alteration of Michael's code, to display the cost / benefit of this approach

using System;
using System.Diagnostics;
using System.Reflection;

namespace ConsoleApplication
{
    class Program
    {
        static bool traceCalls;

        static void Main(string[] args)
        {
            Stopwatch sw;

            // warm up
            for (int i = 0; i < 100000; i++)
            {
                TraceCall();
            }

            // call 100K times, tracing *disabled*, passing method name
            sw = Stopwatch.StartNew();
            traceCalls = false;
            for (int i = 0; i < 100000; i++)
            {
                TraceCall(MethodBase.GetCurrentMethod());
            }
            sw.Stop();
            Console.WriteLine("Tracing Disabled, passing Method Name: {0}ms"
                             , sw.ElapsedMilliseconds);

            // call 100K times, tracing *enabled*, passing method name
            sw = Stopwatch.StartNew();
            traceCalls = true;
            for (int i = 0; i < 100000; i++)
            {
                TraceCall(MethodBase.GetCurrentMethod());
            }
            sw.Stop();
            Console.WriteLine("Tracing Enabled, passing Method Name: {0}ms"
                             , sw.ElapsedMilliseconds);

            // call 100K times, tracing *disabled*, determining method name
            sw = Stopwatch.StartNew();
            traceCalls = false;
            for (int i = 0; i < 100000; i++)
            {
                TraceCall();
            }
            Console.WriteLine("Tracing Disabled, looking up Method Name: {0}ms"
                       , sw.ElapsedMilliseconds);

            // call 100K times, tracing *enabled*, determining method name
            sw = Stopwatch.StartNew();
            traceCalls = true;
            for (int i = 0; i < 100000; i++)
            {
                TraceCall();
            }
            Console.WriteLine("Tracing Enabled, looking up Method Name: {0}ms"
                       , sw.ElapsedMilliseconds);

            Console.ReadKey();
        }

        private static void TraceCall()
        {
            if (traceCalls)
            {
                StackFrame stackFrame = new StackFrame(1);
                TraceCall(stackFrame.GetMethod().Name);
            }
        }

        private static void TraceCall(MethodBase method)
        {
            if (traceCalls)
            {
                TraceCall(method.Name);
            }
        }

        private static void TraceCall(string methodName)
        {
            // Write to log
        }
    }
}

The Results:

Tracing Disabled, passing Method Name: 294ms
Tracing Enabled,  passing Method Name: 298ms
Tracing Disabled, looking up Method Name: 0ms
Tracing Enabled,  looking up Method Name: 1230ms

这篇关于StackFrame 的性能如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆