2011-12-07 6 views
7

Zakładając coś takiego:Czy istnieje standardowe makro do wykrywania architektur wymagających wyrównanego dostępu do pamięci?

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]; 
    } 
} 

mogę jechać szybciej na maszynie niezaangażowanych dostępu (np x86) pisząc coś takiego:

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]; 
    } 
} 

Jednak to musi opierać się na kilku architektur więc chciałbym zrobić coś takiego:

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 
} 

Ale nie mogę znaleźć żadnych informacji na temat dobrych kompilatorów zdefiniowane makra (jak moja hipotetyczna __ALIGNED4__ powyżej), które określają wyrównanie lub jakikolwiek sprytny sposób użycia pre-procesora do określenia wyrównania architektury docelowej. Mogę po prostu przetestować defined (__SVR4) && defined (__sun), ale wolałbym coś, co będzie po prostu działać TM na innych architekturach wymagających wyrównanych dostępów do pamięci.

+1

Procesor wykonuje dodatkowe cykle, aby uzyskać niezaanalizowane dane i przesunąć je we właściwe miejsce. Zwykle byłoby to znacznie wolniejsze, a następnie wyrównane. Zawsze powinieneś próbować czytać wyrównane ... – DipSwitch

+0

Powiem tylko, że zawsze pracowałem nad systemami, które nie są w stanie wykonać kopie w wyrównaniu, tak, że przyjąłem normalne normalne i "szybkie" kopie. –

+0

Niestety jest to w bibliotece i nie mogę kontrolować, w jaki sposób użytkownicy tej biblioteki wyrównują bufory, które mi wysyłają. – nolandda

Odpowiedz

5

Podczas gdy x86 po cichu naprawia nieprzydzielone wejścia, nie jest to optymalne pod względem wydajności. Zazwyczaj najlepiej jest założyć pewne dopasowanie i samodzielnie wykonać poprawkę:

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 */ 
     } 
    } 
} 

Zobacz także instrukcje SIMD.

+0

Opierając się na definicji OP "i" poza pętlą 'for', obawiam się, że nie ma C99 lub' intptr_t'. –

+0

Nawet bez C99 każdy system podobny do unixa, jaki widziałem, miał od wieków 'intptr_t' w' inttypes.h' ... Nie sądzę, żeby to był problem. –

+0

I +1 do Simona za optymalne rozwiązanie problemu nawet w przypadku archsów, które "zezwalają" na niewyrównany dostęp. Ale prawdopodobnie źle jest ustawić "wyrównanie" jako zmienną, a nie stałą. –

2

Standardowym podejściem byłoby posiadanie skryptu configure, który uruchamia program do testowania problemów z wyrównaniem. Jeśli program testowy nie ulega awarii, skrypt configure definiuje makro w generowanym nagłówku konfiguracji, który pozwala na szybszą implementację. Bezpieczniejsza implementacja jest domyślna.

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

(Uważam, że to dziwne, że masz src i mask kiedy naprawdę te dojazdy. I przemianowany mask_bytes do memand. Ale tak czy inaczej ...)

Another opcji jest użycie różnych funkcji, które wykorzystują typy w C. Na przykład:

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]; 
} 

W ten sposób pozwalasz programistce zdecydować.