Verwalteter nicht verwalteter Overhead

8

In .NET gibt es mehrere Stellen, an denen Sie verwalteten Code belassen und in den Bereich des nicht verwalteten a.k.a. systemeigenen Codes eingeben müssen. Um einige zu nennen:

  • externe DLL Funktionen
  • COM-Aufruf

Es gibt immer Kommentare über Overhead, die von einer Seite zur anderen springen, und meine Frage hier ist, ob irgendjemand genau den Overhead misst, der passiert, und erklären kann, wie er berechnet werden kann. Zum Beispiel kann byte[] in IntPtr oder sogar in byte* in .NET konvertiert werden und dem Marshaller beim Speichern einiger CPU-Zyklen helfen.

    
Daniel Mošmondor 16.08.2011, 05:46
quelle

2 Antworten

5

[Ich sehe, ich habe nicht wirklich die Frage beantwortet, wie Sie messen würden; Der beste Weg zur Messung ist nur mit etwas Instrumentierung, entweder mit den Instrumentierungsklassen (siehe: Ссылка ) oder sogar mit etwas so Einfachem, wie in einigen Timern zu platzieren, für welche Anrufe Sie interessiert sind. Also, in der gröbsten Form, als wir versuchten, den Performance-Hit zu finden, der zum Beispiel in unser Aufruf zwischen C # und ATL COM, wir würden einfach Timer um einen leeren Funktionsaufruf platzieren, wir würden einen Timer starten, in einer engen Schleife zwischen C # und der leeren ATL COM-Funktion laufen, genug Schleifen machen, um vernünftige Antworten zu erhalten zwischen Läufen, und dann machen Sie genau das gleiche in C ++. Dann ist der Unterschied zwischen diesen beiden Zahlen der Overhead, um den Anruf über diese Grenze zu tätigen.]

Ich habe wirklich keine festen Zahlen, aber ich kann aus früheren Erfahrungen beantworten, dass C # mit so wenig Aufwand wie möglich in C ++ ausgeführt wird, solange Sie die Dinge effizient verwenden. abhängig von der genauen Art von was Sie versuchen zu tun.

Ich habe an mehreren Anwendungen gearbeitet, die sehr große Mengen von Ultraschalldaten über sehr hohe Frequenzen (100 MHz-3GHz A / D-Karten) und bestimmte Dinge, wie Sie es vorgeschlagen haben, gesammelt haben dann als Zeiger verriegelt und als Puffer für die Daten übergeben werden; große Datenmengen übertragen und zur Abbildung verschiedener Teile verarbeiten).

Damals, als wir mit VB6 mit C ++ - Code kommunizierten, würden wir das C ++ in einfache ATL-COM-Objekte schreiben und Zeiger hin und her geben, wenn sie für Daten und Bilder benötigt werden. Wir praktizierten ähnliche Techniken viel später mit C # in VS.NET 2003. Außerdem habe ich hier eine Bibliothek für eine Frage geschrieben, die massive unmanaged Datenspeicherung ermöglicht, die Unterstützung für sehr große Arrays und Array-Operationen sowie sogar eine Menge bieten kann von LINQ-Typ-Funktionalität! Verwenden von Array-Feldern statt einer großen Anzahl von Objekten . (Hinweis: Es gibt einige Probleme mit der Referenzzählung, die in der neuesten Version waren, und ich habe noch nicht aufgespürt.)

Auch habe ich einige Schnittstellen mit ATL COM mit der FFTW-Bibliothek gemacht, um Hochleistungs-DSP mit gutem Effekt durchzuführen, obwohl diese Bibliothek nicht ganz bereit für Primetime ist, war es die Basis der Spike-Lösung, für die ich geschaffen habe Der Link oben, und der Spike gab mir die meisten Informationen, nach denen ich suchte, um meinen viel umfangreicheren nicht verwalteten Speicherzuordner und die schnelle Array-Verarbeitung zu vervollständigen, die sowohl extern zugewiesene als auch nicht verwaltete Zuordnungen vom unmanaged Heap unterstützen Ersetzen Sie die derzeit in der FFTW C # -Bibliothek vorhandene Verarbeitung.

Also, der Punkt ist, ich glaube, die Leistungseinbuße ist sehr übertrieben, besonders mit der Verarbeitungsleistung, die wir heutzutage haben. In der Tat können Sie eine sehr gute Leistung erzielen, wenn Sie nur darauf achten, einige der Fallstricke zu vermeiden (wie das Aufrufen vieler kleiner Grenzfunktionen anstelle der Übergabe von Puffern oder mehrfache Zuweisung von Strings und dergleichen), indem Sie C # alleine verwenden. Aber wenn es um Hochgeschwindigkeitsverarbeitung geht, kann C # immer noch die Rechnung für alle erwähnten Szenarien erfüllen. Braucht es etwas Voraussicht, ja, manchmal. Aber die Vorteile in Bezug auf Entwicklungsgeschwindigkeit, Wartungsfreundlichkeit und Verständlichkeit, die Zeit, die ich brauchte, um herauszufinden, wie ich die benötigte Leistung erzielen kann, waren immer viel weniger als die Zeit, die ich für die Entwicklung hauptsächlich oder vollständig in C ++ benötigt hätte. p>

Meine zwei Bits. (Oh, eine Einschränkung, ich erwähne ATL COM speziell, weil die Leistung, die Sie bei der Verwendung von MFC erlitten haben, nicht wert war. Wie ich mich erinnere, war es etwa zwei Größenordnungen langsamer beim Aufruf über eine MFC-COM-Objekt gegenüber der Schnittstelle auf der ATL, und nicht unseren Anforderungen. ATL auf der anderen Seite war nur eine Kleinigkeit langsamer als die äquivalente Funktion direkt in C ++ aufrufen. Sorry, ich erinnere mich nicht an bestimmte Zahlen aus, außer dass wir selbst bei den großen Mengen von Ultraschalldaten, die wir gesammelt und bewegt haben, keinen Engpass gefunden haben.)

Oh, ich habe Folgendes gefunden: Ссылка "Leistungstipps und Tricks in .NET-Anwendungen" . Ich fand dieses Zitat sehr interessant:

  

Um die Übergangszeit zu beschleunigen, versuchen Sie, P / Invoke zu verwenden, wenn Sie können.   Der Overhead ist so wenig wie 31 Anweisungen plus die Kosten von   Marshalling, wenn Data Marshalling erforderlich ist, und nur 8 andernfalls. COM   Interop ist viel teurer und benötigt mehr als 65 Anweisungen.

Beispieltitel: "Chunky Calls erstellen", "For Loops für String Iteration verwenden", "Asynchrone IO-Opportunities suchen".

Einige Snippets aus der referenzierten Fast Memory Library:

in MemoryArray.cs

%Vor%

von MemoryArrayEnumerator.cs

%Vor%     
shelleybutterfly 16.08.2011, 06:16
quelle
6

Die Adresse eines verwalteten Arrays ist tatsächlich möglich.

Zuerst müssen Sie das Array über System.Runtime.InteropServices.GCHandle anheften , damit der Garbage Collector das Array nicht bewegt. Sie müssen dieses Handle reservieren, solange der nicht verwaltete Code Zugriff auf das verwaltete Array hat.

%Vor%

Dann sollten Sie in der Lage sein, System.Runtime.InteropServices zu verwenden .Marshal.UnsafeAddrOfPinnedArrayElement um ein IntPtr zu einem beliebigen Element im Array zu bekommen.

%Vor%

Wichtig: Das Fixieren von Objekten stört den GC-Betrieb erheblich. Die Möglichkeit, Objekte im Heap zu verschieben, ist einer der Gründe, warum moderne GCs mit der manuellen Speicherverwaltung (etwas) mithalten können. Durch das Pinnen von Objekten im verwalteten Heap verliert der GC gegenüber der manuellen Speicherverwaltung einen Leistungsvorteil: ein relativ unfragmentierter Heap.

Wenn Sie also planen, diese Arrays für einige Zeit "auf der nicht verwalteten Seite" zu belassen, sollten Sie stattdessen eine Kopie des Arrays erstellen. Kopieren von Speicher ist überraschend schnell. Verwenden Sie die Methoden Marshal.Copy(*) , um von verwaltetem zu nicht verwaltetem Speicher zu kopieren und umgekehrt.

    
Christian Klauser 16.08.2011 06:22
quelle

Tags und Links