Насколько производительный StackFrame?

Июль 2012 обновления ( мерзавец 1.7.12 + )

теперь можно повторно основывать все фиксации, чтобы базироваться, и выбрать вторую фиксацию Y, чтобы быть раздавленными с первым X.

git rebase -i --root master

pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip

Эта команда может теперь использоваться для перезаписи всего продвижения истории от [1 111]"" вниз к корневой фиксации.

См. фиксация df5df20c1308f936ea542c86df1e9c6974168472 на GitHub от [1 125] Chris Webb (arachsys) .

Исходный ответ (февраль 2009)

я полагаю, что Вы найдете различные рецепты для этого в ТАК вопрос" , Как я комбинирую первые две фиксации репозитория мерзавца? "

Charles Bailey обеспеченный там большинство подробный ответ , напоминая нам, что фиксация является полным деревом (не только diffs от предыдущие состояния).
И здесь старая фиксация ("начальная фиксация") и новая фиксация (результат сплющивания) не будет иметь никакого общего предка.
, Которые означают, Вы не можете" commit --amend" начальная фиксация в новую, и затем повторно основывать на новую фиксацию начальной буквы историю предыдущей начальной фиксации (много конфликтов)

(Что последнее предложение больше не верно с [1 114])

Скорее (с [1 115] исходная "начальная фиксация", и B, последующая фиксация должна была быть раздавлена в начальную):

  1. Возвращаются к последней фиксации, что мы хотим сформировать начальную фиксацию (ГОЛОВА отсоединения):

    git checkout 
    
  2. Сброс указатель ответвления на начальную фиксацию, но отъезд индекса и рабочего неповрежденного дерева:

    git reset --soft 
    
  3. Исправление начальное дерево с помощью дерева от 'B':

    git commit --amend
    
  4. Временно тег эта новая начальная фиксация (или Вы могли помнить новую фиксацию sha1 вручную):

    git tag tmp
    
  5. Возвращаются к исходному ответвлению (примите ведущее устройство для этого примера):

    git checkout master
    
  6. Воспроизведение все фиксации после B на новую начальную фиксацию:

    git rebase --onto tmp 
    
  7. Удаляют временный тег:

    git tag -d tmp
    

, Что путь," rebase --onto" не представляет конфликты во время слияния, так как это повторно основывает историю сделанный после последняя фиксация (B), чтобы быть раздавленным в начальную (который был A) к [1 120] (представление раздавленной новой начальной фиксации): тривиальная ускоренная перемотка вперед объединяется только.

, Что работы для" A-B", но также и" A-...-...-...-B" (любое количество фиксаций может быть раздавлено в начальную этот путь)

40
задан 28 August 2009 в 18:31
поделиться

4 ответа

править: Немного фона


У нас есть аналогичная функция, которая отключена в 99% случаев; мы использовали такой подход, как:

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

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

TraceCall(MethodBase.GetCurrentMethod().Name)

Это было просто, но независимо от того, была ли включена трассировка или нет, мы понесли потери производительности из-за использования Reflection для поиска имени метода.

Наши варианты заключались в том, чтобы либо потребовать больше кода в каждый метод (и рисковать простыми ошибками или отказом) или переключиться на использование StackFrame для определения метода вызова только , когда ведение журнала было включено.

Вариант A:

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

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

Вариант B :

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

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

Мы выбрали вариант Б. Он предлагает значительные улучшения производительности по сравнению с вариантом A , когда ведение журнала отключено, в 99% случаев и его очень просто реализовать.

Вот изменение кода Майкла , для отображения затрат / выгод этого подхода

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
        }
    }
}

Результаты:

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
33
ответ дан 27 November 2019 в 01:27
поделиться

Быстрый и наивный тест показывает, что для кода, чувствительного к производительности, да, вы должны обратить внимание на это:

Не генерировать 100K кадров: 3 мс

Генерировать 100K кадров : 1805ms

На моей машине примерно 20 микросекунд на сгенерированный кадр. Небольшая, но ощутимая разница на большом количестве итераций.

Говоря о ваших последующих вопросах («Следует ли мне отключить генерацию StackFrame в моем приложении?»), Я бы посоветовал вам проанализировать ваше приложение, провести тесты производительности, например тот, который я сделал здесь, и посмотрите, соответствует ли разница в производительности вашей рабочей нагрузке.

using System;
using System.Diagnostics;

namespace ConsoleApplication
{
    class Program
    {
        static bool generateFrame;

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

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

            // call 100K times; no stackframes
            sw = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                CallA();
            }
            sw.Stop();
            Console.WriteLine("Don't generate 100K frames: {0}ms"
                                 , sw.ElapsedMilliseconds);

            // call 100K times; generate stackframes
            generateFrame = true;
            sw = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                CallA();
            }
            Console.WriteLine("Generate 100K frames: {0}ms"
                           , sw.ElapsedMilliseconds);

            Console.ReadKey();
        }

        private static void CallA()
        {
            CallB();
        }

        private static void CallB()
        {
            CallC();
        }

        private static void CallC()
        {
            if (generateFrame)
            {
                StackFrame stackFrame = new StackFrame(1);
            }
        }
    }
}
19
ответ дан 27 November 2019 в 01:27
поделиться

Судя по документации MSDN, StackFrames создается постоянно:

StackFrame создается и передается дальше the call stack for every function call made during the execution of a thread. The stack frame always includes MethodBase information, and optionally includes file name, line number, and column number information.

The constructor new StackFrame(1) you'd call would do this:

private void 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);
        }
    }
}

GetStackFramesInternal is an external method. CalculateFramesToSkip has a loop that operates exactly once, since you specified only 1 frame. Everything else looks pretty quick.

Have you tried measuring how long it would take to create, say, 1 million of them?

6
ответ дан 27 November 2019 в 01:27
поделиться

Я рассматриваю возможность использования чего-то вроде StackFrame stackFrame = new StackFrame (1) для регистрации выполняемого метода

Из интереса: Почему? Если вам нужен только текущий метод, то

string methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;

кажется лучше. Может быть, не более производительный (я не сравнивал, но Reflection показывает, что GetCurrentMethod () не просто создает StackFrame, но творит некоторую «магию»), но более ясен в своем намерении.

5
ответ дан 27 November 2019 в 01:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: