memcpy?



  • Hallo,

    ich möchte gerne wissen wie der memcpy Befehl funktioniert.
    a.) ist es ein single CPU Befehl oder ist es eine Funktion die für z.B. 72000Bytes auch 72000(/4) CPU Befehle zum Kopieren im Speicher abschickt?
    b.) wie lange hält sich das im CPU Cache, wenn z.B. alle 33ms ein Copy mit 72kb ausgeführt wird?
    c.) gibt es derzeit effektivere Befehle um CPU Zeit zu sparen?

    Grüße
    TheNoName



  • kommt auf den compiler an und in wie fern du ihm freie hand lässt.

    falls du deinem compiler nicht traust und dort auch noch das letzte qäuntchen geschwindigkeit herausholen willst:
    http://www.asmcommunity.net/board/index.php?topic=12804.0

    vll hilft dir ads ja weiter.

    bb


  • Mod

    a) memcpy hat lineare Zeitkomplexität
    b) Kommt auf deine CPU an. Zeit spielt beim Cache aber eigentlich keine Rolle.
    c) Du kannst memcpy selbst implementieren, wenn du dich auf eine Zielplattform spezialisieren willst:
    http://www.eetimes.com/design/memory-design/4024961/Optimizing-Memcpy-improves-speed
    Du darfst aber davon ausgehen, dass die Version deines Compilers ziemlich gut ist.



  • Also eine X86 CPU kennt einen Befehl der Speicherbereiche Kopieren kann.

    rep movs
    

    Dieser wird aber in Moderne CPUs eh intern in kleine Befehle zerlegt.
    so etwa:

    while(Count--) *pDes++ = *pSrc++;
    

    Nehmen wir mal an das die CPU pro Kopiervorgang einen Takt braucht und gleichzeitig 4 Byte Transferiert, dann sind das bei 65KByte auf einem 2 GHz Prozessor. ca. 0,000008 sek.

    Bei einen Zyklus von 33 ms fällt diese Zeit von ~10 us gar nicht ins Gewicht und der Cache wird sicher (Auf einen Normalen Betriebssystem) immer Invalid sein.

    Lichtlein



  • b) memcpy führt unweigerlich zu Cache-Pollution, deswegen wird in den meisten Fällen ein Cache-Write-Through empfohlen.
    Damit kann die CPU/Chipsatz die Memory-Zugriffe noch optimieren.

    c) Hier ein Code-Beispiel vom AMD-Speed-Guide, funktioniert ab SSE2

    void nontemporal_copy(char* outbuff, char* inbuff, int size) 
    {
    const int step = 64; // cache line
    while(size > step) {
      _mm_prefetch(inbuff + 320, _MM_HINT_NTA); // non-temporal prefetch
      __m128i A = _mm_loadu_si128((__m128i*) (inbuff +  0));
      __m128i B = _mm_loadu_si128((__m128i*) (inbuff + 16));
      __m128i C = _mm_loadu_si128((__m128i*) (inbuff + 32));
      __m128i D = _mm_loadu_si128((__m128i*) (inbuff + 48));
    
      // destination address must be 16-byte aligned!
      _mm_stream_si128((__m128i*) (outbuff +  0), A);
      _mm_stream_si128((__m128i*) (outbuff + 16), B);
      _mm_stream_si128((__m128i*) (outbuff + 32), C);
      _mm_stream_si128((__m128i*) (outbuff + 48), D);
    
      inbuff  += step;
      outbuff += step;
      size -= step;
    }
    _mm_mfence();
    }
    

    Falls ein Cache-Write-Through nicht erwünsch ist, lohnt sich das prefetchen trotzdem.
    Je nach Größe der Cacheline der CPU kann man die Funktion noch anpassen, diese ist quasi hardcoded für 64byte.


Anmelden zum Antworten