En el CLR de Windows, para una cadena de llamadas de profundidad 8, lanzar una excepción es 750 veces más lento que verificar y propagar un valor de retorno. (ver abajo para puntos de referencia)
Este alto costo de excepciones se debe a que el CLR de Windows se integra con algo llamado Manejo de excepciones estructuradas de Windows . Esto permite que las excepciones sean capturadas y lanzadas correctamente en diferentes tiempos de ejecución e idiomas. Sin embargo, es muy muy lento.
Las excepciones en el tiempo de ejecución Mono (en cualquier plataforma) son mucho más rápidas, ya que no se integra con SEH. Sin embargo, hay una pérdida de funcionalidad al pasar excepciones a través de múltiples tiempos de ejecución porque no usa nada como SEH.
Aquí hay resultados abreviados de mi punto de referencia de excepciones vs valores de retorno para el CLR de Windows.
baseline: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.25 (0), time elapsed 13.0007 ms
baseline: recurse_depth 8, error_freqeuncy 0.5 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 0.75 (0), time elapsed 13.0008 ms
baseline: recurse_depth 8, error_freqeuncy 1 (0), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0 (0), time elapsed 13.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.25 (249999), time elapsed 14.0008 ms
retval_error: recurse_depth 5, error_freqeuncy 0.5 (499999), time elapsed 16.0009 ms
retval_error: recurse_depth 5, error_freqeuncy 0.75 (999999), time elapsed 16.001 ms
retval_error: recurse_depth 5, error_freqeuncy 1 (999999), time elapsed 16.0009 ms
retval_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 20.0011 ms
retval_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 21.0012 ms
retval_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 24.0014 ms
retval_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 24.0013 ms
exception_error: recurse_depth 8, error_freqeuncy 0 (0), time elapsed 31.0017 ms
exception_error: recurse_depth 8, error_freqeuncy 0.25 (249999), time elapsed 5607.3208 ms
exception_error: recurse_depth 8, error_freqeuncy 0.5 (499999), time elapsed 11172.639 ms
exception_error: recurse_depth 8, error_freqeuncy 0.75 (999999), time elapsed 22297.2753 ms
exception_error: recurse_depth 8, error_freqeuncy 1 (999999), time elapsed 22102.2641 ms
Y aquí está el código ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1 {
public class TestIt {
int value;
public class TestException : Exception { }
public int getValue() {
return value;
}
public void reset() {
value = 0;
}
public bool baseline_null(bool shouldfail, int recurse_depth) {
if (recurse_depth <= 0) {
return shouldfail;
} else {
return baseline_null(shouldfail,recurse_depth-1);
}
}
public bool retval_error(bool shouldfail, int recurse_depth) {
if (recurse_depth <= 0) {
if (shouldfail) {
return false;
} else {
return true;
}
} else {
bool nested_error = retval_error(shouldfail,recurse_depth-1);
if (nested_error) {
return true;
} else {
return false;
}
}
}
public void exception_error(bool shouldfail, int recurse_depth) {
if (recurse_depth <= 0) {
if (shouldfail) {
throw new TestException();
}
} else {
exception_error(shouldfail,recurse_depth-1);
}
}
public static void Main(String[] args) {
int i;
long l;
TestIt t = new TestIt();
int failures;
int ITERATION_COUNT = 1000000;
// (0) baseline null workload
for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {
int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);
failures = 0;
DateTime start_time = DateTime.Now;
t.reset();
for (i = 1; i < ITERATION_COUNT; i++) {
bool shoulderror = (i % EXCEPTION_MOD) == 0;
t.baseline_null(shoulderror,recurse_depth);
}
double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
Console.WriteLine(
String.Format(
"baseline: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
recurse_depth, exception_freq, failures,elapsed_time));
}
}
// (1) retval_error
for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {
int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);
failures = 0;
DateTime start_time = DateTime.Now;
t.reset();
for (i = 1; i < ITERATION_COUNT; i++) {
bool shoulderror = (i % EXCEPTION_MOD) == 0;
if (!t.retval_error(shoulderror,recurse_depth)) {
failures++;
}
}
double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
Console.WriteLine(
String.Format(
"retval_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
recurse_depth, exception_freq, failures,elapsed_time));
}
}
// (2) exception_error
for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {
int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);
failures = 0;
DateTime start_time = DateTime.Now;
t.reset();
for (i = 1; i < ITERATION_COUNT; i++) {
bool shoulderror = (i % EXCEPTION_MOD) == 0;
try {
t.exception_error(shoulderror,recurse_depth);
} catch (TestException e) {
failures++;
}
}
double elapsed_time = (DateTime.Now - start_time).TotalMilliseconds;
Console.WriteLine(
String.Format(
"exception_error: recurse_depth {0}, error_freqeuncy {1} ({2}), time elapsed {3} ms",
recurse_depth, exception_freq, failures,elapsed_time)); }
}
}
}
}