byte schnell von a nach b kopieren
-
Wenn das Zeug so groß ist, dann ist das einzige, was zählt, das Speicherzugriffsmuster. Ich hoffe, du schreibst es nicht deshalb in Assembler, weil du davon mehr Geschwindigkeit erhoffst.
-
ist die quelle ein stream, oder ist sie einfach nur zeilenweise organisiert?
eine möglichkeit, das problem anzugehen, wäre sie rekursiv aufzufassen:
spiegeln einer matrix an der hauptdiagonalen kann man auch dadurch erreichen, dass man die matrix in kleine handliche matrizen zerlegt, diese einzeln spiegelt und ihre position (anschliessend oder gleichzeitig) an der hauptdiagonalen spiegelt. das würde man dann am besten mit 8*8Matrizen machen, die man mittels aligned read lädt, dort vertauscht und anschliessend mittels non-temporal-store zurückschreibt. zum schluss das sfence nicht vergessen. einfach geht dass allerdings nur mit SSE2, da es nicht ganz trivial (aber möglich) ist, 8*8Matrizen zu spiegeln, wenn man bloss 8 mmx-register hat.
-
Wieso braucht man dann ein sfence?
-
Ja es hat was mit Speicherzugriff zu tun. Um genau zu sein: Ich kippe ein Bild um 90°.
Sicherlich ist momentan die Transferrate zum Speicher der Flaschenhals, aber vielleicht ändert sich das irgendwann.Stefan
-
Nein, nicht in absehbarer Zeit. Im Gegenteil, die Kluft zwischen CPU- und RAM-Frequenz wird immer größer.
-
Ringding schrieb:
Wieso braucht man dann ein sfence?
wir wollen doch sicherstellen, dass die daten auch tatsächlich im speicher gelandet sind, bevor sie verwendet werden. andernfalls kann es zu hässlichen und schwer debugbaren problemen kommen, spätestens in multiprozessorumgebungen, aber das ist gar nicht unbedingt nötig.
die l/s/mfence instruktionen sind generell zu verwenden, wenn man irgendwelche temporal-move befehlen arbeitet. die serialisierung ist dabei keine performance bremse, denn der schreib/lesevorgang, den man serialsiert, muss ja in jedem falle stattfinden, im übrigen benutzt man sie ja meist nur im zusammenhang mit grösseren datenblöcken.
-
Bei multithread-Systemen macht man soundso irgendeine Art von lock, wenn mehrere Threads auf den gleichen Speicher zugreifen. Das ist genauso gut. Also wäre nur der singlethread-Fall interessant. Wenn man einen non-temporal-store macht und danach im gleichen Thread die gleiche Speicheradresse ausliest, kann dann der Wert geliefert werden, der vorher dringestanden ist? Was physikalisch im Speicher steht, interessiert mich nicht wirklich, sondern nur das, was ein Lesebefehl mir liefert.
-
ich nehme an, in diesem falle ist es irrelevant, abgesehen davon, dass es ohne mfence (ja mfence, sfence war ein fehler von mir) möglicherweise langsamer ablaufen könnte. ein normales read lädt ja den speicherbereich in den cache, jedes non-temporal-store invalidiert diese cache zeile aber wieder. wenn also die abfolge so aussähe:
STORE - LOAD - STORE - LOAD (jeweils auf denselben speicherbereich) dann muss bei jedem LOAD der speicher neu in den cache geladen werden. ein rechtzeitiges mfence nach den stores würde dagegen dazu führen:
STORE - STORE - LOAD - LOAD hier kann also das zweite LOAD den cache benutzen, das führt also zu geringerer busaktivität und daher potentiell höherem durchsatz gerade bei operationen, die durch den speicherzugriff gebremst werden. wenn man kein mfence will, sollte man besser auf non-temporal store verzichten, andererseits ist dieses ja sehr wertvoll, wenn man es eben mit solchen operationen wie obigem matrixtransponieren zu tun hat, wenn diese matrix selbst sehr viel platz braucht und daher der platz im cache knapp wird.
-
Das ist mir nicht ganz geheuer, was du da schreibst. Die Zugriffsreihenfolge kann ja gerade über eine mfence-Grenze hinweg auf gar keinen Fall umgestellt werden.
-
Ringding schrieb:
Das ist mir nicht ganz geheuer, was du da schreibst. Die Zugriffsreihenfolge kann ja gerade über eine mfence-Grenze hinweg auf gar keinen Fall umgestellt werden.
das meinte ich doch:
was ist denn mit
movnti [ ecx ], eax movnti [ ecx + 4 ], ebx mov eax, [ ecx ] mov ebx, [ ecx + 4 ]
es gibt keine garantie, dass das store auf [ecx+4] vor dem load von [ecx] passiert, anders mit mfence:
movnti [ ecx ], eax movnti [ ecx + 4 ], ebx mfence mov eax, [ ecx ] mov ebx, [ ecx + 4 ]
EDIT: man denke sich ein hinreichend komplexes beispiel, bei dem store-load-forwarding nicht greifen würde; obiges beispiel ist evtl. zu einfach.
-
So schaut's vernünftig aus. Ich werd trotzdem mal ins Intel Manual schauen :). Man sollte ja mit mfence nicht gerade um sich werfen, weil es einfach eeeeewig dauert. Dagegen ist ein normaler Speicherzugriff ja geradezu so flink, dass er überhaupt nicht ins Gewicht fällt.