2011-12-07 11 views
7

Suponiendo algo como:¿Hay una macro estándar para detectar arquitecturas que requieren un acceso de memoria alineado?

void mask_bytes(unsigned char* dest, unsigned char* src, unsigned char* mask, unsigned int len) 
{ 
    unsigned int i; 
    for(i=0; i<len; i++) 
    { 
    dest[i] = src[i] & mask[i]; 
    } 
} 

puedo ir más rápido en una máquina de acceso no alineados (por ejemplo, 86) escribiendo algo como:

void mask_bytes(unsigned char* dest, unsigned char* src, unsigned char* mask, unsigned int len) 
{ 
    unsigned int i; 
    unsigned int wordlen = len >> 2; 
    for(i=0; i<wordlen; i++) 
    { 
    ((uint32_t*)dest)[i] = ((uint32_t*)src)[i] & ((uint32_t*)mask)[i]; // this raises SIGBUS on SPARC and other archs that require aligned access. 
    } 
    for(i=wordlen<<2; i<len; i++){ 
    dest[i] = src[i] & mask[i]; 
    } 
} 

Sin embargo, tiene que basarse en varias arquitecturas así que le gustaría hacer algo como:

void mask_bytes(unsigned char* dest, unsigned char* src, unsigned char* mask, unsigned int len) 
{ 
    unsigned int i; 
    unsigned int wordlen = len >> 2; 

#if defined(__ALIGNED2__) || defined(__ALIGNED4__) || defined(__ALIGNED8__) 
    // go slow 
    for(i=0; i<len; i++) 
    { 
    dest[i] = src[i] & mask[i]; 
    } 
#else 
    // go fast 
    for(i=0; i<wordlen; i++) 
    { 
    // the following line will raise SIGBUS on SPARC and other archs that require aligned access. 
    ((uint32_t*)dest)[i] = ((uint32_t*)src)[i] & ((uint32_t*)mask)[i]; 
    } 
    for(i=wordlen<<2; i<len; i++){ 
    dest[i] = src[i] & mask[i]; 
    } 
#endif 
} 

Pero no puedo encontrar ninguna buena información sobre las macros compilador definida (como mi hipotética __ALIGNED4__ arriba) que especifican la alineación o cualquier forma inteligente de usar el pre-procesador para determinar la alineación de la arquitectura de destino. Podría simplemente probar defined (__SVR4) && defined (__sun), pero preferiría algo que solo funcione TM en otras arquitecturas que requieran accesos de memoria alineados.

+1

La CPU realiza los ciclos extra para obtener los datos desalineados y cambiarlos al lugar correcto. Normalmente, esto sería mucho más lento que obtener alineado. Siempre debe tratar de leer alineado ... – DipSwitch

+0

Solo quiero decir que siempre trabajé en sistemas que no pueden hacer copias alineadas de forma que acabo de asumir la existencia de copias normales y "rápidas" como es normal. –

+0

Lamentablemente, esto está en una biblioteca y no puedo controlar cómo los usuarios de esta biblioteca alinean los almacenamientos intermedios que me envían. – nolandda

Respuesta

5

Mientras que x86 silenciosamente arregla los accesos no alineados, esto no es óptimo para el rendimiento. Por lo general es mejor asumir una cierta alineación y realizar fixups mismo:

unsigned int const alignment = 8; /* or 16, or sizeof(long) */ 

void memcpy(char *dst, char const *src, unsigned int size) { 
    if((((intptr_t)dst) % alignment) != (((intptr_t)src) % alignment)) { 
     /* no common alignment, copy as bytes or shift around */ 
    } else { 
     if(((intptr_t)dst) % alignment) { 
      /* copy bytes at the beginning */ 
     } 
     /* copy words in the middle */ 
     if(((intptr_t)dst + size) % alignment) { 
      /* copy bytes at the end */ 
     } 
    } 
} 

También, echar un vistazo a las instrucciones SIMD.

+0

Basado en OP que define 'i' fuera del ciclo' for', me preocupa que no tenga C99 o 'intptr_t'. –

+0

Incluso sin C99, cada sistema unix que he visto ha tenido 'intptr_t' en' inttypes.h' durante años ... No creo que sea un problema. –

+0

Y +1 a Simon para resolver el problema de manera óptima, incluso en los arcos que "permiten" el acceso no alineado. Pero probablemente sea una mala idea hacer 'alignment' una variable y no una constante. –

2

El enfoque estándar sería tener un script configure que ejecute un programa para probar problemas de alineación. Si el programa de prueba no falla, la secuencia de comandos de configuración define una macro en un encabezado de configuración generado que permite una implementación más rápida. La implementación más segura es la predeterminada.

void mask_bytes(unsigned char* dest, unsigned char* src, unsigned char* mask, unsigned int len) 
{ 
    unsigned int i; 
    unsigned int wordlen = len >> 2; 

#if defined(UNALIGNED) 
    // go fast 
    for(i=0; i<wordlen; i++) 
    { 
    // the following line will raise SIGBUS on SPARC and other archs that require aligned access. 
    ((uint32_t*)dest)[i] = ((uint32_t*)src)[i] & ((uint32_t*)mask)[i]; 
    } 
    for(i=wordlen<<2; i<len; i++){ 
    dest[i] = src[i] & mask[i]; 
    } 
#else 
    // go slow 
    for(i=0; i<len; i++) 
    { 
    dest[i] = src[i] & mask[i]; 
    } 
#endif 
} 
1

(se me hace raro que tiene src y mask cuando en realidad éstas viaje. Me cambió el nombre a mask_bytesmemand. Pero de todos modos ...)

Otra de las opciones es el uso de diferentes funciones que se aprovechan de tipos en C. Por ejemplo:

void memand_bytes(char *dest, char *src1, char *src2, size_t len) 
{ 
    unsigned int i; 
    for (i = 0; i < len; i++) 
     dest[i] = src1[i] & src2[i]; 
} 

void memand_ints(int *dest, int *src1, int *src2, size_t len) 
{ 
    unsigned int i; 
    for (i = 0; i < len; i++) 
     dest[i] = src1[i] & src2[i]; 
} 

De esta manera usted deja que el programador decida.

Cuestiones relacionadas