O DateTime.Now é a melhor maneira de medir o desempenho de uma função?

474

Preciso encontrar um gargalo e medir o tempo com a maior precisão possível.

O trecho de código a seguir é a melhor maneira de medir o desempenho?

DateTime startTime = DateTime.Now;

// Some execution process

DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
David Basarab
fonte
A propósito, se você não estiver procurando algo, contadores de desempenho rápidos e sujos podem ser usados.
11138 Jonathan C Dickinson #
1
Se você precisar de maior precisão, use Stopwatch.GetTimestamp, caso contrário, a resposta é boa.
dbasnett
@dbasnett Você pode entrar em mais detalhes em uma resposta?
David Basarab
No exemplo acima, altere start e endtime para long e atribua Stopwatch.GetTimestamp a eles em vez de DateTime.Now. O tempo gasto é (início inicial) /Stopwatch.Frequency.
precisa saber é o seguinte
Veja também: The Case Against DateTime.Now
Matt Johnson-Pint

Respostas:

649

Não, não é. Use o cronômetro (pol System.Diagnostics)

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

O cronômetro verifica automaticamente a existência de temporizadores de alta precisão.

Vale ressaltar que DateTime.Nowmuitas vezes é um pouco mais lento do que DateTime.UtcNowdevido ao trabalho que deve ser feito com fusos horários, horário de verão e tal.

DateTime.UtcNow normalmente tem uma resolução de 15 ms. Ver a publicação de blog de John Chapman sobreDateTime.Now precisão para obter um ótimo resumo.

Curiosidades interessantes: o cronômetro volta a funcionar DateTime.UtcNowse o seu hardware não suportar um contador de alta frequência. Você pode verificar se o Stopwatch usa hardware para obter alta precisão observando o campo estático Stopwatch.IsHighResolution .

Markus Olsson
fonte
3
Eu colocaria um PerformWork (); antes do cronômetro para "aquecer".
Divan
2
Também é necessário adicionar a recomendação de que, se você PerformWork()for muito curto, poderá chamá-lo repetidamente e calcular a média do lote de chamadas. Além disso, programe um lote inteiro de chamadas, em vez de iniciar / interromper o seu, Stopwatchpara evitar um efeito estroboscópico que prejudicará suas medições de tempo.
Devgeezer
1
O cronômetro não é seguro para threads em vários núcleos. Consulte stackoverflow.com/questions/6664538/… e stackoverflow.com/questions/1149485/…
Pavel Savara
1
sw.ElapsedMilliseconds; também pode
Flappy
2
@Pavel, para ser claro, o Stopwatch é recomendado pela Microsoft como a melhor solução (baixa sobrecarga e alta precisão) nos modernos processadores multicore que executam o Windows 7 e o Windows 8. msdn.microsoft.com/en-us/library/windows/ área de trabalho /…
Ron
91

Se você quiser algo rápido e sujo, sugiro usar o cronômetro para obter um maior grau de precisão.

Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();

Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);

Como alternativa, se você precisar de algo um pouco mais sofisticado, provavelmente considere usar um criador de perfil de terceiros, como o ANTS .

mmcdole
fonte
56

Este artigo diz que antes de tudo é preciso comparar três alternativas, Stopwatch, DateTime.NowE DateTime.UtcNow.

Também mostra que, em alguns casos (quando o contador de desempenho não existe), o cronômetro está usando o DateTime.UtcNow + algum processamento extra. Por isso, é óbvio que, nesse caso, DateTime.UtcNow é a melhor opção (porque outros a usam + algum processamento)

No entanto, como se vê, o contador quase sempre existe - consulte Explicação sobre o contador de desempenho de alta resolução e sua existência relacionada ao .NET Stopwatch. .

Aqui está um gráfico de desempenho. Observe como o UtcNow de baixo desempenho compara-se às alternativas:

Digite a descrição da imagem aqui

O eixo X é o tamanho dos dados da amostra e o eixo Y é o tempo relativo do exemplo.

Uma coisa Stopwatché melhor: ele fornece medições de tempo de resolução mais alta. Outra é a sua natureza mais OO. No entanto, criar um wrapper OO UtcNownão pode ser difícil.

Valentin Kuzub
fonte
O primeiro link parece estar quebrado.
Peter Mortensen
1
ficou quebrado sim .. máquina do tempo pode mostrar isso, eu acho. Btw porque você edita "os três", o não é necessário aqui eu acredito.
Valentin Kuzub 22/03
18

É útil inserir seu código de benchmarking em uma classe / método de utilitário. A StopWatchclasse não precisa estar Disposedou estar Stoppederrada. Portanto, o código mais simples para cronometrar alguma ação é

public partial class With
{
    public static long Benchmark(Action action)
    {
        var stopwatch = Stopwatch.StartNew();
        action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }
}

Código de chamada de amostra

public void Execute(Action action)
{
    var time = With.Benchmark(action);
    log.DebugFormat(“Did action in {0} ms.”, time);
}

Aqui está a versão do método de extensão

public static class Extensions
{
    public static long Benchmark(this Action action)
    {
        return With.Benchmark(action);
    }
}

E código de chamada de amostra

public void Execute(Action action)
{
    var time = action.Benchmark()
    log.DebugFormat(“Did action in {0} ms.”, time);
}
Anthony Mastrean
fonte
1
Que tal uma melhor granularidade? Muitas coisas acontecem em menos de um ms.
Henrik
Retorne a propriedade Elapsed, então é um TimeSpan. Estou apenas mostrando o padrão. Divirta-se implementando-o.
Anthony Mastrean
Retorne Elapsed.TotalMillisecondspara maior precisão. Veja também esta questão stackoverflow.com/questions/8894425/…
nawfal 12/04/2013
16

A funcionalidade do cronômetro seria melhor (maior precisão). Eu também recomendaria apenas baixar um dos criadores de perfil populares ( DotTrace e ANTS são os que eu mais usei ... a avaliação gratuita do DotTrace é totalmente funcional e não incomoda como alguns dos outros).

jsight
fonte
13

Use a classe System.Diagnostics.Stopwatch.

Stopwatch sw = new Stopwatch();
sw.Start();

// Do some code.

sw.Stop();

// sw.ElapsedMilliseconds = the time your "do some code" took.
rp.
fonte
11

O mesmo cronômetro, é muito melhor.

Em relação à medição de desempenho, você também deve verificar se o seu "// Some Execution Process" é um processo muito curto.

Lembre-se também de que a primeira execução do seu "// Some Execution Process" pode ser muito mais lenta que as execuções subsequentes.

Normalmente, testo um método executando-o 1000 vezes ou 1000000 vezes em um loop e obtenho dados muito mais precisos do que executá-lo uma vez.

Andrei Rînea
fonte
9

Todas essas são ótimas maneiras de medir o tempo, mas essa é apenas uma maneira muito indireta de encontrar gargalos.

A maneira mais direta de encontrar um gargalo em um encadeamento é fazê-lo funcionar e, enquanto faz o que quer que você espere, pare-o com uma tecla de pausa ou quebra. Faça isso várias vezes. Se o seu gargalo demorar X% do tempo, X% é a probabilidade de você capturá-lo em flagrante em cada instantâneo.

Aqui está uma explicação mais completa de como e por que funciona

Mike Dunlavey
fonte
7

@ Sean Chambers

FYI, a classe .NET Timer não é para diagnóstico, ela gera eventos em um intervalo predefinido, como este (do MSDN ):

System.Timers.Timer aTimer;
public static void Main()
{
    // Create a timer with a ten second interval.
    aTimer = new System.Timers.Timer(10000);

    // Hook up the Elapsed event for the timer.
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);

    // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer.Interval = 2000;
    aTimer.Enabled = true;

    Console.WriteLine("Press the Enter key to exit the program.");
    Console.ReadLine();
}

// Specify what you want to happen when the Elapsed event is 
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}

Portanto, isso realmente não ajuda a saber quanto tempo levou algo, apenas que uma certa quantidade de tempo se passou.

O cronômetro também é exposto como um controle no System.Windows.Forms ... você pode encontrá-lo na caixa de ferramentas do designer no VS05 / VS08

Adam Haile
fonte
6

Esta é a maneira correta:

using System;
using System.Diagnostics;

class Program
{
    public static void Main()
    {
        Stopwatch stopWatch = Stopwatch.StartNew();

            // some other code

        stopWatch.Stop();

        // this not correct to get full timer resolution
        Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);

        // Correct way to get accurate high precision timing
        Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
    }
}

Para obter mais informações, consulte Usar o cronômetro em vez do DataTime para obter um contador de desempenho preciso .

jiya jain
fonte
6

O Visual Studio Team System possui alguns recursos que podem ajudar com esse problema. Essencialmente, você pode escrever testes de unidade e misturá-los em diferentes cenários para executar no seu software como parte de um teste de estresse ou carga. Isso pode ajudar a identificar áreas de código que mais afetam o desempenho de seus aplicativos.

O grupo de Padrões e práticas da Microsoft tem algumas orientações nas Diretrizes de teste de desempenho do sistema de equipes do Visual Studio .

Iain
fonte
5

Acabei de encontrar um post no blog de Vance Morrison sobre uma classe CodeTimer que ele escreveu que facilita o uso StopWatche faz algumas coisas legais ao lado.

OwenP
fonte
5

Isso não é profissional o suficiente:

Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);

Uma versão mais confiável é:

PerformWork();

int repeat = 1000;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
   PerformWork();
}

sw.Stop();

Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat);

No meu código real, adicionarei a chamada GC.Collect para alterar o heap gerenciado para um estado conhecido e adicionarei a chamada Sleep, para que diferentes intervalos de código possam ser facilmente separados no perfil ETW.

Bye StackOverflow
fonte
4

Eu fiz muito pouco desse tipo de verificação de desempenho (eu costumo pensar "isso é lento, torna-o mais rápido"), então eu sempre o segui.

Um google revela muitos recursos / artigos para verificação de desempenho.

Muitos mencionam o uso do pinvoke para obter informações de desempenho. Muitos dos materiais que estudo apenas mencionam o uso de perfmon.

Editar:

Vi as palestras do StopWatch. Eu aprendi algo :)

Parece um bom artigo

Rob Cooper
fonte
4

A maneira como uso em meus programas é usando a classe StopWatch, como mostrado aqui.

Stopwatch sw = new Stopwatch();
sw.Start();


// Critical lines of code

long elapsedMs = sw.Elapsed.TotalMilliseconds;
Blackvault
fonte