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?

14 Antworten zu dieser Frage

  1. Antwort von nach 4 Minuten hilfreich
    Re: Speicherkopiererei

    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

    • Antwort von nach 8 Minuten hilfreich
      Re^2: Speicherkopiererei

      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++;
      }

      • Antwort von nach 22 Minuten hilfreich
        Re^3: Speicherkopiererei

        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

      • Antwort von nach 43 Minuten hilfreich
        Re^3: Bruno hat recht...

        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.

        • Antwort von nach einem Tag hilfreich
          Re^4: Bruno hat recht...

          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 )?

          • Antwort von nach einem Tag hilfreich
            Re^5: Keine wirkliche Lösung...

            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] );
            }
            

            Damit erhöhst Du auch nur einmal a, aber nicht die Zeiger. Der Compiler sollte hier indirekte Adressierung verwenden, dann ist das schneller.
            2. Deklariere a als register-Variable. Bin nämlich momentan daran eine
            Texturierungsfunktion ( 3D ) zu schreiben.
            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.

            Gruß

            J.

            • Antwort von nach einem Tag hilfreich
              Re^6: Keine wirkliche Lösung...

              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

            • Antwort von nach 2 Tagen hilfreich
              Re^7: Keine wirkliche Lösung...


              Ü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...

            • Antwort von nach 23 Tagen hilfreich
              Re^7: Keine wirkliche Lösung...

              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



Keine passende Antwort gefunden? Jetzt eigene Frage stellen!