Hice algunas pruebas (en .Net 3.5 ... más tarde comprobaré en casa usando .Net 4). El hecho es: Obtener un objeto como interfaz y luego ejecutar el método es más rápido que obtener un delegado de un método y luego llamar al delegado.
Considerando que la variable ya está en el tipo correcto (interfaz o delegado) e invocación simple hace que el delegado gane.
Por alguna razón, obtener un delegado sobre un método de interfaz (quizás sobre cualquier método virtual) es MUCHO más lento.
Y, considerando que hay casos en los que simplemente no podemos pre-almacenar el delegado (como en Despachos, por ejemplo), eso puede justificar por qué las interfaces son más rápidas.
Éstos son los resultados:
para obtener resultados reales, compilar este en modo de lanzamiento y ejecutarlo fuera de Visual Studio.
Comprobar llamadas directas doble
00: 00: 00,5834988
00: 00: 00,5997071
Comprobar llamadas de interfaz, obteniendo la interfaz en cada llamada
00: 00: 05,8998212
Comprobación llamadas a la interfaz, obteniendo la interfaz una vez
00:00:05.3163224
Comprobación de Acción (delegado) llama, conseguir la acción en cada llamada
00: 00: 17.1807980
Comprobación de Acción (delegado) llama, conseguir la acción una vez
00: 00: 05,3163224
Comprobación de Acción (delegado) sobre un método de interfaz, tanto en conseguir cada llamada
00: 03: 50,7326056
Comprobación de Acción (delegado) a través de una método de interfaz n, consiguiendo la interfaz vez, el delegado en cada llamada
00: 03: 48,9141438
Comprobación de Acción (delegado) sobre un método de interfaz, consiguiendo ambos una vez
00: 00: 04,0036530
Como puede ver, las llamadas directas son realmente rápidas. Almacenar la interfaz o delegar antes, y luego solo llamar es muy rápido. Pero tener que conseguir un delegado es más lento que tener que obtener una interfaz. Tener que obtener un delegado sobre un método de interfaz (o método virtual, no estoy seguro) es realmente lento (compare los 5 segundos de obtener un objeto como interfaz con los casi 4 minutos de hacer lo mismo para obtener la acción).
El código que ha generado estos resultados está aquí:
using System;
namespace ActionVersusInterface
{
public interface IRunnable
{
void Run();
}
public sealed class Runnable:
IRunnable
{
public void Run()
{
}
}
class Program
{
private const int COUNT = 1700000000;
static void Main(string[] args)
{
var r = new Runnable();
Console.WriteLine("To get real results, compile this in Release mode and");
Console.WriteLine("run it outside Visual Studio.");
Console.WriteLine();
Console.WriteLine("Checking direct calls twice");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
r.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
r.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking interface calls, getting the interface at every call");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
IRunnable interf = r;
interf.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking interface calls, getting the interface once");
{
DateTime begin = DateTime.Now;
IRunnable interf = r;
for (int i = 0; i < COUNT; i++)
{
interf.Run();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) calls, getting the action at every call");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
Action a = r.Run;
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) calls, getting the Action once");
{
DateTime begin = DateTime.Now;
Action a = r.Run;
for (int i = 0; i < COUNT; i++)
{
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) over an interface method, getting both at every call");
{
DateTime begin = DateTime.Now;
for (int i = 0; i < COUNT; i++)
{
IRunnable interf = r;
Action a = interf.Run;
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) over an interface method, getting the interface once, the delegate at every call");
{
DateTime begin = DateTime.Now;
IRunnable interf = r;
for (int i = 0; i < COUNT; i++)
{
Action a = interf.Run;
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.WriteLine();
Console.WriteLine("Checking Action (delegate) over an interface method, getting both once");
{
DateTime begin = DateTime.Now;
IRunnable interf = r;
Action a = interf.Run;
for (int i = 0; i < COUNT; i++)
{
a();
}
DateTime end = DateTime.Now;
Console.WriteLine(end - begin);
}
Console.ReadLine();
}
}
}
no estoy claro en lo que estás pidiendo ... devoluciones de llamada de interfaces _are_ delegados. –
Ver http://stackoverflow.com/questions/1269452/is-using-delegates-excessively-a-bad-idea-for-performance y http://stackoverflow.com/questions/304770/does-using-delegates- slow-down-my-net-programs - posibles duplicados? El delegado – itowlson
es necesario si está ejecutando subprocesos separados y necesita interactuar con el subproceso de interfaz de usuario ... por lo que debe refinar su pregunta para que sea más localizada y menos genérica. –