Analizando el rendimiento (If, TryParse, Try-Catch)
Pregunta
Sé mucho sobre las diferentes formas de manejar el análisis de texto para obtener información. Para analizar enteros, por ejemplo, qué tipo de rendimiento se puede esperar. Me pregunto si alguien sabe de alguna buena estadística sobre esto. Estoy buscando algunos números reales de alguien que haya probado esto.
¿Cuál de estos ofrece el mejor rendimiento en qué situaciones?
Parse(...) // Crash if the case is extremely rare .0001%
If (SomethingIsValid) // Check the value before parsing
Parse(...)
TryParse(...) // Using TryParse
try
{
Parse(...)
}
catch
{
// Catch any thrown exceptions
}
Solución
Utilice siempre T. TryParse (string str, out T value) . Lanzar excepciones es costoso y debe evitarse si puede manejar la situación a priori . Usando un bloque try-catch para " guardar " en el rendimiento (porque su velocidad de datos no válida es baja) es un abuso del manejo de excepciones a expensas de la mantenibilidad y las buenas prácticas de codificación. Siga las prácticas sólidas de desarrollo de ingeniería de software, escriba sus casos de prueba, ejecute su aplicación, ENTONCES compare y optimice.
" Deberíamos olvidarnos de las pequeñas eficiencias, digamos alrededor del 97% del tiempo: la optimización prematura es la raíz de todo mal . Sin embargo, no debemos dejar pasar nuestras oportunidades en ese crítico 3% " -Donald Knuth
Por lo tanto, usted asigna, arbitrariamente, como en los créditos de carbono, que el rendimiento de try-catch es peor y que el rendimiento de TryParse es mejor . Solo después de ejecutar nuestra aplicación y determinar que tenemos algún tipo de desaceleración w.r.t. análisis de cadenas incluso consideraríamos usar algo que no sea TryParse.
(editar: dado que parece que el interlocutor quería que los datos de tiempo fueran con buenos consejos, aquí están los datos de tiempo solicitados)
Tiempos para varias tasas de falla en 10,000 entradas del usuario (para los no creyentes):
Failure Rate Try-Catch TryParse Slowdown
0% 00:00:00.0131758 00:00:00.0120421 0.1
10% 00:00:00.1540251 00:00:00.0087699 16.6
20% 00:00:00.2833266 00:00:00.0105229 25.9
30% 00:00:00.4462866 00:00:00.0091487 47.8
40% 00:00:00.6951060 00:00:00.0108980 62.8
50% 00:00:00.7567745 00:00:00.0087065 85.9
60% 00:00:00.7090449 00:00:00.0083365 84.1
70% 00:00:00.8179365 00:00:00.0088809 91.1
80% 00:00:00.9468898 00:00:00.0088562 105.9
90% 00:00:01.0411393 00:00:00.0081040 127.5
100% 00:00:01.1488157 00:00:00.0078877 144.6
/// <param name="errorRate">Rate of errors in user input</param>
/// <returns>Total time taken</returns>
public static TimeSpan TimeTryCatch(double errorRate, int seed, int count)
{
Stopwatch stopwatch = new Stopwatch();
Random random = new Random(seed);
string bad_prefix = @"X";
stopwatch.Start();
for(int ii = 0; ii < count; ++ii)
{
string input = random.Next().ToString();
if (random.NextDouble() < errorRate)
{
input = bad_prefix + input;
}
int value = 0;
try
{
value = Int32.Parse(input);
}
catch(FormatException)
{
value = -1; // we would do something here with a logger perhaps
}
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
/// <param name="errorRate">Rate of errors in user input</param>
/// <returns>Total time taken</returns>
public static TimeSpan TimeTryParse(double errorRate, int seed, int count)
{
Stopwatch stopwatch = new Stopwatch();
Random random = new Random(seed);
string bad_prefix = @"X";
stopwatch.Start();
for(int ii = 0; ii < count; ++ii)
{
string input = random.Next().ToString();
if (random.NextDouble() < errorRate)
{
input = bad_prefix + input;
}
int value = 0;
if (!Int32.TryParse(input, out value))
{
value = -1; // we would do something here with a logger perhaps
}
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
public static void TimeStringParse()
{
double errorRate = 0.1; // 10% of the time our users mess up
int count = 10000; // 10000 entries by a user
TimeSpan trycatch = TimeTryCatch(errorRate, 1, count);
TimeSpan tryparse = TimeTryParse(errorRate, 1, count);
Console.WriteLine("trycatch: {0}", trycatch);
Console.WriteLine("tryparse: {0}", tryparse);
}
Otros consejos
Aunque no he perfilado personalmente las diferentes formas, este capítulo tiene:
Try-Catch siempre será el más lento. TryParse será más rápido.
El IF y TryParse son iguales.
Aquí está otro tipo que también ha perfilado las diferencias de rendimiento , y lo hace con algunos tipos de datos diferentes para ver cuánto (si es que lo hacen) afectan inherentemente el rendimiento: int, bool y DateTime.
Option 1: Will throw an exception on bad data.
Option 2: SomethingIsValid() could be quite expensive - particularly if you are pre-checking a string for Integer parsability.
Option 3: I like this. You need a null check afterwards, but it's pretty cheap.
Option 4 is definitely the worst.
El manejo de excepciones es relativamente costoso, así que evítelo si puede.
En particular, se esperan entradas malas, no excepcionales, por lo que no debe usarlas para esta situación.
(Aunque, antes de TryParse, puede haber sido la mejor opción.)