Mejorar rendimiento de función memcpy

22/04/2005 - 12:24 por Josep Maria | Informe spam
Hola, estamos realizando un proceso que ejecuta copias de
bloques de memoria de un origen a un destino, con la
función memcpy.

Esta función dentro de nuestro proceso se llega a ejecutar
miles y miles de veces.

Si bien el tiempo de respuesta que obtenemos del proceso
es aceptable, queriamos saber si existe alguna función de
biblioteca C que mejore el rendimiento de esta función
memcpy, es decir, que haga a igual cantidad de bytes a
copiar de una zona de memoria a otra, lo haga más rapido.

Realmente, si lo encontramos mejoraría mucho el
rendimimento pues insisto, la función memcpy se está miles
y miles de veces en este proceso y una leve mejora en el
desempeño de esa función haría que globalmente tardase
mucho menos el proceso.

Gracias.

Preguntas similare

Leer las respuestas

#11 Zephryn Xirdal
23/04/2005 - 09:39 | Informe spam
On Sat, 23 Apr 2005 03:15:03 +0200, Rodrigo Corral [MVP]
wrote:

Mostrar la cita
Rodrigo, sacado de la versión profesional del VS 7.1 (ruta: E:\Archivos de
programa\Microsoft Visual Studio .NET 2003\Vc7\crt\src\memcpy.c)

/***
*memcpy.c - contains memcpy routine
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* memcpy() copies a source memory buffer to a destination buffer.
* Overlapping buffers are not treated specially, so propogation may
occur.
*
*******************************************************************************/

#include <cruntime.h>
#include <string.h>

#ifdef _MSC_VER
#pragma function(memcpy)
#endif /* _MSC_VER */

/***
*memcpy - Copy source buffer to destination buffer
*
*Purpose:
* memcpy() copies a source memory buffer to a destination memory
buffer.
* This routine does NOT recognize overlapping buffers, and thus can
lead
* to propogation.
*
* For cases where propogation must be avoided, memmove() must be
used.
*
*Entry:
* void *dst = pointer to destination buffer
* const void *src = pointer to source buffer
* size_t count = number of bytes to copy
*
*Exit:
* Returns a pointer to the destination buffer
*
*Exceptions:
*******************************************************************************/

void * __cdecl memcpy (
void * dst,
const void * src,
size_t count
)
{
void * ret = dst;

#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) ||
defined (_M_IA64)
{
extern void RtlMoveMemory( void *, const void *, size_t count );

RtlMoveMemory( dst, src, count );
}
#else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) ||
defined (_M_IA64) */
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
#endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)
|| defined (_M_IA64) */

return(ret);
}
#12 Josep Maria
23/04/2005 - 13:03 | Informe spam
Hola de nuevo, he estado siguiendo vuestras explicaciones
y se me ha ocurrido probar el siguiente ejemplo C y ver
como lo transform el Visual C++ en ensamblador:

El programita C es el siguiente, sencillo, sencillo:

char uno[10]="sdfsdfsdf";
char dos[10]="111111111";

memcpy(dos,uno,4);

Y al depurar y mirar el código ensamblador sale lo
siguiente, os pongo solamente el código de la instrucción
memcpy:

11: memcpy(dos,uno,4);
00401070 push 4
00401072 lea eax,[ebp-0Ch]
00401075 push eax
00401076 lea ecx,[ebp-18h]
00401079 push ecx
0040107A call memcpy (00407cc0)
0040107F add esp,0Ch

Si os fijais, en la penultima linea hay una llamada a un
procedimiento memcpy que es el siguiente:

intel\memcpy.asm -

memcpy:
00407CC0 push ebp
00407CC1 mov ebp,esp
00407CC3 push edi
00407CC4 push esi
00407CC5 mov esi,dword ptr [src]
00407CC8 mov ecx,dword ptr [count]
00407CCB mov edi,dword ptr [dst]
00407CCE mov eax,ecx
00407CD0 mov edx,ecx
00407CD2 add eax,esi
00407CD4 cmp edi,esi
00407CD6 jbe CopyUp (00407ce0)
00407CD8 cmp edi,eax
00407CDA jb CopyDown (00407e58)
CopyUp:
00407CE0 test edi,3
00407CE6 jne CopyLeadUp (00407cfc)
00407CE8 shr ecx,2
00407CEB and edx,3
00407CEE cmp ecx,8
00407CF1 jb CopyUnwindUp (00407d1c)
00407CF3 rep movs dword ptr [edi],dword ptr [esi]
00407CF5 jmp dword ptr [edx*4+407E08h]
CopyLeadUp:
00407CFC mov eax,edi
00407CFE mov edx,3
00407D03 sub ecx,4
00407D06 jb ByteCopyUp (00407d14)
00407D08 and eax,3
00407D0B add ecx,eax
00407D0D jmp dword ptr [eax*4+407D20h]
ByteCopyUp:
00407D14 jmp dword ptr [ecx*4+407E18h]
00407D1B nop
CopyUnwindUp:
00407D1C jmp dword ptr [ecx*4+407D9Ch]
00407D23 nop
00407D24 xor byte ptr [ebp+40h],bh
00407D27 add byte ptr [ebp+edi*2+40h],bl
00407D2B add byte ptr [eax+2300407Dh],al
00407D31 ror dword ptr [edx-75F877FAh],1
00407D37 inc esi
00407D38 add dword ptr [eax+468A0147h],ecx
00407D3E add al,cl
00407D40 jmp 02880547
00407D45 add esi,3
00407D48 add edi,3
00407D4B cmp ecx,8
00407D4E jb CopyUnwindUp (00407d1c)
00407D50 rep movs dword ptr [edi],dword ptr [esi]
00407D52 jmp dword ptr [edx*4+407E08h]
00407D59 lea ecx,[ecx]

y sigue y sigue ...


Esto da la impresión de que es larguísimo y la verdad, yo
recuerdo cuando hice ensamblador Intel x86 era tan
sencillo como las 4 instrucciones que se han puesto
anteriormente en otro mensaje.

Alguna idea ?
#13 Zephryn Xirdal
23/04/2005 - 13:50 | Informe spam
Lo más rápido sin salir del C, sería:

__inline void mi_memcpy(char *orig,char *dest,int cuenta)
{
while(cuenta--)
*dest++=*orig++;
}

Y deja al compilador que haga lo demás. Desconozco si el VC lleva
"register coloring" o paso de parámetros en registros, pero casi el código
generado debería ser muy parecido a mi post anterior. Si el compilador
genera toda la parrafada que has puesto más abajo, pues encontes hazla tu
en ensamblador, con inline si te deja o una macro si no:

__inline __asm mi_memcpyAsm(reg si:char *org,reg di:char *dest,reg ax:int
cuenta)
{
repnz
}
Mira a ver la semántica para pasar registros a una función de ensamblador,
pero no debería estar muy lejos de lo que he puesto.

Desde luego, si un compilador de los que uso me genera el código que
muestras abajo para un bucle while como el descrito, lo mando a tomar p*r
c*l*, le pongo un correo al fabricante riéndome de él y cambio de
compilador (como en su momento hice con el gnu para motorola, que incluso
era peor).


On Sat, 23 Apr 2005 13:03:33 +0200, Josep Maria
wrote:

Mostrar la cita
#14 Josep Maria
24/04/2005 - 01:01 | Informe spam
Estoy tratando de incluir código ensamblador en nuestro
programa C pero me da errores al linkar:


Esta es la función que he añadido al final de mi fichero C

__inline __asm mi_memcpyAsm(reg si:char *org,reg di:char
*dest,reg ax:int
cuenta)
{
repnz
}

y me da los siguientes errores:

Deleting intermediate files and output files for project
'12 - Win32 Debug'.
Debug--
Compiling...
12.cpp
c:\pruebac\12\12.cpp(17) : warning C4091: 'inline ' :
ignored on left of 'int' when no variable is declared
c:\pruebac\12\12.cpp(17) : error C2143: syntax error :
missing ';' before '__asm'
c:\pruebac\12\12.cpp(17) : error C2143: syntax error :
missing ';' before '__asm'
c:\pruebac\12\12.cpp(17) : error C2146: syntax error :
missing ';' before identifier 'di'
c:\pruebac\12\12.cpp(17) : fatal error C1004: unexpected
end of file found
Error executing cl.exe.

12.exe - 4 error(s), 1 warning(s)


Trabajo con Visual C++, como se introduce código
ensamblador pues ? Creo que esa será la solución final pues
visto el código poco optimizado de memcpy con 4
instrucciones de assembler estará hecho e irá más rapido.
Pero antes debo saber como incluir código ensamblador en el
código C.

He de poner algun archivo especial de cabecera con #include
al principio ?
#15 Zephryn Xirdal
24/04/2005 - 10:14 | Informe spam
Sigue abajo.

On Sun, 24 Apr 2005 01:01:16 +0200, Josep Maria
wrote:

Mostrar la cita
El código que puse fue meramente orientativo. El primer error es que estoy
usando registros de 16 bits en lugar de 32 bits: todos los registros
lleven una e delante, como "eax" o "esi". El segundo es que no recuerdo
bien si "repnz" trabaja con el acumulador como contador y esi y edi como
fuente/destino. Miralo en una referencia de ensamblador (seguro que en
internet hay muchas).
Ignoro si se permiten funciones en ensamblador e inlines. La ayuda del
compilador te lo dirá. El primer mensaje te está diciendo que va a ingorar
el iniline y que va a usar una función normal. Todos los demás se deben a
que no entiende nada de lo que se ha puesto.

Mira en la ayuda de la MSDN. Busca la palabra __inline, __asm e "inline
functions" y "assembler functions" a ver si aparece algo. Puedes buscar
también "parameters in register". De lo que estoy completamente seguro es
de que, al menos con el Borland, todo eso se podía hacer.
Ads by Google
Search Busqueda sugerida