libreria funciones biblioteca c++ performance memory allocation copying

c++ - funciones - memset



¿En qué casos debo usar memcpy sobre los operadores estándar en C++? (7)

La eficiencia no debe ser tu problema.
Escribe un código limpio y fácil de mantener.

Me molesta que tantas respuestas indiquen que memcpy () es ineficiente. Está diseñado para ser la forma más eficiente de copiar bloques de memoria (para programas C).

Así que escribí lo siguiente como una prueba:

#include <algorithm> extern float a[3]; extern float b[3]; extern void base(); int main() { base(); #if defined(M1) a[0] = b[0]; a[1] = b[1]; a[2] = b[2]; #elif defined(M2) memcpy(a, b, 3*sizeof(float)); #elif defined(M3) std::copy(&a[0], &a[3], &b[0]); #endif base(); }

Entonces, para comparar, el código produce:

g++ -O3 -S xr.cpp -o s0.s g++ -O3 -S xr.cpp -o s1.s -DM1 g++ -O3 -S xr.cpp -o s2.s -DM2 g++ -O3 -S xr.cpp -o s3.s -DM3 echo "=======" > D diff s0.s s1.s >> D echo "=======" >> D diff s0.s s2.s >> D echo "=======" >> D diff s0.s s3.s >> D

Esto dio como resultado: (comentarios agregados a mano)

======= // Copy by hand 10a11,18 > movq _a@GOTPCREL(%rip), %rcx > movq _b@GOTPCREL(%rip), %rdx > movl (%rdx), %eax > movl %eax, (%rcx) > movl 4(%rdx), %eax > movl %eax, 4(%rcx) > movl 8(%rdx), %eax > movl %eax, 8(%rcx) ======= // memcpy() 10a11,16 > movq _a@GOTPCREL(%rip), %rcx > movq _b@GOTPCREL(%rip), %rdx > movq (%rdx), %rax > movq %rax, (%rcx) > movl 8(%rdx), %eax > movl %eax, 8(%rcx) ======= // std::copy() 10a11,14 > movq _a@GOTPCREL(%rip), %rsi > movl $12, %edx > movq _b@GOTPCREL(%rip), %rdi > call _memmove

Se agregaron los resultados de tiempo para ejecutar lo anterior dentro de un bucle de 1000000000 .

g++ -c -O3 -DM1 X.cpp g++ -O3 X.o base.o -o m1 g++ -c -O3 -DM2 X.cpp g++ -O3 X.o base.o -o m2 g++ -c -O3 -DM3 X.cpp g++ -O3 X.o base.o -o m3 time ./m1 real 0m2.486s user 0m2.478s sys 0m0.005s time ./m2 real 0m1.859s user 0m1.853s sys 0m0.004s time ./m3 real 0m1.858s user 0m1.851s sys 0m0.006s

¿Cuándo puedo obtener un mejor rendimiento con memcpy o cómo me beneficio al usarlo? Por ejemplo:

float a[3]; float b[3];

es código:

memcpy(a, b, 3*sizeof(float));

más rápido que este?

a[0] = b[0]; a[1] = b[1]; a[2] = b[2];


Los beneficios de memcpy? Probablemente legibilidad. De lo contrario, tendrías que hacer una serie de asignaciones o tener un bucle for para copiar, ninguno de los cuales es tan simple y claro como hacer memcpy (por supuesto, siempre y cuando tus tipos sean simples y no requieran construcción / destrucción).

Además, memcpy generalmente está relativamente optimizado para plataformas específicas, hasta el punto de que no será mucho más lento que la asignación simple, y puede incluso ser más rápido.


Los compiladores optimizan específicamente las llamadas memcpy , al menos clang & gcc lo hace. Entonces deberías preferirlo donde sea que puedas.


No opte por micro optimizaciones prematuras como usar memcpy como este. El uso de la asignación es más claro y menos propenso a errores, y cualquier compilador decente generará un código adecuadamente eficiente. Si, y solo si, ha perfilado el código y encontrado que las asignaciones son un cuello de botella significativo, entonces puede considerar algún tipo de microoptimización, pero en general siempre debe escribir un código claro y sólido en primera instancia.


Puede usar memcpy solo si los objetos que está copiando no tienen constructores explícitos, así como sus miembros (el llamado POD, "Datos antiguos simples"). Entonces está bien llamar a memcpy para float , pero es incorrecto para, por ejemplo, std::string .

Pero parte del trabajo ya ha sido hecho para usted: std::copy from <algorithm> está especializado para tipos incorporados (y posiblemente para cualquier otro tipo de POD - depende de la implementación de STL). Así que escribir std::copy(a, a + 3, b) es tan rápido (después de la optimización del compilador) como memcpy , pero es menos propenso a errores.


Supuestamente, como dijo Nawaz, la versión de asignación debería ser más rápida en la mayoría de las plataformas. Esto se debe a que memcpy() copiará byte por byte, mientras que la segunda versión podría copiar 4 bytes a la vez.

Como siempre es el caso, siempre debe perfilar las aplicaciones para asegurarse de que lo que espera ser el cuello de botella coincida con la realidad.

Editar
Lo mismo se aplica a la matriz dinámica. Como mencionas C ++, deberías usar el algoritmo std::copy() en ese caso.

Editar
Esta es la salida de código para Windows XP con GCC 4.5.0, compilada con el indicador -O3:

extern "C" void cpy(float* d, float* s, size_t n) { memcpy(d, s, sizeof(float)*n); }

He hecho esta función porque OP especificó matrices dinámicas también.

El ensamblaje de salida es el siguiente:

_cpy: LFB393: pushl %ebp LCFI0: movl %esp, %ebp LCFI1: pushl %edi LCFI2: pushl %esi LCFI3: movl 8(%ebp), %eax movl 12(%ebp), %esi movl 16(%ebp), %ecx sall $2, %ecx movl %eax, %edi rep movsb popl %esi LCFI4: popl %edi LCFI5: leave LCFI6: ret

por supuesto, supongo que todos los expertos aquí saben qué rep movsb significa rep movsb .

Esta es la versión de asignación:

extern "C" void cpy2(float* d, float* s, size_t n) { while (n > 0) { d[n] = s[n]; n--; } }

que produce el siguiente código:

_cpy2: LFB394: pushl %ebp LCFI7: movl %esp, %ebp LCFI8: pushl %ebx LCFI9: movl 8(%ebp), %ebx movl 12(%ebp), %ecx movl 16(%ebp), %eax testl %eax, %eax je L2 .p2align 2,,3 L5: movl (%ecx,%eax,4), %edx movl %edx, (%ebx,%eax,4) decl %eax jne L5 L2: popl %ebx LCFI10: leave LCFI11: ret

Que mueve 4 bytes a la vez.


Use std::copy() . Como el archivo de encabezado para notas de g++ :

Esta función en línea se reducirá a una llamada a @c memmove siempre que sea posible.

Probablemente, Visual Studio no sea muy diferente. Vaya con la manera normal y optimice una vez que tenga conocimiento del cuello de una botella. En el caso de una copia simple, el compilador probablemente ya esté optimizando para usted.