Pergunta

É possível perfilar métodos individuais por meio de atributos no .NET?

Atualmente, estou tentando localizar alguns dos pescoços de garrafa em um grande aplicativo legado que faz uso pesado de métodos estáticos. A integração de uma estrutura simplesmente não é uma opção no momento. Como a maioria das chamadas usa métodos estáticos, as interfaces e a injeção de dependência não estão disponíveis. Hackear o código para registrar o diagnóstico também não é uma solução viável.

Sei que existem algumas ferramentas de perfil no mercado, mas elas estão atualmente fora do orçamento. Idealmente, eu seria capaz de criar meu próprio atributo personalizado que registraria algumas informações básicas sobre a entrada e saída do método. Eu realmente nunca trabalhei com atributos personalizados, portanto, qualquer insight sobre se isso for possível seria apreciado.

Se possível, gostaria de ativar o perfil por meio de um arquivo de configuração. Isso apoiaria o perfil por meio de testes de unidade e integração.

Foi útil?

Solução

Você não pode usar atributos para o que está fazendo. No entanto, você tem algumas opções:

Primeiro, muitas das ferramentas do Profiler por aí (como Redgate Formigas) são relativamente baratos (US $ 200 a US $ 300), fáceis de usar e a maioria oferece períodos de avaliação gratuitos de algumas semanas - para que você possa ver se eles lhe darão o elevador que precisa agora, antes de decidir se os deve comprá -los. Também o .NET CLR Profiler é gratuito para baixar.

Se isso não for possível, o PostSharp é provavelmente a maneira mais fácil de tecer essa lógica em seu código.

Por fim, se você não pode usar o PostSharp por qualquer motivo e estiver disposto a ir e adicionar atributos ao seu código, também poderá adicionar um bloco de instrumentação simples a cada método na forma de um bloco de uso:

public void SomeMethodToProfile()
{
    // following line collects information about current executing method
    // and logs it when the metric tracker is disposed of
    using(MetricTracker.Track(MethodBase.GetCurrentMethod()))
    { 
        // original code here...
    }
}

Uma implementação típica do metracker se parece com o seguinte:

public sealed class MetricTracker : IDisposable
{
    private readonly string m_MethodName;
    private readonly Stopwatch m_Stopwatch;

    private MetricTracker( string methodName ) 
       { m_MethodName = methodName; m_Stopwatch = Stopwatch.StartNew(); }

    void IDisposable.Dispose()
       { m_Stopwatch.Stop(); LogToSomewhere(); }

    private void LogToSomewhere()
       { /* supply your own implementation here...*/ }

    public static MetricTracker Track( MethodBase mb )
       { return new MetricTracker( mb.Name ); }
}

Outras dicas

Você poderia usar PostSharp Para fazer uma tecelagem, basicamente girando:

[Profiled]
public void Foo()
{
     DoSomeStuff();
}

em

public void Foo()
{
    Stopwatch sw = Stopwatch.StartNew();
    try
    {
        DoSomeStuff();
    }
    finally
    {
        sw.Stop();
        ProfileData.AddSample("Foo", sw.Elapsed);
    }
}

De fato, olhando para a documentação do PostSharp, você deve poder usar Gibraltar (com o PostSharp) para isso, se você puder pagar. Caso contrário, você pode acabar gastando um dia ou mais pegando o jeito do PostSharp, mas ainda pode valer a pena.

Observe que eu sei que você disse que não poderia se dar ao luxo de integrar uma estrutura na sua base de código, mas não é como se você realmente estivesse "integrando", mas para fazer com que o PostSharp execute algumas transformações pós-compile no seu código.

Eu estava procurando algo muito semelhante ao que você descreveu. Não consegui encontrar uma estrutura tão, então rolei a minha. Eu deveria observar que isso é muito simples, mas às vezes simples é bom!

Eu o descreveria como benchmarking atende aos testes de unidade. O conceito é isolar seções de código para medir ou comparar a velocidade.

Um exemplo típico do uso de atributos pareceria o seguinte:

[ProfileClass]
public class ForEachLoopBenchmarks
{
    [ProfileMethod]
    public void ForLoopBenchmark()
    {
        List<int> list = GetNumberList();

        for (int i = 0; i < list.Count; i++)
        {
        }
    }

    [ProfileMethod]
    public void ForEachLoopBenchmark()
    {
        List<int> list = GetNumberList();

        foreach (int i in list)
        {
        }
    }

    private List<int> GetNumberList()
    {
        List<int> list = new List<int>();
        for (int i = 0; i < 1000; i++)
        {
            list.Add(i);
        }
        return list;
    }
}

Você então cria um aplicativo de console e cola o código abaixo no Principal Método e adicione uma referência à montagem que contém a classe descrita anteriormente que é decorada com os atributos. O tempo de execução para cada método (execute 1000 vezes) será emitido para o console.

class Program
{
    static void Main(string[] args)
    {
        ProfileRunner rp = new ProfileRunner();
        rp.Run();
    }
}

A saída do console pareceria o seguinte:

console output

Você precisa adicionar referência do punit.dll para o aplicativo de console e a biblioteca de classes que contém os métodos marcados com os atributos.

Você pode obter isso como um pacote de NUGET aqui.

Comando Nuget: PM> Install-Package Punit

Se você preferir o código -fonte completo, pode encontrá -lo em Github aqui.

Eu baseei o método que realmente mede o tempo de execução nesta pergunta: https://stackoverflow.com/a/1048708/1139752

Eu cobro a implementação com mais detalhes no seguinte Postagem do blog.

Há também algum Ferramentas de perfil gratuitas que valem a pena olhar.

Eu faço ajuste de desempenho em C#. Tudo o que preciso é essa técnica. Não é grande coisa.

É baseado em uma ideia simples. Se você está esperando por muito mais tempo do que o necessário, isso significa que parte do programa também aguarda muito mais tempo do que o necessário, por algo que realmente não precisa ser feito.

E como está esperando? Quase sempre, em um local de chamada, na pilha de chamadas.

Portanto, se você apenas pausar enquanto espera e olhe para a pilha de chamadas, verá o que está esperando e, se não é realmente necessário (o que geralmente não é), você verá por que imediatamente.

Não confie apenas em uma amostra - faça isso algumas vezes. Qualquer coisa que apareça em mais de uma amostra de pilha é algo que, se você pode fazer algo a respeito, economizará muito tempo.

Veja bem, não se trata de funções de tempo ou de contar quantas vezes eles são chamados. Trata -se de entrar no programa sem aviso prévio, algumas vezes, e perguntar o que está fazendo e por quê. Se algo estiver desperdiçando 80% (ou 20% ou qualquer outra coisa) da época, 80% dos ciclos estarão no estado de não ser realmente necessário, portanto, basta entrar neles e dar uma olhada. Você não precisa de medição de precisão.

Funciona com grandes problemas. Também funciona com pequenos problemas. E se você fizer a coisa toda mais de uma vez, à medida que o programa for rápido, os pequenos problemas se tornam relativamente maiores e mais fáceis de encontrar.

Você não pode implementar isso por meio de atributos, a menos que queira usar a programação orientada para aspectos por meio de algo como PostSharp Para alcançar isto.

Você pode colocar a lógica condicional lá, com base em uma definição (potencialmente definida em uma configuração de compilação), no entanto. Isso pode ativar ou desligar o log com horários, dependendo das configurações de compilação atuais.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top