我知道下面渔获物和抛
块是多余的,我很好奇什么样的伤害可以做?
可以编译器优化也走在释放模式?或者,它只是反正捕获异常并重新抛出呢?如果是后一种情况,什么样的性能损失的会引起?
尝试
{
// ...
}
抓住 {
扔;
}
解决方案
编译器不会优化了这一点,即使是在一个发布版本。
看看下面的测试应用程序:
公共类节目{
公共静态无效的主要(字串[] args){
尝试 {
CallsThrow();
}
赶上(例外前){
Console.WriteLine(主()捕获的异常:+ ex.Message);
}
Console.Read();
}
私有静态无效CallsThrow(){
尝试 {
扔();
}
抓住 {
扔;
}
}
私有静态无效掷(){
抛出新的异常(这是我的例外。);
}
}
使用 ILSpy 我们可以看看在输出二进制的的 IL 水平。我们看到,尝试
/ 抓
在 CallsThrow
仍是在我们发布的二进制有:
。方法私人hidebysig静
无效CallsThrow()CIL管理
{
//方法开始于RVA 0x2094
// code尺寸11(0XB)
.maxstack 1
。尝试
{
IL_0000:调用无效JunkCSharpConsole.Program ::掷()
IL_0005:leave.s IL_000a
} //结束。尝试
赶上[mscorlib程序] System.Object的
{
IL_0007:流行
IL_0008:重新抛出
} //结束处理
IL_000a:RET
法计划} //结束:: CallsThrow
code:
公共类节目
{
公共静态无效的主要(字串[] args){
const int的N =亿;
#如果调试
常量字符串模式=调试;
#其他
常量字符串模式=释放;
#ENDIF
Console.WriteLine(测试{0}在迭代{1}模式:,N模式);
//尝试JIT /缓存
CallsThrowWithTryCatch(假);
CallsThrowWithoutTryCatch(假);
//用try / catch语句+掷测试
变种S1 = Stopwatch.StartNew();
的for(int i = 0; I&n种;我++)
CallsThrowWithTryCatch(假);
s1.Stop();
Console.WriteLine(随着try / catch语句:{0}毫秒,s1.ElapsedMilliseconds);
//测试没有的try / catch +掷
变种S2 = Stopwatch.StartNew();
的for(int i = 0; I&n种;我++)
CallsThrowWithoutTryCatch(假);
s2.Stop();
Console.WriteLine(没有的try / catch {0}毫秒,s2.ElapsedMilliseconds);
VAR PCT =(s1.ElapsedMilliseconds - s2.ElapsedMilliseconds)/(双)s1.ElapsedMilliseconds * 100.0;
Console.WriteLine(没有的try / catch快了{0:0.02}%,PCT);
//只是表明它的工作原理
尝试 {
CallsThrowWithTryCatch(真正的);
}
赶上(例外前){
Console.WriteLine(主()捕获的异常:+ ex.Message);
}
//等待退出
Console.WriteLine(preSS ENTER退出。);
Console.Read();
}
私有静态无效CallsThrowWithTryCatch(布尔doThrow){
尝试 {
投掷(doThrow);
}
抓住 {
扔;
}
}
私有静态无效CallsThrowWithoutTryCatch(布尔doThrow){
投掷(doThrow);
}
私有静态无效掷(布尔doThrow){
如果(doThrow)
抛出新的异常(这是我的例外。);
}
}
结果:
测试亿反复调试模式:
随着try / catch语句:1492毫秒
如果没有的try / catch:1474毫秒
没有的try / catch快了1.22%
主()捕获的异常:这是我的异常。
preSS ENTER退出。
测试亿反复在发行方式:
随着try / catch语句:598毫秒
如果没有的try / catch:458毫秒
没有的try / catch快了23.42%
主()捕获的异常:这是我的异常。
preSS ENTER退出。
我们可以看到,对了,还有的是的关联的性能损失,甚至空尝试
/ 捉
。在调试版本也不是那么显著,但发布版本呈大幅23.42%的改善通过删除尝试
/ 抓
。
I know the following catch and throw
block is redundant, I am curious what kind of damage it can do ?
Can compiler optimizes it away in release mode? Or it will just catch the exception anyway and rethrow it ? If it is the latter case, what kind of performance penalty it will cause ?
try
{
//...
}
catch {
throw;
}
解决方案
The compiler will not optimize this out, even in a Release build.
Take the following test application:
public class Program {
public static void Main(string[] args) {
try {
CallsThrow();
}
catch(Exception ex) {
Console.WriteLine("Main() caught exception: " + ex.Message);
}
Console.Read();
}
private static void CallsThrow() {
try {
Throw();
}
catch {
throw;
}
}
private static void Throw() {
throw new Exception("Here's my exception.");
}
}
Using ILSpy we can look at the output binary at the IL level. We see that the try
/catch
in CallsThrow
is still there in our Release binary:
.method private hidebysig static
void CallsThrow () cil managed
{
// Method begins at RVA 0x2094
// Code size 11 (0xb)
.maxstack 1
.try
{
IL_0000: call void JunkCSharpConsole.Program::Throw()
IL_0005: leave.s IL_000a
} // end .try
catch [mscorlib]System.Object
{
IL_0007: pop
IL_0008: rethrow
} // end handler
IL_000a: ret
} // end of method Program::CallsThrow
Code:
public class Program
{
public static void Main(string[] args) {
const int N = 100000000;
#if DEBUG
const string mode = "Debug";
#else
const string mode = "Release";
#endif
Console.WriteLine("Testing {0} iterations in {1} mode:", N, mode);
// Attempt to JIT / cache
CallsThrowWithTryCatch(false);
CallsThrowWithoutTryCatch(false);
// Test with try/catch+throw
var s1 = Stopwatch.StartNew();
for (int i = 0; i < N; i++ )
CallsThrowWithTryCatch(false);
s1.Stop();
Console.WriteLine(" With try/catch: {0} ms", s1.ElapsedMilliseconds);
// Test without try/catch+throw
var s2 = Stopwatch.StartNew();
for (int i = 0; i < N; i++)
CallsThrowWithoutTryCatch(false);
s2.Stop();
Console.WriteLine(" Without try/catch: {0} ms", s2.ElapsedMilliseconds);
var pct = (s1.ElapsedMilliseconds - s2.ElapsedMilliseconds) / (double)s1.ElapsedMilliseconds * 100.0;
Console.WriteLine("No try/catch faster by {0:.02}%", pct);
// Just show that it works
try {
CallsThrowWithTryCatch(true);
}
catch (Exception ex) {
Console.WriteLine("Main() caught exception: " + ex.Message);
}
// Wait to exit
Console.WriteLine("Press ENTER to exit.");
Console.Read();
}
private static void CallsThrowWithTryCatch(bool doThrow) {
try {
Throw(doThrow);
}
catch {
throw;
}
}
private static void CallsThrowWithoutTryCatch(bool doThrow) {
Throw(doThrow);
}
private static void Throw(bool doThrow) {
if (doThrow)
throw new Exception("Here's my exception.");
}
}
Results:
Testing 100000000 iterations in Debug mode:
With try/catch: 1492 ms
Without try/catch: 1474 ms
No try/catch faster by 1.22%
Main() caught exception: Here's my exception.
Press ENTER to exit.
Testing 100000000 iterations in Release mode:
With try/catch: 598 ms
Without try/catch: 458 ms
No try/catch faster by 23.42%
Main() caught exception: Here's my exception.
Press ENTER to exit.
We can see that yes, there is a performance penalty associated with even the empty try
/catch
. In Debug builds it is not so significant, but a Release build showed a substantial 23.42% improvement by removing the try
/catch
.