Разве дешевле получить определенный StackFrame вместо StackTrace.GetFrame?

Если я просто сделаю следующее, чтобы узнать, что вызвало меня,

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

Правильно ли мой подход (и результаты)?

Edit

Я бы использовал атрибуты Информация о вызывающем абоненте, однако я застрял в .NET 3.5 в настоящее время.

Ответ 1

См. рекомендации для компиляции правильного эталона. Вы должны использовать простое число итераций (для подавления оптимизации JIT Loop unwinding), запустить тест в режиме деблокирования без отладки, использовать разминку кеша и т.д.

Я добавил ваш пример в 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;
}