8
votes

Y at-il une macro standard pour détecter les architectures nécessitant un accès Alignés mémoire?

En supposant quelque chose comme: xxx

Je peux aller plus vite sur une machine d'accès non alignée (par exemple x86) en écrivant quelque chose comme: xxx

Cependant, il doit construire sur plusieurs architectures, donc je voudrais faire quelque chose comme: xxx

mais je ne trouve aucune bonne information sur les macros définies du compilateur (comme mon hypothétique __ aligné4 __ ci-dessus) qui spécifient l'alignement ou toute façon intelligente d'utiliser le pré-processeur pour déterminer l'alignement de l'architecture cible. Je pourrais simplement tester défini (__svr4) && défini (__sun) , mais je préférerais quelque chose qui va simplement fonctionner tm sur d'autres architectures nécessitant des accès à la mémoire alignée.


3 commentaires

La CPU fait les cycles supplémentaires pour obtenir les données non alignées et le transférer au bon endroit. Normalement, cela serait significatif plus lent puis aligné obtenir. Vous devriez toujours essayer de lire aligné ...


Tout comme dire que j'ai toujours travaillé sur des systèmes qui ne peuvent pas faire des copies transversales, de sorte que je viens de venir assumer l'existence de copies normales et "rapides" comme normale.


Malheureusement, cela se trouve dans une bibliothèque et je ne peux pas contrôler comment les utilisateurs de cette bibliothèque alignent les tampons qu'ils m'envoient.


3 Réponses :


6
votes

Alors que X86 fixe silencieusement les accès non alignés, cela n'est guère optimal pour la performance. Il est généralement préférable d'assumer un certain alignement et de réaliser des réalisations vous-même:

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


7 commentaires

Sur la base de la définition OP i en dehors du boucle, je suis inquiet qu'il n'a pas C99 ou intptr_t .


Même sans C99, chaque système de type UNIX que j'ai vu a eu intptr_t dans intttypes.h pour les âges ... Je ne pense pas que ce soit un problème.


Et +1 à Simon pour résoudre le problème de manière optimale même sur les arcs qui "permettent" d'un accès non aligné. Mais c'est probablement une mauvaise idée de faire une alignement une variable et non une constante.


+1, mais j'utiliserais uintptr_t , modulo de valeurs signées est suspect


et pour les instructions de SIMD, un bon compilateur doit comprendre que par lui-même, par exemple si vous donniez -march = natif à GCC, je le ferais, je pense, dès que vous lui donnez grand assez de types entiers à traiter.


Je crains que la copie des deux tampons de source dans des tampons alignés, la création d'un tampon de bande aligné, faisant le masque de manière alignée, puis en déplaçant la mémoire tampon de bande alignée dans le tampon Dest non aligné que l'utilisateur transmettra de manière significative plus que juste Payer le coût du masque de ByTewise non aligné en premier lieu.


J'utilisais memcpy principalement à titre d'exemple, car il est facile de comprendre.



2
votes

L'approche standard serait d'avoir un script code> configurer code> qui exécute un programme pour tester les problèmes d'alignement. Si le programme de test ne se bloque pas, le script de configuration définit une macro dans une en-tête de configuration générée qui permet une implémentation plus rapide. La mise en œuvre plus sûre est la valeur par défaut.

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
}


0 commentaires

1
votes

(Je trouve qu'il est étrange que vous avez src code> et masque code> quand vraiment ces commutes. J'ai renommé mask_bytes > à memand code>. Mais de toute façon ...)

Une autre option consiste à utiliser différentes fonctions qui tirent parti des types dans C. Par exemple: P>

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


0 commentaires