在一次微软员工的代码检查中,我们在一个try{}块中发现了一大块代码。她和一位IT代表表示,这可能会对代码的性能产生影响。事实上,他们建议大部分代码应该在try/catch块之外,并且只检查重要的部分。这位微软员工补充说,即将发布的白皮书对错误的尝试/捕获块提出了警告。
我环顾四周,发现它会影响优化,但它似乎只适用于在作用域之间共享变量时。
我不是在问代码的可维护性,甚至不是在问如何处理正确的异常(毫无疑问,有问题的代码需要重构)。我也不是指使用异常进行流控制,这在大多数情况下显然是错误的。这些都是重要的问题(有些更重要),但不是这里的重点。
当不抛出异常时,try/catch块如何影响性能?
我试了一个深接球。
static void TryCatch(int level, int max)
{
try
{
if (level < max) TryCatch(level + 1, max);
}
catch
{ }
}
static void NoTryCatch(int level, int max)
{
if (level < max) NoTryCatch(level + 1, max);
}
static void Main(string[] args)
{
var s = new Stopwatch();
const int max = 10000;
s.Start();
TryCatch(0, max);
s.Stop();
Console.WriteLine("try-catch " + s.Elapsed);
s.Restart();
NoTryCatch(0, max);
s.Stop();
Console.WriteLine("no try-catch " + s.Elapsed);
}
结果:
try-catch 00:00:00.0008528
no try-catch 00:00:00.0002422
在本m的例子中,结构是不同的。它将在内部的for循环中扩展开销,这将导致它不能很好地比较两种情况。
下面是更准确的比较,整个代码检查(包括变量声明)在Try/Catch块中:
for (int j = 0; j < 10; j++)
{
Stopwatch w = new Stopwatch();
w.Start();
try {
double d1 = 0;
for (int i = 0; i < 10000000; i++) {
d1 = Math.Sin(d1);
d1 = Math.Sin(d1);
}
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
finally {
//d1 = Math.Sin(d1);
}
w.Stop();
Console.Write(" try/catch/finally: ");
Console.WriteLine(w.ElapsedMilliseconds);
w.Reset();
w.Start();
double d2 = 0;
for (int i = 0; i < 10000000; i++) {
d2 = Math.Sin(d2);
d2 = Math.Sin(d2);
}
w.Stop();
Console.Write("No try/catch/finally: ");
Console.WriteLine(w.ElapsedMilliseconds);
Console.WriteLine();
}
当我运行Ben M的原始测试代码时,我注意到Debug和Releas配置中的差异。
这个版本,我注意到在调试版本中有一个不同(实际上比其他版本更多),但在发布版本中没有任何不同。
结论:
基于这些测试,我认为我们可以说Try/Catch确实对性能有很小的影响。
编辑:
我尝试将循环值从10000000增加到1000000000,并在Release中再次运行以获得发布中的一些差异,结果是这样的:
try/catch/finally: 509
No try/catch/finally: 486
try/catch/finally: 479
No try/catch/finally: 511
try/catch/finally: 475
No try/catch/finally: 477
try/catch/finally: 477
No try/catch/finally: 475
try/catch/finally: 475
No try/catch/finally: 476
try/catch/finally: 477
No try/catch/finally: 474
try/catch/finally: 475
No try/catch/finally: 475
try/catch/finally: 476
No try/catch/finally: 476
try/catch/finally: 475
No try/catch/finally: 476
try/catch/finally: 475
No try/catch/finally: 474
你看,结果是不必然的。在某些情况下,使用Try/Catch的版本实际上更快!
我试了一个深接球。
static void TryCatch(int level, int max)
{
try
{
if (level < max) TryCatch(level + 1, max);
}
catch
{ }
}
static void NoTryCatch(int level, int max)
{
if (level < max) NoTryCatch(level + 1, max);
}
static void Main(string[] args)
{
var s = new Stopwatch();
const int max = 10000;
s.Start();
TryCatch(0, max);
s.Stop();
Console.WriteLine("try-catch " + s.Elapsed);
s.Restart();
NoTryCatch(0, max);
s.Stop();
Console.WriteLine("no try-catch " + s.Elapsed);
}
结果:
try-catch 00:00:00.0008528
no try-catch 00:00:00.0002422
. net异常模型的非常全面的解释。
Rico Mariani的性能花絮:异常成本:何时抛出,何时不抛出
The first kind of cost is the static
cost of having exception handling in
your code at all. Managed exceptions
actually do comparatively well here,
by which I mean the static cost can be
much lower than say in C++. Why is
this? Well, static cost is really
incurred in two kinds of places:
First, the actual sites of
try/finally/catch/throw where there's
code for those constructs. Second, in
unmanged code, there's the stealth
cost associated with keeping track of
all the objects that must be
destructed in the event that an
exception is thrown. There's a
considerable amount of cleanup logic
that must be present and the sneaky
part is that even code that doesn't
itself throw or catch or otherwise
have any overt use of exceptions still
bears the burden of knowing how to
clean up after itself.
德米特里·扎斯拉夫斯基:
根据Chris Brumme的注释:有
还有一个与事实有关的成本
有些优化没有进行
由JIT在现场执行
抓