测量许多方法的执行时间

时间:2014-02-26 18:50:19

标签: c# sql-server entity-framework

我在WPF的项目中工作,它使用Entity Framework和SQL Server。最优化有废话。一切都太慢了。我想要诊断代码的哪些部分会影响性能 - 我认为只有少数(我的意思是很多但不是全部)设计不良并且会导致性能下降。

现在我们每个表都有类,例如UserRepository。它不完全是存储库模式。这些类的方法如:GetAll(...), GetById(...), GetNewest(...), GetAllWithHigherSalaryThan(int salary, int companyId),等等,db的许多方法访问器。数据库仅用于存储库类。

我不想在这里谈论重构。我只想测量每个方法执行的时间以及在运行时执行的次数。通过这些信息,我将能够找到错误。

我想测量大约100个在许多类中“从db中选择”的方法。 SQL Server Profiler没有做到这一点,因为这些方法无数次执行,如果可能的话,使用我们的数据库方法分析来自profiler的日志是噩梦。

示例方法:

public IEnumerable<Foo> GetFoo(int y, int z)
{
        return Context.Where(p =>
            p.X == null &&
            p.Y == y &&
            p.Time >= z).OrderBy(x => x.Time).AsEnumerable();
}

现在我正在考虑为每个方法添加秒表,测量执行时间,计算执行次数并将其传递给某些单例或其他东西,然后显示它。当然,当我完成它时我会关闭这个诊断,但这种方法是非常周的课程,我需要编辑每个方法,我不知道如何关闭它 - 我的意思是我知道我可以使用像#define DEBUG这样的东西,但无论如何都是蹩脚的。

我试图通过Reflections来解决问题,但没有任何影响...也许有办法在C#中查看方法的执行时间?

你推荐什么?

2 个答案:

答案 0 :(得分:4)

我的第一个建议是一个合适的探查器,我的收藏是来自Jetbrains的dotTrace,但还有其他人。

如果某种方式不是一种选择,你坚持自己做:

我推荐Postsharp - 或其他AOP包。它可以做的是轻松地为许多方法添加方面。

使用Postsharp,您可以写一个方面:

[Serializable] 
public class TraceAttribute : OnMethodBoundaryAspect 
{ 
    public override void OnEntry( MethodExecutionArgs args ) 
    { 
        // start measuring time here
    } 

    public override void OnExit( MethodExecutionArgs args ) 
    { 
        // stop measuring here 
    } 
}

然后将该方面应用于一堆方法(来自MyNamespace的所有公共方法):

#if DEBUG 
[assembly: Trace( AttributeTargetTypes = "MyNamespace.*", 
    AttributeTargetTypeAttributes = MulticastAttributes.Public, 
    AttributeTargetMemberAttributes = MulticastAttributes.Public )] 
#endif 

这是一次性处理多种方法所需的所有代码。

我最近这样做了,我将所有测量结果放入csv文本文件中,然后将其转储到SQL Server中,我可以轻松地进行选择和分组,例如找出方法消耗的总时间。我没有使用分析器,因为我在生产环境中进行了测量 - 从仪器中获得的性能影响并不大(如果程序速度很慢)。

以下是有关的更多信息: http://www.postsharp.net/aspects/examples/logging

甚至免费版本的PostSharp Express就足以做到这一点。

答案 1 :(得分:0)

语言本身没有内置的探查器。如果你不介意指定你正在使用的内容,你的IDE / Debugger 可能有一些内置的分析器。

如果您按照指示运行MSSQL,则可能无法以适用于ya的方式进行概要分析。这是两个对我有神奇影响的查询:

SELECT TOP 10 SUBSTRING(qt.TEXT, (qs.statement_start_offset/2)+1,
 ((CASE qs.statement_end_offset
 WHEN -1 THEN DATALENGTH(qt.TEXT)
 ELSE qs.statement_end_offset
 END - qs.statement_start_offset)/2)+1),
 qs.execution_count,
 qs.total_logical_reads, qs.last_logical_reads,
 qs.total_logical_writes, qs.last_logical_writes,
 qs.total_worker_time,
 qs.last_worker_time,
 qs.total_elapsed_time/1000000 total_elapsed_time_in_S,
 qs.last_elapsed_time/1000000 last_elapsed_time_in_S,
 qs.last_execution_time,
 qp.query_plan
 FROM sys.dm_exec_query_stats qs
 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
 CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
 ORDER BY qs.total_logical_reads DESC -- logical reads
 -- ORDER BY qs.total_logical_writes DESC -- logical writes
 -- ORDER BY qs.total_worker_time DESC -- CPU time

source

SELECT DISTINCT TOP 10
 t.TEXT QueryName,
 s.execution_count AS ExecutionCount,
 s.max_elapsed_time AS MaxElapsedTime,
 ISNULL(s.total_elapsed_time / s.execution_count, 0) AS AvgElapsedTime,
 s.creation_time AS LogCreatedOn,
 ISNULL(s.execution_count / DATEDIFF(s, s.creation_time, GETDATE()), 0) AS FrequencyPerSec
 FROM sys.dm_exec_query_stats s
 CROSS APPLY sys.dm_exec_sql_text( s.sql_handle ) t
 ORDER BY
 s.max_elapsed_time DESC
 GO 

source

这里的神奇之处在于你可以找出哪些命令总体上占用的时间最多,不管它是一个非常缓慢运行10次的命令,还是运行1,000,000次但非常快的命令。

你也可以通过更高级的东西逃脱。我之前通过将探查器附加到每个Web请求的开头和结尾来调试项目,但是再次找不到导致问题的特定命令。