2009-04-21 15 views
59

Ich habe vor kurzem eine Schnittstelle erstellt, um den DataAccessProvider von unserer Business-Logik-Schicht zu unterscheiden. Mit diesem Ansatz können wir unsere Wahl von DataAccessProvider jederzeit ändern, indem Sie die Werte in der Web/App.Config ändern. (weitere Details können bei Bedarf angegeben werden).Wie langsam ist Reflection

Wie auch immer, wir verwenden Reflektion, um unsere DataProvider-Klasse zu erreichen, auf der wir arbeiten können.

/// <summary> 
/// The constructor will create a new provider with the use of reflection. 
/// If the assembly could not be loaded an AssemblyNotFoundException will be thrown. 
/// </summary> 
public DataAccessProviderFactory() 
{ 
    string providerName = ConfigurationManager.AppSettings["DataProvider"]; 
    string providerFactoryName = ConfigurationManager.AppSettings["DataProviderFactory"]; 
    try 
    { 
     activeProvider = Assembly.Load(providerName); 
     activeDataProviderFactory = (IDataProviderFactory)activeProvider.CreateInstance(providerFactoryName); 
    } 
    catch 
    { 
     throw new AssemblyNotFoundException(); 
    } 
} 

Aber jetzt frage ich mich, wie langsam Reflexion ist?

+4

Sicher wäre es trivial, einen Test-Kabelbaum zu erstellen, um dies zu bewerten? – marijne

+2

Wenn die Factory ein Singleton ist, wird Assembly.Load nur einmal aufgerufen? – CVertex

+0

http://stackoverflow.com/questions/25458/how-costly-is-net-reflection?rq=1 – nawfal

Antwort

72

In den meisten Fällen: mehr als schnell genug. Wenn Sie beispielsweise ein DAL-Wrapper-Objekt erstellen, wird die Zeit für die Erstellung des Objekts über die Reflektion minuscule verglichen mit der Zeit, die für die Verbindung mit einem Netzwerk benötigt wird. Eine Optimierung wäre also Zeitverschwendung.

Wenn Sie Reflexion in einer engen Schleife verwenden, gibt es Tricks, es zu verbessern:

  • Generika (mit einem Wrapper where T : new() und MakeGenericType)
  • Delegate.CreateDelegate (zu einem typisierten Delegaten, funktioniert nicht für Konstrukteure)
  • Reflection.Emit - hardcore
  • Expression (wie Delegate.CreateDelegate, aber flexibler und arbeitet für Konstrukteure)

Aber für Ihre Zwecke ist CreateInstance völlig in Ordnung. Bleiben Sie dabei und halten Sie die Dinge einfach.


Edit: während der Punkt, um die relative Performance bleibt, und während das Wichtigste, „es messen“ bleibt, sollte ich einige der oben klären. Manchmal ... es ist Angelegenheit. Zuerst messen. Allerdings, wenn Sie es zu langsam finden, möchten Sie möglicherweise etwas wie FastMember, die alle Reflection.Emit Code ruhig im Hintergrund, um Ihnen eine nette einfache API zu sehen; zum Beispiel:

var accessor = TypeAccessor.Create(type); 
List<object> results = new List<object>(); 
foreach(var row in rows) { 
    object obj = accessor.CreateNew(); 
    foreach(var col in cols) { 
     accessor[obj, col.Name] = col.Value; 
    } 
    results.Add(obj); 
} 

das ist einfach, aber wird sehr schnell sein. Im konkreten Beispiel erwähne ich über ein DAL-Wrapper-wenn Sie diese viel tun, sollten Sie so etwas wie dapper, was wiederum macht die ganzen Reflection.Emit Code im Hintergrund Ihnen die schnellstmöglichen, aber einfach zu bedienen API zu geben:

int id = 12345; 
var orders = connection.Query<Order>(
    "select top 10 * from Orders where CustomerId = @id order by Id desc", 
    new { id }).ToList(); 
+2

Wenn jemand sehen möchte, wie die Reflexion emittiert funktioniert für den Zugriff auf Felder (es ist nicht zu kompliziert) Siehe: http: //sharpanalytics.blogspot.de/2012/08/dynamic-methods-for-accessing-fields.html – SACO

+0

@Marc: \t Ich habe Reflexion verwendet, um Methode, Klassenname der aktuellen Methode zu erhalten, um den Fehler in try zu protokollieren -Fang. im Grunde um zu vermeiden, den Funktionsnamen beim Logging-Fehler hart zu codieren. Muss ich mir Sorgen machen? –

+1

@Sangram wahrscheinlich nicht, keine –

9
+5

Seien Sie nicht geschockt. Die längste gemessene Zeit betrug 22 Sekunden für eine Million Iterationen. 22 * Mikro * Sekunden pro Anruf für den schlimmsten Fall. Wenn Sie nicht eine große Anzahl dieser Objekte erstellen, ist das wirklich keine große Sache. Natürlich, wenn Sie * eine große Anzahl dieser Objekte erstellen, dann ist es vielleicht eine große Sache, aber wie Marc bemerkt, wird es immer noch von der Datenbankverbindung und den Abfragezeiten überschwemmt. Lassen Sie sich nicht durch "x-mal so langsame" Artikel aus der Ruhe bringen, es sei denn, Sie wissen, dass es performancekritisch ist. – itowlson

+0

Ich stimme zu, auch wenn es für die meisten Anwendungen langsamer ist, die Leistungseinbußen überwiegen nicht die Vorteile von Reflection. –

3

Anders als die Links in anderen Antworten zu verfolgen und sicherzustellen, dass Sie nicht "pathalogisch schlecht" Code schreiben, dann ist für mich die beste Antwort, es selbst zu testen.

Nur Sie wissen, wo Sie Flaschenhals sind, wie oft Ihr Reflexionscode Benutzer sein wird, ob der Reflexionscode in engen Schleifen usw. sein wird. Sie kennen Ihren Geschäftsfall, wie viele Benutzer auf Ihre Website zugreifen, was Perf Anforderungen sind.

Allerdings, angesichts der Codeschnipsel, die Sie hier gezeigt haben, wäre meine Vermutung, dass der Overhead der Reflexion kein großes Problem sein wird.

VS.NET-Webtest- und Leistungstestfunktionen sollten die Leistung dieses Codes ziemlich einfach messen lassen.

Wenn Sie keine Reflektion verwenden, wie wird Ihr Code aussehen? Welche Einschränkungen wird es haben? Es kann sein, dass Sie nicht mit den Einschränkungen leben können, mit denen Sie sich konfrontiert sehen, wenn Sie den Reflexionscode entfernen. Es könnte einen Versuch wert sein, diesen Code ohne die Reflexion zu entwerfen, um zu sehen, ob es möglich ist oder ob die Alternative wünschenswert ist.

5

Reflexion ist nicht so langsam. Der Aufruf einer Methode durch Reflektion ist etwa 3 mal langsamer als normal. Das ist kein Problem, wenn Sie dies nur einmal oder in unkritischen Situationen tun. Wenn Sie es 10'000 Mal in einer zeitkritischen Methode verwenden, würde ich erwägen, die Implementierung zu ändern.

+0

Wenn das stimmt, mag ich diese Aussage wirklich. "Das Aufrufen einer Methode durch Reflektion ist etwa dreimal langsamer als normal." Hast du Referenzen? –

+0

Uf meine Post ist etwa 3 Jahre alt, ich kann nicht erinnern, woher ich diese Informationen bekam. – Enyra

+0

Ich konvertierte eine Datenzugriffsebene von der FieldInfo und PropertyInfo GetValue SetValue in den kompilierten Ausdruck. Die Zeit, die zum Lesen von 30.000 Zeilen mit 40 Spalten benötigt wurde, ging von 4 Sekunden auf 1 Sekunde zurück. Bei einem direkten Vergleich ist die Reflexion jedoch beim Festlegen und Abrufen von Werten zwischen 200 und 250 Mal langsamer als ein kompilierter Ausdruck. – Loathing

16

Es ist langsamer im Vergleich zu nicht reflektierenden Code. Die wichtige Sache ist nicht, wenn es langsam ist, aber wenn es langsam ist, wo es zählt. Wenn Sie beispielsweise Objekte mithilfe der Reflektion in einer Webumgebung instanziieren, bei der die erwartete Konsistenz bis zu 10 KB betragen kann, ist dies langsam.

Wie auch immer, es ist gut, nicht im Voraus über die Leistung besorgt zu sein. Wenn sich die Dinge als langsam erweisen, können Sie sie immer beschleunigen, wenn Sie die Dinge richtig entworfen haben, so dass Teile, die Sie in Zukunft möglicherweise optimieren müssten, lokalisiert sind.

Sie können diesen berühmten Artikel, wenn Sie beschleunigen müssen:

Dynamic... But Fast: The Tale of Three Monkeys, A Wolf and the DynamicMethod and ILGenerator Classes

+1

Die URL scheint sich geändert zu haben, sie kann jetzt gefunden werden unter: http://www.codeproject.com/Articles/19513/Dynamic-But-Fast-The-Tale-of-Three-Monkeys-A- Wolf – GrandMasterFlush

0

I somethign ähnlich tat, bis ich das Spiel mit IoC gestartet. Ich würde eine Spring-Objektdefinition verwenden, um den Datenprovider anzugeben - SQL, XML oder Mocks!

+0

Spring.net ist durchaus in der Lage Abhängigkeiten zur Laufzeit zu aktualisieren. Wenn Sie die Konfigurationsdatei aktualisieren und eine Instanz von der Factory neu laden, erhalten Sie einen Verweis auf die aktualisierte Instanz. (Beachten Sie, dass dies nicht funktioniert, wenn Sie die Konfiguration aus app.config laden, nur wenn Sie eine separate Quell-XML-Datei verwenden. – Jacob

2

Ich dachte, ich würde einen schnellen Test machen, um zu demonstrieren, wie langsame Reflexion im Vergleich zu ohne ist.

Mit Reflexion

  • Instanziierungsanwendung 58 Objekte, indem sie durch jedes ihrer Attribute Iterieren und passende
  • Gesamtspielzeit: 52.254 ns

    while (reader.Read()) { 
        string[] columns = reader.CurrentRecord; 
        CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry(); 
        IEnumerable<PropertyInfo> rawPayFileAttributes = typeof(CdsRawPayfileEntry).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(CustomIndexAttribute))); 
        foreach (var property in rawPayFileAttributes) { 
         int propertyIndex = ((CustomIndexAttribute)property.GetCustomAttribute(typeof(CustomIndexAttribute))).Index; 
         if (propertyIndex < columns.Length) 
          property.SetValue(toReturn, columns[propertyIndex]); 
         else 
          break; 
        } 
    } 
    

Ohne Reflexion

  • Instanziierungsanwendung 58 Objekte durch ein neues Objekt
  • Gesamtspielzeit zu schaffen: 868 ns

    while (reader2.Read()) { 
         string[] columns = reader2.CurrentRecord; 
         CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry() { 
          ColumnZero = columns[0], 
          ColumnOne = columns[1], 
          ColumnTwo = columns[2], 
          ColumnThree = columns[3], 
          ColumnFour = columns[4], 
          ColumnFive = columns[5], 
          ColumnSix = columns[6], 
          ColumnSeven = columns[7], 
          ColumnEight = columns[8], 
          ColumnNine = columns[9], 
          ColumnTen = columns[10], 
          ColumnEleven = columns[11], 
          ColumnTwelve = columns[12], 
          ColumnThirteen = columns[13], 
          ColumnFourteen = columns[14], 
          ColumnFifteen = columns[15], 
          ColumnSixteen = columns[16], 
          ColumnSeventeen = columns[17] 
         }; 
        } 
    

Wenn auch nicht ganz fair, da auch die Reflexion hat abrufen ein spezifisches Attribut jeder Eigenschaft 58 * 18-mal über das Erstellen eines neuen Objekts durch Reflexion, aber es bietet zumindest eine gewisse Perspektive.