我在WPF的项目中工作,该项目使用实体框架和SQL Server。那里的优化是垃圾。一切都太慢了。我想诊断代码的哪些部分会影响性能——我认为只有少数(我指的是很多,但不是全部)地方设计不好,会影响性能。
现在,对于每个表,我们都有类,例如UserRepository
。这并不完全是存储库模式。这些类有一些方法,比如:GetAll(...), GetById(...), GetNewest(...), GetAllWithHigherSalaryThan(int salary, int companyId)
等等,还有很多数据库的方法访问器。数据库仅在存储库类中使用。
我不想在这里谈论重构。我只想测量每个方法执行多长时间,以及在运行时执行了多少次。通过这些信息,我将能够找到漏洞。
我想测量大约100个方法,这些方法在许多类中都有"从数据库中选择"。SQL Server 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();
}
现在我正在考虑为每个方法添加秒表,测量执行时间,计算执行次数,并将其传递给某个singleton或其他什么东西,然后显示它。当然,当我完成诊断后,我会关闭它,但这种方法是非常周的课程,我需要编辑每个方法,之后我不知道如何关闭它——我的意思是,我知道我可以使用#define DEBUG
之类的东西,但无论如何它都很蹩脚。
我试图用"反射"来解决一些问题,但没有效果。。。也许有一种方法可以查看C#中方法的执行时间?
你推荐什么?
我的第一个建议是一个合适的分析器,我最喜欢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也足以做到这一点。
语言本身没有内置的探查器。不过,如果您不介意指定正在使用的内容,您的IDE/Degger可能会内置一些探查器。
如果您按照指示运行MSSQL,您可能不会以适合您的方式进行分析。以下是两个对我来说很神奇的查询:
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
源
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
源
这里的神奇之处在于,无论是运行速度非常慢的10次命令,还是运行速度非常快的1000000次命令,您都可以找出哪些命令总体上占用的时间最多。
你也可能会得到一些更高的东西。我以前可以通过在每个web请求的开头和结尾附加一个探查器来调试项目,但后来又找不到导致问题的特定命令。