Optimierung der Array-Transponierungsfunktion

8

Ich arbeite an einer Hausaufgabe und habe stundenlang an meiner Lösung festgemacht. Das Problem, das wir bekommen haben, ist, den folgenden Code zu optimieren, so dass er schneller läuft, egal wie unordentlich er wird. Wir sollten Dinge wie das Ausnutzen von Cache-Blöcken und Schleifen-Abrollung verwenden.

Problem:

%Vor%

Was ich bisher habe:

%Vor%

Einige Ideen, bitte korrigieren Sie mich, wenn ich falsch liege:

Ich habe über Loop-Abrollung nachgedacht, aber ich denke nicht, dass das helfen würde, weil wir nicht wissen, ob die NxN-Matrix Primzahlen hat oder nicht. Wenn ich das überprüfen würde, würde das zu viele Berechnungen enthalten, die die Funktion verlangsamen würden.

Cache-Blöcke wären nicht sehr nützlich, denn egal, was wir tun, wir werden linear auf ein Array zugreifen (1,2,3,4), während wir auf das andere in Sprüngen von N zugreifen werden Funktion, um den Cache zu missbrauchen und schneller auf den src-Block zuzugreifen, es wird noch lange dauern, diese in die dst-Matrix zu plazieren.

Ich habe auch versucht, Zeiger anstelle von Array-Zugriffsmethoden zu verwenden, aber ich glaube nicht, dass das Programm tatsächlich in irgendeiner Weise beschleunigt.

Jede Hilfe würde sehr geschätzt werden.

Danke

    
Glen Takahashi 30.05.2012, 05:37
quelle

5 Antworten

7

Cache-Blockierung kann nützlich sein. Nehmen wir zum Beispiel an, dass wir eine Cache-Zeilengröße von 64 Bytes haben (was x86 an diesen Tagen verwendet). Für eine Matrix, die groß genug ist, um größer zu sein als die Cachegröße, passen wir also, wenn wir einen 16x16-Block transponieren (seit sizeof (int) == 4, also 16 ints in eine Cachezeile) ) Wir müssen 32 laden (16 von der Quellmatrix, 16 von der Zielmatrix, bevor wir sie verschmutzen können), Zeilen aus dem Speicher zwischenspeichern und weitere 16 Zeilen speichern (auch wenn die Speicher nicht sequentiell sind). Im Gegensatz dazu erfordert das Transponieren der äquivalenten 16 · 16 Elemente ohne Cachespeicherblock das Laden von 16 Cachezeilen aus der Quellenmatrix, aber 16 · 16 = 256 Cachezeilen, die geladen und dann für die Zielmatrix gespeichert werden.

    
janneb 30.05.2012, 09:14
quelle
3

Das Abrollen ist nützlich für große Matrizen.
Sie benötigen etwas Code, um mit überschüssigen Elementen umzugehen, wenn die Matrixgröße nicht ein Vielfaches der Zeiten ist, die Sie ausrollen. Aber dies wird außerhalb der kritischsten Schleife sein, so dass es sich für eine große Matrix lohnt.

In Bezug auf die Richtung der Zugriffe - es kann besser sein, linear zu lesen und in Sprüngen von N zu schreiben, als umgekehrt. Dies liegt daran, dass Leseoperationen die CPU blockieren, während Schreiboperationen dies nicht tun (bis zu einem bestimmten Grenzwert).

Weitere Vorschläge:
1. Können Sie die Parallelisierung verwenden? OpenMP kann helfen (obwohl, wenn Sie erwartet werden, Single-CPU-Leistung zu liefern, ist es nicht gut).
2. Zerlegen Sie die Funktion und lesen Sie sie, wobei Sie sich auf die innerste Schleife konzentrieren. Sie können Dinge finden, die Sie im C-Code nicht bemerken würden.
3. Das Verwenden von absteigenden Zählern (Anhalten bei 0) ist möglicherweise etwas effizienter als das Erhöhen von Zählern.
4. Der Compiler muss davon ausgehen, dass src und dst Aliasnamen haben (auf denselben oder überlappenden Speicher zeigen), was seine Optimierungsmöglichkeiten einschränkt. Wenn Sie dem Compiler irgendwie sagen könnten, dass sie sich nicht überschneiden können, könnte das eine große Hilfe sein. Ich bin mir jedoch nicht sicher, wie das geht (vielleicht verwende ich den restrict Qualifier).

    
ugoren 30.05.2012 06:34
quelle
1

Unordnung ist kein Problem, also: Ich würde jeder Matrix eine transposed -Flag hinzufügen. Dieses Flag zeigt an, ob das gespeicherte Datenfeld einer Matrix in normaler oder transponierter Reihenfolge interpretiert werden soll.

Alle Matrixoperationen sollten diese neuen Flags zusätzlich zu jedem Matrixparameter erhalten. Innerhalb jeder Operation implementieren Sie den Code für alle möglichen Kombinationen von Flags. Vielleicht können Makros hier überflüssiges Schreiben sparen.

In dieser neuen Implementierung schaltet die Matrixtransposition einfach das Flag um: Der für die Transponieroperation benötigte Raum und die Zeit sind konstant.

    
Peter G. 30.05.2012 09:58
quelle
0

Nur eine Idee, wie man das Abrollen implementiert:

%Vor%

Natürlich sollten Sie die Zählung (wie i*dim ) aus der inneren Schleife entfernen, wie Sie es bereits bei Ihren Versuchen getan haben.

Cache-Prefetch könnte für die Quellmatrix verwendet werden.

    
SKi 30.05.2012 09:03
quelle
0

Sie wissen das wahrscheinlich, aber register int (Sie sagen dem Compiler, dass es klug wäre, dies zu registrieren). Und wenn du den unsigned des int machst, mag das Dinge ein bisschen schneller machen.

    
fhtuft 30.05.2012 09:52
quelle

Tags und Links