是否可以通过.NET中的属性分析各个方法?
我目前正在尝试在大型遗留应用程序中找到一些瓶颈,这些应用程序大量使用静态方法。目前,集成框架根本不是一种选择。由于大多数调用都使用静态方法,因此接口和依赖注入不可用。攻击代码以记录诊断也不是一个可行的解决方案。
我知道市场上有一些分析工具,但它们目前不在预算范围内。理想情况下,我将能够创建自己的自定义属性,该属性将记录有关方法输入和方法退出的一些基本信息。我从来没有真正使用过自定义属性,所以任何洞察甚至是否可能都会受到赞赏。
如果可能,我想通过配置文件启用分析。这将通过单元和集成测试支持分析。
答案 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();
}
}
控制台输出如下所示:
您需要将 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%的周期将处于不是真正必要的状态,所以只需将它们放入其中并进行查看即可。您不需要精确测量。
它适用于大问题。它也适用于小问题。如果你不止一次地完成整个过程,随着程序的快速发展,小问题变得相对更大,更容易找到。