获取特定的StackFrame而不是StackTrace.GetFrame更便宜吗?

时间:2013-09-16 20:32:05

标签: c# .net performance benchmarking callstack

如果我只想做以下事情,看看有什么叫我,

var st = new StackTrace();
var callingMethod = st.GetFrame(1).GetMethod()

获得特定框架会更便宜吗?

var sf = new StackFrame(1);
var callingMethod = sf.GetMethod()

我测试了下面的代码,但我不确定我的方法是否合理。


Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
  var method = new StackFrame(1, false);
}
sw.Stop();
Trace.WriteLine(sw.ElapsedMilliseconds);

sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
  var method = new StackTrace().GetFrame(1);
}
sw.Stop();
Trace.WriteLine(sw.ElapsedMilliseconds);

// Results
// StackFrame: 850
// StackTrace: 1334

我的方法(和结果)是否正确?

修改

我使用Caller Information属性,但是,我暂时停留在.NET 3.5中。

1 个答案:

答案 0 :(得分:6)

有关正确基准的编译,请参阅recommendations。您应该使用素数迭代(用于抑制JIT Loop unwinding优化),在Release模式下运行基准测试而不进行调试,使用缓存预热等等。

我在BenchmarkDotNet中添加了您的示例,请查看StackFrameProgram.cs

public class StackFrameProgram
{
    private const int IterationCount = 100001;

    public void Run()
    {
        var competition = new BenchmarkCompetition();
        competition.AddTask("StackFrame", () => StackFrame());
        competition.AddTask("StackTrace", () => StackTrace());
        competition.Run();
    }

    private StackFrame StackFrame()
    {
        StackFrame method = null;
        for (int i = 0; i < IterationCount; i++)
            method = new StackFrame(1, false);
        return method;
    }

    private StackFrame StackTrace()
    {
        StackFrame method = null;
        for (int i = 0; i < IterationCount; i++)
            method = new StackTrace().GetFrame(1);
        return method;
    }
}

有我的结果(Intel Core i7-3632QM CPU 2.20GHz):

x86, .NET 3.5:
StackFrame : 1035ms
StackTrace : 1619ms

x64, .NET 3.5:
StackFrame :  981ms
StackTrace : 1754ms

x86, .NET 4.0:
StackFrame :  735ms
StackTrace : 1150ms

x64, .NET 4.0:
StackFrame : 637ms
StackTrace : 880ms

让我们看看里面:

public StackFrame.ctor(int skipFrames, bool fNeedFileInfo)
{
    this.InitMembers();
    this.BuildStackFrame(skipFrames, fNeedFileInfo);
}

private void StackFrame.BuildStackFrame(int skipFrames, bool fNeedFileInfo)
{
    StackFrameHelper sfh = new StackFrameHelper(fNeedFileInfo, null);
    StackTrace.GetStackFramesInternal(sfh, 0, null);
    int numberOfFrames = sfh.GetNumberOfFrames();
    skipFrames += StackTrace.CalculateFramesToSkip(sfh, numberOfFrames);
    if ((numberOfFrames - skipFrames) > 0)
    {
        this.method = sfh.GetMethodBase(skipFrames);
        this.offset = sfh.GetOffset(skipFrames);
        this.ILOffset = sfh.GetILOffset(skipFrames);
        if (fNeedFileInfo)
        {
            this.strFileName = sfh.GetFilename(skipFrames);
            this.iLineNumber = sfh.GetLineNumber(skipFrames);
            this.iColumnNumber = sfh.GetColumnNumber(skipFrames);
        }
    }
} 

public StackTrace.ctor()
{
    this.m_iNumOfFrames = 0;
    this.m_iMethodsToSkip = 0;
    this.CaptureStackTrace(0, false, null, null);
}

private void StackTrace.CaptureStackTrace(int iSkip, bool fNeedFileInfo, Thread targetThread, Exception e)
{
    this.m_iMethodsToSkip += iSkip;
    StackFrameHelper sfh = new StackFrameHelper(fNeedFileInfo, targetThread);
    GetStackFramesInternal(sfh, 0, e);
    this.m_iNumOfFrames = sfh.GetNumberOfFrames();
    if (this.m_iMethodsToSkip > this.m_iNumOfFrames)
    {
        this.m_iMethodsToSkip = this.m_iNumOfFrames;
    }
    if (this.m_iNumOfFrames != 0)
    {
        this.frames = new StackFrame[this.m_iNumOfFrames];
        for (int i = 0; i < this.m_iNumOfFrames; i++)
        {
            bool flag = true;
            bool flag2 = true;
            StackFrame frame = new StackFrame(flag, flag2);
            frame.SetMethodBase(sfh.GetMethodBase(i));
            frame.SetOffset(sfh.GetOffset(i));
            frame.SetILOffset(sfh.GetILOffset(i));
            frame.SetIsLastFrameFromForeignExceptionStackTrace(sfh.IsLastFrameFromForeignExceptionStackTrace(i));
            if (fNeedFileInfo)
            {
                frame.SetFileName(sfh.GetFilename(i));
                frame.SetLineNumber(sfh.GetLineNumber(i));
                frame.SetColumnNumber(sfh.GetColumnNumber(i));
            }
            this.frames[i] = frame;
        }
        if (e == null)
        {
            this.m_iMethodsToSkip += CalculateFramesToSkip(sfh, this.m_iNumOfFrames);
        }
        this.m_iNumOfFrames -= this.m_iMethodsToSkip;
        if (this.m_iNumOfFrames < 0)
        {
            this.m_iNumOfFrames = 0;
        }
    }
    else
    {
        this.frames = null;
    }
}

public virtual StackFrame StackTrace.GetFrame(int index)
{
    if (((this.frames != null) && (index < this.m_iNumOfFrames)) && (index >= 0))
    {
        return this.frames[index + this.m_iMethodsToSkip];
    }
    return null;
}