C#中异常的代价是多少?只要堆栈不深,它们似乎并不昂贵。但是我读了有冲突的报告。
是否有尚未被反驳的权威报告?
C#中异常的代价是多少?只要堆栈不深,它们似乎并不昂贵。但是我读了有冲突的报告。
是否有尚未被反驳的权威报告?
Answers:
Jon Skeet于2006年1月在.NET中撰写了“异常和性能”
更新了异常和性能Redux(感谢@Gulzar)
Rico Mariani在“ .NET异常的真正代价”中提出的建议–解决方案
另请参见:Krzysztof Cwalina-设计指南更新:异常抛出
读完例外对于性能造成的损失,我提出了一个简单的测量程序,该程序与几年前发布的Jon Skeet非常相似。我在这里提到这个主要是为了提供更新的数字。
该程序花了29914毫秒以下的时间来处理100万个异常,相当于每毫秒33个异常。这样的速度足以使异常成为大多数情况下返回代码的可行替代方案。
但是请注意,使用返回码而不是异常,同一程序的运行时间不到一毫秒,这意味着异常比返回码慢至少30,000倍。正如Rico Mariani强调的那样,这些数字也是最小数字。实际上,引发和捕获异常将花费更多时间。
在具有.NET 4.0发行版的Intel Core2 Duo T8100 @ 2,1 GHz的笔记本电脑上进行测量,该测试未在调试器下运行(这会使运行速度变慢)。
这是我的测试代码:
static void Main(string[] args)
{
int iterations = 1000000;
Console.WriteLine("Starting " + iterations.ToString() + " iterations...\n");
var stopwatch = new Stopwatch();
// Test exceptions
stopwatch.Reset();
stopwatch.Start();
for (int i = 1; i <= iterations; i++)
{
try
{
TestExceptions();
}
catch (Exception)
{
// Do nothing
}
}
stopwatch.Stop();
Console.WriteLine("Exceptions: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
// Test return codes
stopwatch.Reset();
stopwatch.Start();
int retcode;
for (int i = 1; i <= iterations; i++)
{
retcode = TestReturnCodes();
if (retcode == 1)
{
// Do nothing
}
}
stopwatch.Stop();
Console.WriteLine("Return codes: " + stopwatch.ElapsedMilliseconds.ToString() + " ms");
Console.WriteLine("\nFinished.");
Console.ReadKey();
}
static void TestExceptions()
{
throw new Exception("Failed");
}
static int TestReturnCodes()
{
return 1;
}
assert
在发生异常情况时使用错误代码进行错误处理。
That is fast enough to make exceptions a viable alternative to return codes for most situations.
我不会说深度为2-3的调用堆栈确实代表了“大多数情况”。更深层次的调用堆栈可能也应该考虑。
我想我在营地,如果你的应用程序,那么你会抛出异常影响性能WAY太多了。例外应针对特殊情况,而不是作为常规错误处理。
就是说,我对异常处理方式的回忆实质上是在栈上查找与抛出的异常类型相匹配的catch语句。因此,性能将受到您对捕获的了解程度以及您拥有多少个捕获语句的影响最大。
就我而言,例外非常昂贵。我改写了这个:
public BlockTemplate this[int x,int y, int z]
{
get
{
try
{
return Data.BlockTemplate[World[Center.X + x, Center.Y + y, Center.Z + z]];
}
catch(IndexOutOfRangeException e)
{
return Data.BlockTemplate[BlockType.Air];
}
}
}
变成这个:
public BlockTemplate this[int x,int y, int z]
{
get
{
int ix = Center.X + x;
int iy = Center.Y + y;
int iz = Center.Z + z;
if (ix < 0 || ix >= World.GetLength(0)
|| iy < 0 || iy >= World.GetLength(1)
|| iz < 0 || iz >= World.GetLength(2))
return Data.BlockTemplate[BlockType.Air];
return Data.BlockTemplate[World[ix, iy, iz]];
}
}
并注意到大约30秒的良好速度增加。启动此函数至少调用32K次。代码并未明确意图是什么,但是节省了大量成本。
我进行了自己的测量,以了解异常含义的严重性。我没有尝试测量引发/捕获异常的绝对时间。我最感兴趣的是,如果在每次通过中都抛出异常,循环将变慢多少。测量代码如下所示
for(; ; ) {
iValue = Level1(iValue);
lCounter += 1;
if(DateTime.Now >= sFinish) break;
}
与
for(; ; ) {
try {
iValue = Level3Throw(iValue);
}
catch(InvalidOperationException) {
iValue += 3;
}
lCounter += 1;
if(DateTime.Now >= sFinish) break;
}
相差20倍。第二个片段要慢20倍。
只是为了提供个人经验:我正在使用NewtonSoft开发一个程序来解析JSON文件并从中提取数据。
我改写了这个:
try
{
name = rawPropWithChildren.Value["title"].ToString();
}
catch(System.NullReferenceException)
{
name = rawPropWithChildren.Name;
}
对此:
if(rawPropWithChildren.Value["title"] == null)
{
name = rawPropWithChildren.Name;
}
else
{
name = rawPropWithChildren.Value["title"].ToString();
}
OFC你真的没有上下文它法官,但这里是我的结果:
(在调试模式)
选项1,有例外。 38.50秒
选项2,有例外。 06.48秒
为了提供一些背景信息,我正在使用数千个可以为null的json属性。异常抛出过多,例如在执行时间的15%之内。好吧,不是很精确,但是它们被扔了太多次了。我想解决此问题,所以我更改了代码,但我不明白为什么执行时间如此之快。那是因为我糟糕的异常处理。
因此,我从中学到的内容是:仅在特殊情况下以及无法用简单的条件语句进行测试的情况下,才需要使用异常。还必须尽可能少地抛出它们。
对于您来说,这是一个随机的故事,但是我想从现在开始在代码中使用异常之前,我肯定会三思而后行!
异常的代价很高,但是要在异常代码和返回代码之间进行选择时,异常代码的功能更多。
从历史上讲,例外是确保代码被强制处理的情况,而返回代码可以忽略,我从来不赞成使用这些参数,因为没有程序员会故意忽略并破坏他们的代码-尤其是一支优秀的测试团队/或编写良好的测试用例绝对不会忽略返回代码。
从现代编程实践的角度来看,不仅要考虑异常例外的代价,而且还要考虑其可行性。
1号 由于大多数前端都将与抛出异常的API断开连接。例如使用Rest API的移动应用。相同的api也可以用于基于Angular js的Web前端。
无论哪种情况,都将首选返回码而不是例外。
2号 如今,黑客随机尝试破坏所有Web实用程序。在这种情况下,如果他们不断攻击您的应用程序登录api,并且应用程序不断引发异常,那么您每天将要处理成千上万的异常。当然,许多人会说防火墙会处理此类攻击,但是并非所有人都在花钱来管理专用防火墙或昂贵的反垃圾邮件服务,因此最好为这些情况准备代码。