Entendiendo el código fuente de memcpy()

7 minutos de lectura

Entendiendo el codigo fuente de memcpy
Angus

00018 void *memcpy(void *dst, const void *src, size_t len)
00019 {
00020         size_t i;
00021 
00022         /*
00023          * memcpy does not support overlapping buffers, so always do it
00024          * forwards. (Don't change this without adjusting memmove.)
00025          *
00026          * For speedy copying, optimize the common case where both pointers
00027          * and the length are word-aligned, and copy word-at-a-time instead
00028          * of byte-at-a-time. Otherwise, copy by bytes.
00029          *
00030          * The alignment logic below should be portable. We rely on
00031          * the compiler to be reasonably intelligent about optimizing
00032          * the divides and modulos out. Fortunately, it is.
00033          */
00034 
00035         if ((uintptr_t)dst % sizeof(long) == 0 &&
00036             (uintptr_t)src % sizeof(long) == 0 &&
00037             len % sizeof(long) == 0) {
00038 
00039                 long *d = dst;
00040                 const long *s = src;
00041 
00042                 for (i=0; i<len/sizeof(long); i++) {
00043                         d[i] = s[i];
00044                 }
00045         }
00046         else {
00047                 char *d = dst;
00048                 const char *s = src;
00049 
00050                 for (i=0; i<len; i++) {
00051                         d[i] = s[i];
00052                 }
00053         }
00054 
00055         return dst;
00056 }

Estaba pasando por una implementación de memcpy, para comprender en qué se diferencia del uso de un bucle. Pero no pude ver ninguna diferencia entre usar un bucle en lugar de memcpycomo memcpy usa bucle de nuevo internamente para copiar.

no pude entender if parte que hacen para números enteros – i < len/sizeof(long). ¿Por qué es necesario este cálculo?

  • ¿De dónde viene este código? He visto implementaciones memcpy mejor optimizadas…

    – Maxime Cheramy

    11 de julio de 2013 a las 11:08

  • @Maxime: ¿cómo lo sabría? ¡Ni siquiera conoce el procesador de destino (o el compilador para el caso)!

    -Olof Forshell

    11 de julio de 2013 a las 11:20


  • @Angus, a juzgar por su respuesta en stackoverflow.com/questions/11772553/… parece entender la alineación. los long es una palabra del procesador y la dirección debe estar alineada con la palabra del procesador para una copia más rápida (la mayoría de las arquitecturas hacen copias más rápidas en datos alineados). Si no puede hacerlo, hágalo lentamente, byte por byte. Hay buenas respuestas a continuación.

    – tontería

    11 de julio de 2013 a las 11:20

  • ¿Esta función no rompe las reglas de creación de alias (acceder a la memoria a través de un largo * que no se declaró necesariamente como largo) y, por lo tanto, es un comportamiento indefinido?

    – jcodificador

    11 de julio de 2013 a las 12:04

  • @jcoder la implementación de la implementación no tiene que seguir ninguna regla. Sería una violación si copiara este código en una función de su propio nombre y lo usara como tal.

    –MM

    21 de agosto de 2014 a las 19:47


No pude entender si parte lo hacen para números enteros. i < largo/tamaño de (largo). ¿Por qué es necesario este cálculo?

Porque están copiando palabras, no bytes individuales, en este caso (como dice el comentario, es una optimización: requiere menos iteraciones y la CPU puede manejar datos alineados con palabras de manera más eficiente).

len es el número de bytes para copiar, y sizeof(long) es el tamaño de una sola palabrapor lo que el número de elementos para copiar (es decir, iteraciones de bucle para ejecutar) es len / sizeof(long).

  • La copia alineada con Word en realidad no depende de la CPU. Depende de qué tan conectada esté la memoria RAM.

    – m0skit0

    11 de julio de 2013 a las 11:17


  • @ m0skit0 Diría que depende de la arquitectura: hay CPU que ni siquiera pueden acceder a datos en direcciones no alineadas, y hay otras que pueden acceder a bytes individuales en todas las direcciones, pero estos accesos pueden ser mucho más lentos que acceder a un dirección alineada

    – Andreas Fester

    11 de julio de 2013 a las 11:21


  • Sí, lo sé. Y precisamente eso es por lo cableada que está la memoria RAM 😉 En fin, esto es un detalle. Tu respuesta es correcta.

    – m0skit0

    11 de julio de 2013 a las 11:37


  • Probablemente debería haber dicho “arquitectura” en mi respuesta en lugar de “CPU”, como hiciste tú 🙂

    – Andreas Fester

    11 de julio de 2013 a las 11:45

para entender en qué se diferencia del uso de un bucle. Pero no pude ver la diferencia de usar un bucle en lugar de memcpy, ya que memcpy usa loop nuevamente internamente para copiar

Bueno, entonces usa un bucle. Tal vez otras implementaciones de libc no lo hagan así. De todos modos, ¿cuál es el problema/pregunta si usa un bucle? Además, como puede ver, hace más que un ciclo: verifica la alineación y realiza un tipo diferente de ciclo dependiendo de la alineación.

No pude entender si parte lo hacen para números enteros. i < largo/tamaño de (largo). ¿Por qué es necesario este cálculo?

Esto es verificar la alineación de palabras de memoria. Si las direcciones de origen y de destino están alineadas por palabra, y la copia de longitud es múltiplo del tamaño de palabra, entonces realiza una copia alineada por palabra (long), que es más rápido que usar bytes (char), no solo por el tamaño, sino también porque la mayoría de las arquitecturas hacen copias alineadas con palabras mucho más rápido.

  • lo que hago usando bucles también está alineado con palabras (int -> 4 bytes).

    – Angus

    12 de julio de 2013 a las 7:21

  • Solo vale la pena hacerlo por 4 bytes si se aplican las condiciones. De lo contrario, la copia de palabras podría ser más lenta que la copia de bytes. De hecho, en algunas arquitecturas, solo puede copiar zonas de memoria si están alineadas con las palabras; de lo contrario, es una excepción de hardware (por ejemplo, MIPS IIRC).

    – m0skit0

    12 de julio de 2013 a las 9:53


Entendiendo el codigo fuente de memcpy
huseyin tugrul buyukisik

len%sizeof(long) comprueba si está intentando copiar largos completos que no forman parte de long.

00035    if ((uintptr_t)dst % sizeof(long) == 0 &&
00036             (uintptr_t)src % sizeof(long) == 0 &&
00037             len % sizeof(long) == 0) {
00038 
00039                 long *d = dst;
00040                 const long *s = src;
00041 
00042                 for (i=0; i<len/sizeof(long); i++) {
00043                         d[i] = s[i];
00044                 }

comprueba la alineación y, si es cierto, copia rápidamente (sizeof(long) bytes a la vez).

00046    else {
00047                 char *d = dst;
00048                 const char *s = src;
00049 
00050                 for (i=0; i<len; i++) {
00051                         d[i] = s[i];
00052                 }
00053    }

esto es para las matrices desalineadas (copia lenta (1 byte a la vez))

for (i=0; i<len/sizeof(long); i++) {
    d[i] = s[i];
}

En este ciclo for, cada vez que un long se copia, hay un tamaño total de len para copiar, por eso necesita i<len/sizeof(long) como condición para terminar el bucle.

Estaba pasando por una implementación de memcpy, para comprender en qué se diferencia del uso de un bucle. Pero no pude ver ninguna diferencia entre usar un bucle en lugar de memcpy, ya que memcpy usa bucle de nuevo internamente para copiar.

Loop (sentencias de control) es uno de los elementos básicos adyacentes a if (sentencias de decisión) y algunas otras cosas similares. Entonces, la pregunta aquí no es sobre cuál es la diferencia entre el bucle normal y el uso memcpy.

memcpy solo ayuda en su tarea brindándole una llamada API lista para usar, en lugar de tener que escribir 20 líneas de código para una cosa insignificante. Si lo desea, puede optar por escribir su propio código para proporcionarle la misma funcionalidad.

El segundo punto, como ya se señaló anteriormente, es que, el mejoramiento proporciona entre long tipo de datos y otros tipos. Porque en long está copiar un bloque de datos a la vez lo que llamamos una palabra en lugar de copiar byte por byte, lo que llevaría más tiempo. En caso de largo, la misma operación que requeriría 8 iteraciones para completar, memcpy lo hace en una sola iteración copiando la palabra a la vez.

Entendiendo el codigo fuente de memcpy
Pankaj Suryawanshi

Como si ve el código ensamblador de memcpy, muestra que en el sistema de 32 bits, cada registro es de 32 bits y puede almacenar 4 bytes a la vez, si copia solo un byte en el registro de 32 bits, la CPU necesita un ciclo de instrucción adicional.

Si len/count está alineado en el múltiplo de 4, podemos copiar 4 bytes en un ciclo

    MOV FROM, R2
    MOV TO,   R3
    MOV R2,   R4
    ADD LEN,  R4
CP: MOV (R2+), (R3+) ; "(Rx+)" means "*Rx++" in C
    CMP R2, R4
    BNE CP

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad