使用属性进行方法分析

时间:2009-09-28 20:16:23

标签: .net profiling custom-attributes

是否可以通过.NET中的属性分析各个方法?

我目前正在尝试在大型遗留应用程序中找到一些瓶颈,这些应用程序大量使用静态方法。目前,集成框架根本不是一种选择。由于大多数调用都使用静态方法,因此接口和依赖注入不可用。攻击代码以记录诊断也不是一个可行的解决方案。

我知道市场上有一些分析工具,但它们目前不在预算范围内。理想情况下,我将能够创建自己的自定义属性,该属性将记录有关方法输入和方法退出的一些基本信息。我从来没有真正使用过自定义属性,所以任何洞察甚至是否可能都会受到赞赏。

如果可能,我想通过配置文件启用分析。这将通过单元和集成测试支持分析。

6 个答案:

答案 0 :(得分:5)

您不能使用属性来处理您正在做的事情。但是,您确实有一些选择:

首先,许多分析工具(如RedGate ANTS)相对便宜(200美元至300美元),易于使用,大多数提供几周的免费评估期 - 所以你可以看看是否在您决定是否购买之前,他们会为您提供您现在需要的电梯。此外,.NET CLR profiler可免费下载。

如果不可能,PostSharp可能是将这种逻辑编织到代码中的最简单方法。

最后,如果由于某种原因无法使用PostSharp并且您愿意为代码添加属性,那么您也可以使用块的形式为每个方法添加一个简单的检测块:

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

典型的MetricTracker实现如下所示:

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

答案 1 :(得分:3)

您可以使用PostSharp做一些编织,基本上转向:

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

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

确实,看看PostSharp文档,如果你能负担得起的话,你应该可以使用Gibraltar(使用PostSharp)。否则你最终可能会花费一天时间来掌握PostSharp,但它仍然值得。

请注意,我知道你说你无法将框架集成到你的代码库中,但它并不像你真正“集成”那么多,因为让PostSharp在你的代码上运行一些编译后转换。

答案 2 :(得分:1)

我正在寻找与你描述的非常相似的东西。我找不到这样的框架,所以我自己动手了。我应该注意这是非常简单,但有时简单就好了!

我会将其描述为基准测试符合单元测试。这个概念是为了测量或比较速度而隔离代码段。

属性用法的典型示例如下所示:

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

然后,您创建一个控制台应用程序并将以下代码粘贴到 Main 方法中,并添加对包含先前描述的使用属性修饰的类的程序集的引用。然后将每个方法的执行时间(运行1000次)输出到控制台。

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

控制台输出如下所示:

console output

您需要将 pUnit.dll 的引用添加到控制台应用程序以及包含标记有属性的方法的类库中。

您可以通过 Nuget here将其作为一个包。

Nuget命令: PM&gt;安装包pUnit

如果您想要完整的源代码,可以在 Github here找到它。

我基于实际衡量此问题执行时间的方法:https://stackoverflow.com/a/1048708/1139752

我在以下blog post中更详细地介绍了实现。

答案 3 :(得分:0)

还有some个免费的分析工具可能值得一看。

答案 4 :(得分:0)

您无法通过属性实现此功能,除非您希望通过PostSharp之类的方式使用面向方面编程来实现此目的。

然而,您可以根据定义(可能在构建配置中设置)将条件逻辑放在那里。这可以根据您当前的编译设置打开或关闭您的日志记录。

答案 5 :(得分:0)

我在C#中进行性能调优。 All I need is this technique.这不是什么大问题。

这是基于一个简单的想法。如果你等待的时间超过了必要的时间,这意味着程序的一部分也会等待很长时间,而不是真正需要做的事情。

等待怎么样?几乎总是在呼叫堆栈上的呼叫站点。

所以,如果你只是在等待时暂停它,并查看调用堆栈,你会看到它正在等待什么,如果它不是真的必要(通常​​不是这样),你会看到为什么立即。

不要只信任一个样本 - 做几次。任何出现在多个堆栈样本上的东西,如果你可以做些什么,都会节省很多时间。

所以你看,它不是关于计时功能或计算它们被调用的次数。这是关于在未事先通知的情况下进入该计划,几次,并询问它正在做什么以及为什么。如果某些东西浪费了80%(或20%或其他)的时间,那么80%的周期将处于不是真正必要的状态,所以只需将它们放入其中并进行查看即可。您不需要精确测量。

它适用于大问题。它也适用于小问题。如果你不止一次地完成整个过程,随着程序的快速发展,小问题变得相对更大,更容易找到。