¿Memcpy es más eficiente que copiar elemento por elemento en bucle iterativo?

Te sorprenderías, ¡pero el compilador a menudo convierte tu ciclo de copia básico en memcpy por sí solo!

Ver la prueba:

rep ~ $ cat aa.cpp
#include
usando el espacio de nombres estándar;
int main ()
{
int buf [60000], buf2 [60000];
para (int i = 0; i <60000; ++ i) buf2 [i] = buf [i];

// Todo el código a continuación es simplemente una forma complicada de engañar al compilador en
// generar código en lugar de optimizar todo
// Tenemos que usar tanto buf1 como buf2 y algunos cálculos que
// el compilador no puede razonar y convertirlo en una constante
int k = 0;
para (int i = 0; i <60000; ++ i) buf [i] = i * buf [i];
para (int i = 0; i <60000; ++ i) k + = buf [i] + buf2 [i];
cout << k << endl;
}

El ensamblador se ve así:

rep ~ $ g ++ -O3 -S aa.cpp
rep ~ $ cat aa.s
.archivo “aa.cpp”
.section .text.un probable, “ax”, @ progbits
.LCOLDB2:
.section .text.startup, “ax”, @ progbits
.LHOTB2:
.p2align 4,, 15
.globl principal
.type main, @function
principal:
.LFB1024:
.cfi_startproc
subq $ 480008,% rsp
.cfi_def_cfa_offset 480016
movl $ 240000,% edx
leaq 240000 (% rsp),% rdi
movq% rsp,% rsi
llamar a memcpy
movdqa .LC1 (% rip),% xmm4
leaq 240000 (% rsp),% rdx
movq% rsp,% rax
movdqa .LC0 (% rip),% xmm3

…recorte…..

¿Mira eso?
Redujo un bucle en una memcpy ()

Los compiladores son muy inteligentes: a menos que seas un genio, nunca hay uso de hipotéticas micro optimizaciones.

Desde un punto de vista de legibilidad, es mejor llamar a funciones que escribir bucles, así que use std :: copy o std :: fill o lo que sea.

Casi con seguridad a menos que la biblioteca esté realmente mal escrita. memcpy () puede optimizarse para hardware específico para que pueda aprovechar cosas como caché y canalizaciones. También probablemente desenrolla el bucle. También puede hacerlo usted mismo si insiste en usar el bucle.

Para el caché, desea mover una línea de caché completa a la vez, ya que eso es lo que hará el procesador (o el controlador de caché) Desenrollar significa copiar más de una palabra para cada bucle, por ejemplo, este es el dispositivo de Duff:

enviar (a, desde, contar)
registrarse corto * a, * desde;
registro de conteo;
{
registro n = (cuenta + 7) / 8;
interruptor (cuenta% 8) {
caso 0:
hacer {
* a ++ = * desde ++;
caso 7:
* a ++ = * desde ++;
caso 6:
* a ++ = * desde ++;
caso 5:
* a ++ = * desde ++;
caso 4:
* a ++ = * desde ++;
caso 3:
* a ++ = * desde ++;
caso 2:
* a ++ = * desde ++;
caso 1:
* a ++ = * desde ++;
} while (–n> 0);
}
}

Esto es más rápido porque solo necesita disminuir y verificar el contador una vez cada ocho copias (excepto los primeros bytes si n! = 0). La versión de biblioteca optimizada podría implementar algo como esto, en ensamblador. En algunos procesadores DSP, las cosas pueden ser aún más complejas, ya que tienen estructuras diseñadas específicamente para el movimiento rápido de datos. Por ejemplo, los procesadores DSP de Texas Instrument tienen un SPLOOP que hace que los bucles se ejecuten simultáneamente en varias tuberías.

También es un código horrible, así que no lo use a menos que sea la única solución (solo use memcpy (), o si es necesario y posible, transferencias DMA).

SPLOOP: Página en ti.com.cn, capítulo 7.

memcpy tiene trucos bajo la manga que un bucle simple ni siquiera está optimizado / vectorizado / desenrollado por el compilador (como la reasignación de páginas VM), pero algunos compiladores modernos realmente reconocen bucles simples de copia de memoria y los compilan en llamadas a memcpy ( clang), __intel_fast_memcpy (intel), lo que tienes.

Sí. memcpy es un lenguaje ensamblador especialmente optimizado que generalmente lee cuatro u ocho bytes a la vez. También se ocupará adecuadamente del acceso no alineado.

Pero lo mejor es probarlo usted mismo. Haga un pequeño programa de prueba que copie 100 MB usando cualquier método. Entonces verás gor tú mismo.

More Interesting

¿Cuál es el enfoque para encontrar un acuerdo que produzca el salario mínimo?

Cómo verificar si la suma de los números de la primera mitad y la segunda mitad de una matriz es la misma

En el algoritmo de coincidencia del patrón de fuerza bruta cuando todos los caracteres en el patrón son únicos, entonces la fuerza bruta se puede implementar en la complejidad Big-oh (n) donde n es la longitud de la cadena (referencia: introducción a los algoritmos). ¿Alguien puede ayudarme con el algoritmo? Gracias por adelantado

¿En qué sitio web debo buscar gráficos en la estructura de datos?

Dada una matriz que contiene enteros distintos, ¿cuál es el número promedio de veces que se establece el valor máximo del elemento al encontrarlo?

¿Qué algoritmo usa Matlab para calcular las raíces de un polinomio de alto rango?

¿Cómo funciona la recursividad en el árbol de búsqueda binaria en orden? ¿Cómo se pueden explicar las llamadas recursivas, sin resumirlas como llamadas de pila?

¿Cuál es la forma más eficiente de representar una matriz binaria dispersa?

¿Hay algún algoritmo de compresión de texto con pérdida?

¿Cuáles son algunos algoritmos o sistemas de mejora automática?

Cómo elegir un elemento único de una lista dentro de un bucle en R

¿Cuál es la diferencia entre hashing y encriptación?

¿Cómo resolver la pregunta 1 de ZIO2015? ¿Es un enfoque de programación dinámica?

¿Qué estructuras de datos y algoritmos de programación heredados se enseñan en la universidad pero que no se usan después de la academia? ¿Aún debemos aprenderlos?

¿Por qué chupo la programación (algoritmos de programación dinámica en particular)?