Ich habe einen Bildverarbeitungscode, der zwei mehrdimensionale Byte-Arrays (der gleichen Größe) durchläuft. Es nimmt einen Wert aus dem Quell-Array, führt eine Berechnung durch und speichert das Ergebnis in einem anderen Array.
%Vor%Die Schleife dauert derzeit ~ 11ms, was vermutlich auf den Zugriff auf die Byte-Array-Werte zurückzuführen ist, da die Berechnung ziemlich einfach ist (2 Multiplikationen und 1 Addition).
Gibt es etwas, was ich tun kann, um dies zu beschleunigen? Es ist ein zeitkritischer Teil meines Programms und dieser Code wird 80-100 Mal pro Sekunde aufgerufen, so dass jede Geschwindigkeitssteigerung, egal wie klein sie auch sein mag, einen Unterschied machen wird. Auch im Moment xSize = 768 und ySize = 576, aber das wird in Zukunft zunehmen.
Update : Dank Guffa (siehe Antwort unten) erspart mich der folgende Code 4-5ms pro Schleife. Es ist zwar unsicherer Code.
%Vor%Um einen echten speadup für diesen Code zu erhalten, müssen Sie Pointer verwenden, um auf die Arrays zuzugreifen, wodurch alle Indexberechnungen und die Überprüfung der Grenzen entfernt werden.
%Vor% Bearbeiten:
Feste Variablen können nicht geändert werden, daher habe ich Code hinzugefügt, um die Zeiger auf neue Zeiger zu kopieren, die geändert werden können.
Dies sind alles unabhängige Berechnungen. Wenn Sie also eine Multicore-CPU haben, sollten Sie in der Lage sein, durch die Parallelisierung der Berechnung einen gewissen Nutzen zu erzielen. Beachten Sie, dass Sie die Threads beibehalten und sie einfach weiterreichen müssen, da der Overhead der Thread-Erstellung dieses wahrscheinlich langsamer und nicht schneller macht, wenn die Threads jedes Mal neu erstellt werden.
Die andere Sache, die vielleicht funktioniert, ist das Arbeiten am Grafikprozessor. Sehen Sie sich diese Frage für einige Ideen an, zum Beispiel mit Beschleuniger .
Eine Option wäre die Verwendung von unsicherem Code: Fixieren des Arrays im Speicher und Verwenden von Zeigeroperationen. Ich bezweifle jedoch, dass die Geschwindigkeitserhöhung so dramatisch sein wird.
Eine Anmerkung: Wie geht es Ihnen? Wenn Sie DateTime verwenden, beachten Sie, dass diese Klasse eine schlechte Auflösung hat. Sie sollten eine äußere Schleife hinzufügen und die Operation zehn Mal wiederholen - ich wette, das Ergebnis ist weniger als 110ms.
%Vor%Da erscheint wird jede Zelle in der Matrix völlig unabhängig von den anderen berechnet. Vielleicht möchten Sie prüfen, ob mehr als ein Thread dies erledigt. Um die Kosten für das Erstellen von Threads zu vermeiden, könnten Sie einen Thread-Pool haben.
Wenn die Matrix von ausreichender Größe ist, könnte es eine sehr gute Geschwindigkeitsverstärkung sein. Auf der anderen Seite, wenn es zu klein ist, kann es nicht helfen (sogar verletzen). Einen Versuch wert aber.
Ein Beispiel (Pseudocode) könnte so aussehen:
%Vor%BEARBEITEN Michael Meadows erwähnt in einem Kommentar, dass plinq möglicherweise geeignet ist Alternative: Ссылка
Ich würde empfehlen, ein paar leere Tests durchzuführen, um herauszufinden, was Ihre theoretischen Grenzen sind. Nehmen Sie zum Beispiel die Berechnung aus der Schleife heraus und sehen Sie, wie viel Zeit gespart wird. Versuchen Sie, die doppelte Schleife durch eine einzelne Schleife zu ersetzen, die dieselbe Anzahl von Malen ausführt und wie viel Zeit gespeichert wird. Dann können Sie sicher sein, dass Sie den richtigen Weg zur Optimierung gehen (die zwei Pfade, die ich sehe, reduzieren die doppelte Schleife in eine einzige Schleife und arbeiten mit der Multiplikation [vielleicht wäre eine Nachschlagetabelle schneller]).
Sie leiden wahrscheinlich an Boundschecking. Wie Jon Skeet sagt, wird ein gezacktes Array anstelle eines multidimensionalen (das ist data[][]
anstelle von data[,]
) schneller, seltsam wie es scheinen mag.
Der Compiler optimiert
%Vor%durch Eliminieren der Bereichsüberprüfung pro Element. Aber es ist eine Art Spezialfall, es wird nicht dasselbe für GetLength ().
Aus demselben Grund war auch das Caching oder das Hochladen der Length-Eigenschaft (das Einfügen in eine Variable wie xSize) eine schlechte Sache, obwohl ich das mit Framework 3.5 nicht verifizieren konnte
Versuchen Sie, die x- und y-for-Schleifen für ein lineareres Speicherzugriffsmuster und (so) weniger Cache-Misses auszutauschen, so.
%Vor%Wenn Sie LockBits verwenden, um in den Image-Puffer zu gelangen, sollten Sie y in der äußeren Schleife und x in der inneren Schleife durchlaufen, so wie es im Speicher gespeichert ist (nach Zeile, nicht nach Spalte). Ich würde sagen, dass 11ms ziemlich verdammt schnell ist ...
Müssen die Bilddaten in einem mehrdimensionalen (rechteckigen) Array gespeichert werden? Wenn Sie stattdessen gezackte Arrays verwenden, können Sie möglicherweise feststellen, dass das JIT über mehr Optimierungen verfügt (einschließlich Entfernen der Überprüfung der Grenzen).
Wenn CurrentImageData und / oder AlphaImageData nicht jedes Mal geändert werden, wenn Sie Ihr Code-Snippet ausführen, können Sie das Produkt vor dem Ausführen des angezeigten Code-Snippets speichern und diese Multiplikation in Ihren Schleifen vermeiden.
Edit: Eine andere Sache, an die ich gerade gedacht habe: Manchmal sind Int-Operationen schneller als Byte-Operationen. Versetze dies mit deiner Prozessor-Cache-Nutzung (du erhöhst die Datenmenge beträchtlich und bestehst ein größeres Risiko eines Cache-Fehltreffers).
442,368 Additionen und 884.736 Multiplikationen für die Berechnung würde ich denken, dass 11ms tatsächlich extrem langsam auf einer modernen CPU ist.
Obwohl ich nicht viel über die Besonderheiten von .net weiß, weiß ich, dass Hochgeschwindigkeitsberechnung nicht seine starke Farbe ist. In der Vergangenheit habe ich Java-Anwendungen mit ähnlichen Problemen gebaut, ich habe immer C-Bibliotheken verwendet, um die Bild- / Audioverarbeitung zu machen.
Aus Hardware-Sicht möchten Sie sicherstellen, dass die Speicherzugriffe sequenziell sind, dh Sie durchlaufen den Puffer in der Reihenfolge, in der er im Speicher vorhanden ist. Sie müssen diese möglicherweise auch neu anordnen, sodass der Compiler verfügbare Anweisungen wie SIMD nutzt. Wie man das anstellt, wird am Ende von Ihrem Compiler abhängig sein und ich kann nicht auf vs.net helfen.
auf einem eingebetteten DSP würde ich ausbrechen
(AlphaImageData [x, y] * OneMinusAlphaValue) und (CurrentImageData [x, y] * AlphaValue) und verwenden SIMD-Anweisungen, um Puffer zu berechnen, möglicherweise parallel, bevor die Addition durchgeführt wird. vielleicht kleine Stücke genug, um die Puffer im Cache auf der CPU zu halten.
Ich glaube, dass alles, was Sie tun, einen direkteren Zugriff auf den Speicher / die CPU erfordert, als .net erlaubt.
Sie können auch die Mono-Laufzeitumgebung und ihre Simd-Erweiterungen betrachten. Vielleicht können einige Ihrer Berechnungen die SSE-Beschleunigung nutzen, da ich feststelle, dass Sie grundsätzlich Vektorberechnungen durchführen (ich weiß nicht, bis zu welcher Vektorgröße es eine Beschleunigung für die Multiplikation gibt, aber für einige Größen).
(Blogpost, der Mono.Simd ankündigt: Ссылка )
Natürlich würde das bei Microsoft .NET nicht funktionieren, aber vielleicht interessiert Sie etwas Experimentieren.
Interessanterweise sind Bilddaten häufig ziemlich ähnlich, was bedeutet, dass die Berechnungen wahrscheinlich sehr repetitiv sind. Haben Sie eine Nachschlagetabelle für die Berechnungen erstellt? Wann immer 0,8 mit 128 multipliziert wurde - Wert [80,128], den Sie auf 102,4 vorberechnet haben, haben Sie einfach nachgesehen? Sie handeln grundsätzlich Speicherplatz für CPU-Geschwindigkeit, aber es könnte für Sie arbeiten.
Wenn Ihre Bilddaten eine zu hohe Auflösung haben (und eine zu hohe Ziffer haben), ist das natürlich nicht praktikabel.
Tags und Links optimization .net c# image-processing