Speicherkopiererei
Von: , Frage gestellt am Mo, 30. Okt 2000
Meine Frage ist kurz aber essentiell: Warum verdammt ist memcpy so schnell? Können die zaubern?
Meine Frage ist kurz aber essentiell: Warum verdammt ist memcpy so schnell? Können die zaubern?
Meine Frage ist kurz aber essentiell: Warum verdammt ist
memcpy so schnell? Können die zaubern?
Kurze Antwort: RAM ist einfach schnell.
Die Festplatte braucht nicht bemüht werden...
Bruno
Meine Frage ist kurz aber essentiell: Warum verdammt ist
memcpy so schnell? Können die zaubern?
Kurze Antwort: RAM ist einfach schnell.
Die Festplatte braucht nicht bemüht werden...
Warum ist eine Schleife die ich selbst schreibe dann so viel langsamer?
Schleife:
BYTE* Zeiger = irgendeine Zieladresse;
BYTE* Zeiger2 = irgendeine Quelladresse;
for( int a = 0; a < Anzahl; a++ )
{
*( Zeiger ) = *( Zeiger2 );
Zeiger++;
Zeiger2++;
}
Warum ist eine Schleife die ich selbst schreibe dann so viel
langsamer?
Keine Ahnung, wie die memcpy() Funktion intern auf deiner Maschine implementiert wurde. Die werden das vermutlich direkt in Assembler-Code unter Ausnutzung aller Performance und Caching-Tricks gebaut haben.
Bruno
Meine Frage ist kurz aber essentiell: Warum verdammt ist
memcpy so schnell? Können die zaubern?
Die heutigen Prozessoren (d.h. eigentlich schon ab dem 386, wenn nicht schon der 286) haben hardwaremäßig implementierte Kopierbefehle (MOVSB, MOVSW, MOVSD), die naturgemäß schneller ablaufen. Siehe z.B. hier:
http://www.acc.umu.se/~john/TAE/appd/movsb.htm
Der Compiler wird memcpy darauf abgestimmt haben.
gruß
J.
Ich versuch euch mal klarzumachen, was es bedeuten würde, wenn man memcpy nochmal in dieser spitzenmäßigen Performance - Form kompilieren könnte, nachdem meine einige winzige Änderungen ( an der Zeigerarithmetik ) vorgenommen hat: Man könnte superschnelle Kopieraktionen schreiben, die auf eine bestimmte Programmsituation abgestimmt sind. Was ist z.B. wenn die Abstände zwischen den zu kopierenden Werten nicht 1 ( einfacher Zeigerinkrement ) sondern unregelmäßig sind? Ist das der Fall kann man memcpy bereits nicht mehr benutzen und man muß eigene, viel lansamere Funktionen entwerfen. Also: Könnte man memcpy entsprechend anpassen, ergäben sich ungeahnte Möglichkeiten. Bin nämlich momentan daran eine Texturierungsfunktion ( 3D ) zu schreiben. Mir ist jeder Performance - Schub zur Zeit recht. Irgendwelche Vorschläge, wie man die Idee realisieren könnte ( also die mit dem variierten memcpy )?
Ich versuch euch mal klarzumachen, was es bedeuten würde, wenn
man memcpy nochmal in dieser spitzenmäßigen Performance - Form
kompilieren könnte, nachdem meine einige winzige Änderungen (
an der Zeigerarithmetik ) vorgenommen hat:
Hmmm. Ich habe den Quellcode zu memcpy nicht hier; was ich über MOVSB geschrieben habe, war eine Vermutung ("Der Compiler wird memcpy darauf abgestimmt haben."). Wenn sie aber zutrifft, hast Du eher schlechte Karten, denn MOVSB und Derivate sind in der Hardware implementiert. Das bedeutet:
In Deiner Routine hast Du drei Variablen, die in jedem Schleifendurchlauf erhöht werden müssen (Zeiger, zeiger2 und a); bei MOVSB ist es nur ein Prozessorregister (CX, wen ich mich recht entsinne), dessen Inhalt auf die Quell- und Zielregister addiert wird. Das geht viel schneller. Aber Du kannst den Befehl MOVSB leider nicht verändern.
Ein paar Dinge kannst Du evtl. tun und ausprobieren, ob das evtl. schneller geht:
1. Programmier Deine Schleife nach MOVSB-Manier:
BYTE* Zeiger = irgendeine Zieladresse;
BYTE* Zeiger2 = irgendeine Quelladresse;
for( int a = 0; a < Anzahl; a++ )
{
*( Zeiger[a] ) = *( Zeiger2[a] );
}
Da fällt mir ein: Texturierungsfunktion? Arbeitest Du dabei im
Speicher der Grafikkarte? Da gibt es doch ganz andere Dinge zu
beachten. Ich kenne das zwar nur aus der vorsintflutlichen
VGA-Karte, aber schon diese hatte einen 4x breiteren und
wesentlich schnelleren Datenbus; bei internen
Kopieroperationen (innerhalb des Karten-RAM) geht alles
ratzfatz. Da gibt es spezielle Routinen, die im BIOS der Karte
implementiert sind. Vielleicht solltest Du die Doku der
Kartenhersteller zu Rate ziehen.
Da hast du absolut Recht. Ja, ich wühle im Speicher der Grafikkarte rum. Da ich das aber mit DirectX ( DirectDraw ) mache, kann ich selber bzgl. der speziellen Routinen im BIOS der Karte nichts mehr optimieren.
Manchmal wundert man sich tatsächlich, wie schnell kopiert werden kann. Auf der anderen Seite ist bei Raycasting - Techniken niemals genug Geschwindigkeit verfügbar, glaub's mir. Somit bleibt einem nur die Möglichkeit, an den Schleifen zu optimieren. Dabei fällt auf: Zeichnet man z.B. ein Rechteck auf den Bildschirm, muß man die Grafikdaten Zeilenweise in den Speicher der Karte kopieren. Nehme ich memcpy anstatt einer eigenen Schleife, kann ich manchmal die Geschwindigkeit verdoppeln ( ! ), obwohl meine Schleife nach meinem Kenntnisstand absolut optimiert ist ( in C++ ). Will ich nun aber texturieren, zeichne ich Dreiecke und hole Grafikdaten aus "gedachten" Dreiecken. Es ist klar, daß ich hier memcpy nicht mehr nutzen kann.
Übrigens war der Schleifentypus, den ich verwendet habe im Vergleich zu deiner Variante bei 307200 Pixel bei 16Bit Tiefe ( 640x480 ) um gute zehn Frames schneller. Aber danke für den Tipp. Ist das vielleicht compilerabhängig was schneller ist? Nur zum Test definiere ich manchmal einfach so Variablen an entsprechenden Stellen als register. Ich hab's auch schon an dem Punkt verwendet, wo du es mir geraten hast. Ein Performance - Unterschied ist nie festzustellen.
Florian
Übrigens war der Schleifentypus, den ich verwendet habe im
Vergleich zu deiner Variante bei 307200 Pixel bei 16Bit Tiefe
( 640x480 ) um gute zehn Frames schneller. Aber danke für den
Tipp. Ist das vielleicht compilerabhängig was schneller ist?
Nur zum Test definiere ich manchmal einfach so Variablen an
entsprechenden Stellen als register. Ich hab's auch schon an
dem Punkt verwendet, wo du es mir geraten hast. Ein
Performance - Unterschied ist nie festzustellen.
Die geschwindigkeitsunterschiede verschiedener Konstrukte ist Compilerabhängig, da viele Compiler hersteller Ihr eigenes Süppchen Kochen. Da hilft nur ein Profiler weiter.
Die Register anweisungen sind nur eine EMPFEHLUNG an den Compiler. Es kann sein das der die Variablen ohnehin schon ins Register verlagert hat, es kann aber auch sein das er es trotz Anweisung NICHT tun wird. Wie das mit Empfehlungen nun mal so ist...
Hallo Florian !
Bzgl. der Kopiererei ist ja schon eine Menge gesagt worden.
Vielleicht kannst Du mit folgender Loeschfunktion (mittels FPU) was anfangen:
void g_Memclear(g_void * dst, g_int bsize)
{
__asm
{
mov ecx,bsize
mov edi,dst
shr ecx,3
fldz // mit Null laden
cloop: fst qword ptr [edi]
add edi,8
dec ecx
jnz cloop
}
}
Renderst Du die einzelnen Frames im RAM ?
Auf AGP-Grafikkarten laeuft mein Softwarerenderer mit folgender Einstellungen am schnellsten:
DDSURFACEDESC surfdesc;
surfdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;
Falls Du an Source interessiert bist, maile mir !
Markus