Anzeige:
Ergebnis 1 bis 11 von 11

Thema: Memcpy oder nicht

  1. #1
    Registrierter Benutzer
    Registriert seit
    02.02.2006
    Beiträge
    41

    Memcpy oder nicht

    Hallo zusammen,

    ich habe da mal ne grundsätzliche Frage.
    Wann benutzt man eigentlich memcpy?
    Konkret geht's bei mir darum, dass ich eine double Matrix kopieren möchte.

    Aktuell kopiere ich so...
    Code:
    double A[50][50];
    double B[50][50];
    int M =50;
    for (i=0;i<M*M;i++){
      B[i] = A[i];
    }
    Wäre da memcpy schneller?

    Andi

  2. #2
    Registrierter Benutzer Avatar von jeebee
    Registriert seit
    01.01.2005
    Ort
    Bern || Zürich
    Beiträge
    540
    memcpy ist üblicherweise einiges schneller als ein "normaler" for-loop:
    Code:
    #include <stdio.h>
    #include <string.h>
    #include "cyc.h"
    
    double a[50][50];
    double b[50][50];
    
    void do_memcpy() {
    	memcpy(b, a, 50*50*sizeof(double));
    }
    
    void do_loop() {
    	int elem_count = 50*50;
    	int i = 0;
    	double *A = &a[0][0];
    	double *B = &b[0][0];
    	for (i=0; i<elem_count;i++) {
    		B[i]=A[i];
    	}
    }
    
    int main() {
    	// init cycle counter
    	start_counter();
    
    	// init cache
    	double start_cyc = get_counter();
    	do_loop();
    	double warmup_time = get_counter() - start_cyc;
    
    	// measure loop performance
    	start_cyc = get_counter();
    	do_loop();
    	double loop_time = get_counter() - start_cyc;
    
    	// measure memcpy performance
    	start_cyc = get_counter();
    	do_memcpy();
    	double memcpy_time = get_counter() - start_cyc;
    
    	printf("Copying a 50x50 matrix of doubles:\n"
    	       "    using a for-loop: %.0lf cycles\n"
    	       "    using memcpy:     %.0lf cycles\n",
    	       loop_time, memcpy_time);
    
    	printf("\nCache warmup: %.0lf cycles\n", warmup_time);
    
    	return 0;
    }
    Resultate bei mir (Athlon X2 BE-2400, 512k cache):
    Code:
    Copying a 50x50 matrix of doubles:
        using a for-loop: 47689 cycles
        using memcpy:     17349 cycles
    
    Cache warmup: 97446 cycles
    Die Resultate ändern nur unwesentlich wenn zuerst die memcpy cycles und dann die for-loop cycles gemessen werden.

    cycle counter implementation:
    Code:
    // cyc.h
    #ifndef __CYC_H
    #define __CYC_H
    
    double get_counter();
    void start_counter();
    
    #endif
    Code:
    // cyc.c
    static unsigned cyc_hi = 0;
    static unsigned cyc_lo = 0;
    
    void access_counter(unsigned *hi, unsigned *lo) {
    	/* Get cycle counter */
    	asm("rdtsc; movl %%edx, %0; movl %%eax, %1"
    		: "=r" (*hi), "=r" (*lo)
    		: /* No input */
    		: "%edx", "%eax");
    }
    
    double get_counter() {
    	unsigned ncyc_hi, ncyc_lo;
    	unsigned hi, lo, borrow;
    	/* Get cycle counter */
    	access_counter(&ncyc_hi, &ncyc_lo);
    	/* 64 bit subtraction */
    	lo = ncyc_lo - cyc_lo;
    	borrow = lo > ncyc_lo;
    	hi = ncyc_hi - cyc_hi - borrow;
    	return (double) hi * (1 << 30) * 4 + lo;
    }
    
    void start_counter() {
    	/* Get current value */
    	access_counter(&cyc_hi, &cyc_lo);
    	return;
    }
    my very own 128 bit integer
    C4 D3 B8 A8 9E A0 C6 EC 7D EC A8 15 28 D1 92 58
    more information

  3. #3
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Wann benutzt man eigentlich memcpy?
    Wenn man einen Block von Bytes kopieren möchte, wobei das Ziel nicht mit dem Original überlappt.
    Code:
    double A[50][50];
    double B[50][50];
    int M =50;
    for (i=0;i<M*M;i++){
      B[i] = A[i];
    }
    Es gibt Zweifel daran, dass deine Schleife korrekt ist. Du indizierst A und B außerhalb ihrer Grenzen. Dass ist nicht erlaubt. Es gibt zwar eine Regel die besagt, dass Array Elemente "dicht gepackt" werden, dennoch gibt es umfangreiche Diskussionen darüber ob damit die erste Regel außer Kraft gesetzt wird.

  4. #4
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Code:
    double A[50][50];
    double B[50][50];
    int M =50;
    for (i=0;i<M*M;i++){
      B[i] = A[i];
    }
    Das ist natürlich noch falscher als mir vorhin aufgefallen ist... erstens kompiliert es nicht und zweitens, wenn es das täte würde es *auf jeden Fall* die Arraygrenzen verletzen.

  5. #5
    Registrierter Benutzer
    Registriert seit
    02.02.2006
    Beiträge
    41
    Oh, da habe ich wohl einige Fehler beim Erstellen meines Beispiels gemacht. Also,
    hier ist das Original, wobei ich ein Matlab-Mex-File (C-API von Matlab) erstellt habe.
    Die hier verwendeten Datentypen haben immer den Anhang "_T", damit Matlab Plattformübergreifend kompilieren kann.
    Code:
    int M;
    real_T *A;
    real_T *B; 
    
    M = 50;
    A=mxCalloc((M*M), sizeof(real_T)); // analog zu calloc
    B=mxCalloc((M*M), sizeof(real_T));
    
    // Daten fuellen
    for (i=0;i<(M*M);i++){
     A[i] = 1.0/i;
    }
    
    // Nun das eigentliche kopieren was ich durch memcpy ersetzen möchte
    for (i=0;i<(M*M);i++){
     B[i] = A[i];
    }
    mxFree(A);
    mxFree(B);
    Hoffe das funz nun.
    Andi

  6. #6
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Zitat Zitat von Andi_Rostock Beitrag anzeigen
    Oh, da habe ich wohl einige Fehler beim Erstellen meines Beispiels gemacht. Also,
    hier ist das Original, [...]
    Code:
    int M;
    real_T *A;
    real_T *B; 
    
    M = 50;
    A=mxCalloc((M*M), sizeof(real_T)); // analog zu calloc
    B=mxCalloc((M*M), sizeof(real_T));
    
    // Daten fuellen
    for (i=0;i<(M*M);i++){
     A[i] = 1.0/i;
    }
    
    // Nun das eigentliche kopieren was ich durch memcpy ersetzen möchte
    for (i=0;i<(M*M);i++){
     B[i] = A[i];
    }
    Das ist in der Tat deutlich anders, weil du jetzt keine deklarierten Arrays, sondern angeforderten Speicher hast. Und soweit ist das auch korrekt. Allerdings hast du hier auch noch eine Division durch Null drin. Die muss raus.

  7. #7
    Registrierter Benutzer
    Registriert seit
    02.02.2006
    Beiträge
    41
    Erstmal sorry für die sehr verspätete Antwort. Ich war letzte Woche auf Reisen, daher konnte ich nicht weiter nachschauen.
    Natürlich hast Du Recht. Die Division durch Null darf nicht sein. Es war aber nur Teil des hier vorgestellten Beispiels. Ich korrigiere es nun zu:
    Code:
    int M;
    real_T *A;
    real_T *B; 
    
    M = 50;
    A=mxCalloc((M*M), sizeof(real_T)); // analog zu calloc
    B=mxCalloc((M*M), sizeof(real_T));
    
    // Daten fuellen
    for (i=0;i<(M*M);i++){
     A[i] = 1.0/(i*0.5+1);
    }
    
    // Nun das eigentliche kopieren was ich durch memcpy ersetzen möchte
    for (i=0;i<(M*M);i++){
     B[i] = A[i];
    }
    mxFree(A);
    mxFree(B);
    Hoffentlich stimmts nun. Aber ... bzgl. meiner Ausgangsfrage: Ist memcpy nun besser oder net?

  8. #8
    Registrierter Benutzer Avatar von jeebee
    Registriert seit
    01.01.2005
    Ort
    Bern || Zürich
    Beiträge
    540
    siehe post #2 für code.

    Resultate bei mir (Athlon X2 BE-2400, 512k cache):
    Code:
    gcc -o memcpy_test memcpy_test.c cyc.c
    Copying a 50x50 matrix of doubles:
        using a for-loop: 47689 cycles
        using memcpy:     17349 cycles
    
    Cache warmup: 97446 cycles
    -------
    gcc -O1 -o memcpy_test memcpy_test.c cyc.c
    Copying a 50x50 matrix of doubles:
        using a for-loop:      26581 cycles
        using a 4way for-loop: 23944 cycles
        using memcpy:          17232 cycles
    
    Cache warmup: 76411 cycles
    -------
    gcc -O2 -o memcpy_test memcpy_test.c cyc.c
    Copying a 50x50 matrix of doubles:
        using a for-loop:      19049 cycles
        using a 4way for-loop: 12077 cycles
        using memcpy:          17602 cycles
    
    Cache warmup: 125178 cycles
    -------
    gcc -O3 -o memcpy_test memcpy_test.c cyc.c
    Copying a 50x50 matrix of doubles:
        using a for-loop:      14227 cycles
        using a 4way for-loop: 13680 cycles
        using memcpy:          16912 cycles
    
    Cache warmup: 66657 cycles
    --> for-loop ist optimiert schneller...
    my very own 128 bit integer
    C4 D3 B8 A8 9E A0 C6 EC 7D EC A8 15 28 D1 92 58
    more information

  9. #9
    Registrierter Benutzer
    Registriert seit
    02.02.2006
    Beiträge
    41
    Vielen Dank für deine Bemühungen!!
    Also kann ich die For-Loops eigentlich stehen lassen.
    Kannst Du evtl. auch mal für größere Matrizen testen? Vielleicht mal 1000x1000? Nur rein des Interesses wegen.

  10. #10
    Registrierter Benutzer Avatar von jeebee
    Registriert seit
    01.01.2005
    Ort
    Bern || Zürich
    Beiträge
    540
    kannst das ja eigentlich auch selbst, der Code ist ja in Post #2 komplett vorhanden...

    edit: Nur meine Schlussfolgerung war falsch, da ich nicht daran dachte die Optimierungen einzuschalten.
    my very own 128 bit integer
    C4 D3 B8 A8 9E A0 C6 EC 7D EC A8 15 28 D1 92 58
    more information

  11. #11
    Registrierter Benutzer
    Registriert seit
    02.02.2006
    Beiträge
    41
    Ja, habs gerade schnell getestet. Also ab einer Matrix-Größe oberhalb von 500x500 Elementen ist memcpy schneller.
    Das ist doch mal ne Aussage!!!

    Vielen Dank jeebee,
    Andi

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •