Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Debate Sobre C++ Y Oop

Iniciado por Jare, 19 de Diciembre de 2005, 07:44:30 PM

« anterior - próximo »

zupervaca

 Estoy usando el .NET 2003 y por lo visto optimiza mucho mejor que el Visual C++ 6.0, increíble.
Te paso tu código modificado con otro método mucho mas rápido aun para copiar cadenas (DesplazarCadenaHyperSlide), es usando el memmove. También podrías usar el strcpy que iría igual de rápido que el memmove. La ventaja del memmove es que si se solapan lo resuelve copiando byte a byte, si no se solapan copia con las optimizaciones del memcpy.
He cambiado la función DesplazarCadena, ahora se le pasa también el tamaño, como se ve el strlen es una operación costosa por lo que siempre es mejor pasar el tamaño de las cadenas a la función.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <conio.h>
//--------------------------------------------------------------------------------
#define MAX 8000000 //100000000
//--------------------------------------------------------------------------------
void DesplazarCadenaHyperSlide (char * psz, int nLength, int nSlide)
{
if( psz != NULL && nLength > 0 )
{
 size_t nCount = nLength -nSlide +1;
 if( nCount > 0 )
 {
  memmove( psz, psz + nSlide, nCount );
 }
}
}
//--------------------------------------------------------------------------------
void DesplazarCadenaExtremo (char * cadena)
{
    __asm
    {
          mov  EBX, cadena

          cmp  BYTE PTR [EBX], 0
          jz   fin

inicio:
          mov  AL, BYTE PTR [EBX+1]
          mov  BYTE PTR [EBX], AL
          cmp  BYTE PTR [EBX+1], 0
          jz   fin
          inc  EBX
          jmp  inicio
fin:
    }
}
//--------------------------------------------------------------------------------
void DesplazarCadena (char * cadena, size_t tam)
{
    for(size_t i = 0; i < tam; i++)
          cadena[i] = cadena[i+1];
}
//--------------------------------------------------------------------------------
void DesplazarCadenaAP (char * cadena)
{
    while(*cadena)
    {
          *(cadena) = *(cadena+1);
          cadena++;
}
}
//--------------------------------------------------------------------------------
void SlideString( char *psz, size_t nLength, int nSlide, char *pszDest )
{
if( psz != NULL && nLength > 0 )
{
 size_t nCount = nLength -nSlide +1;
 if( nCount > 0 )
 {
  memcpy( pszDest, psz + nSlide, nCount );
 }
}
}
//--------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    clock_t ti, tf;
    int i;

    char * jose = (char *) malloc(20);
 strcpy(jose, "a mi me gusta luisa");
  size_t tamanio = strlen(jose);
 char *resultado = new char[tamanio];


    //----------------------------------------------------------------------------

    ti = ((clock() * 1000) / CLK_TCK);

    for(i = 0; i < MAX; i++)
    {
          strcpy(jose, "a mi me gusta luisa");
          DesplazarCadenaExtremo(jose);
    }

    tf = ((clock() * 1000) / CLK_TCK);

    printf("DesplazarCadenaExtremo = %d\n", tf-ti);

    //----------------------------------------------------------------------------

    ti = ((clock() * 1000) / CLK_TCK);

    for(i = 0; i < MAX; i++)
    {
          strcpy(jose, "a mi me gusta luisa");
          DesplazarCadena(jose, tamanio);
    }

    tf = ((clock() * 1000) / CLK_TCK);

    printf("DesplazarCadena        = %d\n", tf-ti);

    //----------------------------------------------------------------------------

    ti = ((clock() * 1000) / CLK_TCK);

    for(i = 0; i < MAX; i++)
    {
          strcpy(jose, "a mi me gusta luisa");
          DesplazarCadenaAP(jose);
    }

    tf = ((clock() * 1000) / CLK_TCK);

    printf("DesplazarCadenaAP      = %d\n", tf-ti);

    //----------------------------------------------------------------------------

    ti = ((clock() * 1000) / CLK_TCK);

    for(i = 0; i < MAX; i++)
    {
    strcpy(jose, "a mi me gusta luisa");
 SlideString(jose, tamanio, 1, resultado);
    }

    tf = ((clock() * 1000) / CLK_TCK);

    printf("DesplazarCadenaSlide      = %d\n", tf-ti);

    //----------------------------------------------------------------------------

    ti = ((clock() * 1000) / CLK_TCK);

    for(i = 0; i < MAX; i++)
    {
          strcpy(jose, "a mi me gusta luisa");
    DesplazarCadenaHyperSlide(jose, tamanio, 1);
    }

    tf = ((clock() * 1000) / CLK_TCK);

    printf("DesplazarCadenaHyperSlider      = %d\n", tf-ti);

    //----------------------------------------------------------------------------

 delete resultado;
 free(jose);
 //getch();
    return 0;
}


Ultimos resultados:
DesplazarCadenaExtremo: 468
DesplazarCadena: 657
DesplazarCadenaAP: 469~484 (De tres pruebas sufri estas variaciones, de quedarme siempre prefiero quedarme con los valores mas altos)
DesplazarCadenaSlide: 178~250
DesplazarCadenaHyperSlide: 109~187

Editado: Es importante indicar que con la prueba SlideString se esta realizando el strcpy, el cual no haría falta ya que no se esta modificando la cadena origen, con lo que se ganaría velocidad.


sés

Soy indeciso... ¿o no?

zupervaca

 Mira ver si en modo release tienes la opción de optimizar para velocidad o si esta desactivado. Con la opción desactivada no veas lo cutre que se vuelve el tema.

senior wapo

 Si de verdad tienes interés mírate el código ensamblador generado por el compilador, ya que el compilador puede modificar el código ensamblador que le metes (al menos gcc lo hace, por eso tiene esa manera tan rara de incluir fragmentos asm, indicando que registros usas y cuales modificar).

Además, incluir fragmentos asm puede cargarse ciertas optimizaciones si considera el bloque asm como algo opaco, porque entonces no puede asumir ciertas cosas de cara a optimizar (o hacerlo mal).

Pero vamos, que suponer está muy bien y tal, pero la verdadera respuesta para cada Build de cada compilador la ves mirando el código generado. Después, a jugar con los parámetros que se le pasa al compilador en la línea de comandos.

Un poco friki todo esto de las cadenas, ¿ no ? :P  

zupervaca

 Vamos al frikismo extremo  :lol:, he hecho lo que supuestamente hace el memcpy en c salvo la comprobación de si el origen y el destino están en dirección impar y mi sorpresa es que esta función es más rápida que el memcpy, por lo menos con cadenas cortas ya que el rep al principio consume algun ciclo que otro.


void MemCpy( char *pszDest, char *pszSource, size_t nSize )
{
int *pszDestI = (int*)pszDest, *pszSourceI = (int*)pszSource;
int *pszDestEndI = (int*)pszDest + (nSize >> 2);
while( pszDestI != pszDestEndI )
{
 *pszDestI = *pszSourceI;
 pszDestI++;
 pszSourceI++;
}
if( (nSize & 3) == 3 )
{
 *((short*)pszDestI) = *((short*)pszSourceI);
 *(((char*)pszDestI)+2) = *(((char*)pszSourceI)+2);
}
else
{
 if( (nSize & 1) == 1 )
 {
  *((char*)pszDestI) = *((char*)pszSourceI);
 }
}
}


DesplazarCadenaSlide me da ahora 171~187 siempre (ole)

senior wapo

 memcpy() tiene mayor overhead porque alinea las direcciones a múltiplos de 4 bytes de forma que no se produzcan 'stalls' en el procesador al mover un DWORD de/a una dirección no alineada.

En tu código, si los punteros no estan alineados a 4bytes (2bits inferiores deberían ser 0) cada copia de DWORD recibe una penalización de varios ciclos (no era 1 ciclo por cada byte de desplazamiento? no me acuerdo ya). Al menos, esto era así allá por la époce pre-pentium III no se ahora, ni si pasa con los AMDs, pero imagino que continúa pasando. Hace muchísimo que no reescribo bucles en asm.

El caso es que para cadenas cortas (7-12 bytes?) puede que la carga extra que supone alinear (debido a los CMPs y JMPs) puede ser más lenta que el parón que se produce por no estar alineado.

zupervaca

 Ya, si lo puse en mi post eso: "salvo la comprobación de si el origen y el destino están en dirección impar"
El motivo de por que es mas rápido esta función es que el memcpy no puede alinear la memoria a direcciones pares ya que el destino siempre es el origen +1, es decir, una dirección impar si el origen es par y una direccion par si el origen es impar.

No obstante haciendo una modificación a mi código puedes solapar memoria y que se copie correctamente, lo cual no esta mal si te asegura que se copie bien. (Presuponiendo que seguimos con que nSlide sea positivo claro).
void MemCpy2( char *pszDest, char *pszSource, size_t nSize )
{
char *pszDestEnd = pszDest + (nSize >> 2);
while( pszDest != pszDestEnd )
{
 *((int*)pszDest) = *((int*)pszSource);
 pszDest += 4;
 pszSource += 4;
}
if( (nSize & 1) == 1 )
{
 *((char*)pszDest) = *((char*)pszSource);
 pszDest++;
 pszSource++;
}
if( (nSize & 2) == 2 )
{
 *((short*)pszDest) = *((short*)pszSource);
}
}


DesplazarCadenaHyperSlide se quearia en 157.

zupervaca

 Perdonar que ponga dos post seguidos, pero es que no me deja editar el anterior.
El código final es así, salvo algún error que no me haya dado cuenta:

void MemCpy( char *pszDest, char *pszSource, size_t nSize )
{
// Alinear a direcciones pares
if( ((__int64)pszDest & 1) == 1 && ((__int64)pszSource & 1) == 1 )
{
 *((char*)pszDest) = *((char*)pszSource);
 pszDest++;
 pszSource++;
 nSize--;
}
// Alinear a direcciones multiplo de 4
if( ((__int64)pszDest & 2) == 2 && ((__int64)pszSource & 2) == 2 )
{
 *((short*)pszDest) = *((short*)pszSource);
 pszDest += 2;
 pszSource += 2;
 nSize -= 2;
}
// Copiar de 4 en 4 bytes
int *pszDestI = (int*)pszDest, *pszSourceI = (int*)pszSource;
int *pszDestEndI = (int*)pszDest + (nSize >> 2);
while( pszDestI != pszDestEndI )
{
 *pszDestI = *pszSourceI;
 pszDestI++;
 pszSourceI++;
}
// Copiar 3 bytes si quedan
if( (nSize & 3) == 3 )
{
 *((short*)pszDestI) = *((short*)pszSourceI);
 *(((char*)pszDestI)+2) = *(((char*)pszSourceI)+2);
}
else
{
 // Copiar el ultimo byte si queda
 if( (nSize & 1) == 1 )
 {
  *((char*)pszDestI) = *((char*)pszSourceI);
 }
}
}


No he notado cambio de velocidad por lo que comentaba antes.

Mars Attacks

 Wenas. Toy un poco amodorrado, así que no me hagáis mucho caso... (ni me he leído el tema entero ni sé si va de esto, así que... XD)


#include <stdio.h>

int main(){

char lalala[]="lalala";
char *lerele=&lalala[1];

printf("%s\n",lalala);
printf("%s\n",lerele);

}


My 0'02 €

synchrnzr

 Va a ser que el método de Mars es el más rápido (ole)

sync

zupervaca

 Va ser que no vale <_<, debe de modificarse el búfer origen o el destino ya que si te devuelve el origen +1 y después haces un delete de ese puntero o del que le has pasado a la función... como broma valdría.

Mars Attacks

 Yo sólo digo que os pasáis la vida buscando la solución más óptima a problemas estúpidos: invertir cadenas, desplazar cadenas... Luego en la "vida real" suele haber una forma "chachiguay" de solucionar cada problema en concreto.
De todas formas, no soy coder, así que no tengo el cerebro tan enfermo y reblandecido y no os entiendo. Cada cual con sus pasatiempos  ;)  






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.