Détection des besoins en mémoire alignés sur le processeur cible

J’essaie actuellement de construire un code qui est supposé fonctionner sur une large gamme de machines, des poches pour ordinateurs de poche et des capteurs aux gros serveurs des centres de données.

L’une des (nombreuses) différences entre ces architectures est la nécessité d’un access mémoire aligné.

Un access mémoire aligné n’est pas requirejs sur les CPU x86 “standard”, mais de nombreux autres CPU en ont besoin et produisent une exception si la règle n’est pas respectée.

Jusqu’à présent, je m’en occupais en obligeant le compilateur à faire preuve de prudence lors de l’access à des données spécifiques, connues pour être risquées, à l’aide de l’atsortingbut condensé (ou pragma). Et ça marche bien.

Le problème est que le compilateur est si prudent que de nombreuses performances sont perdues au cours du processus.

Les performances étant importantes, il serait préférable de réécrire une partie du code pour travailler spécifiquement sur les processeurs ssortingctement alignés. Par contre, ce code serait plus lent sur les processeurs prenant en charge les access mémoire non alignés (tels que x86). Nous souhaitons donc l’utiliser uniquement sur des processeurs nécessitant un access ssortingct à la mémoire.

Et maintenant la question: comment détecter, au moment de la compilation, que l’architecture cible nécessite un access mémoire ssortingctement aligné? (ou l’inverse)

À ma connaissance, aucune implémentation C ne fournit une macro de préprocesseur pour vous aider à comprendre cela. Étant donné que votre code est censé fonctionner sur un large éventail de machines, je suppose que vous avez access à une grande variété de machines pour les tests. Vous pouvez donc trouver la réponse à l’aide d’un programme de test. Ensuite, vous pouvez écrire votre propre macro, quelque chose comme ci-dessous:

#if defined(__sparc__) /* Unaligned access will crash your app on a SPARC */ #define ALIGN_ACCESS 1 #elif defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) /* Unaligned access is too slow on a PowerPC (maybe?) */ #define ALIGN_ACCESS 1 #elif defined(__i386__) || defined(__x86_64__) || \ defined(_M_IX86) || defined(_M_X64) /* x86 / x64 are fairly forgiving */ #define ALIGN_ACCESS 0 #else #warning "Unsupported architecture" #define ALIGN_ACCESS 1 #endif 

Notez que la vitesse d’un access non aligné dépendra des limites qu’il traverse. Par exemple, si l’access franchit une limite de page de 4 ko, il sera beaucoup plus lent et il peut y avoir d’autres limites qui le ralentissent encore. Même sur x86, certains access non alignés ne sont pas gérés par le processeur mais par le kernel du système d’exploitation. C’est incroyablement lent.

De plus, rien ne garantit qu’une implémentation future (ou actuelle) ne modifiera pas soudainement les caractéristiques de performance des access non alignés. Ceci est arrivé dans le passé et peut arriver dans le futur; le PowerPC 601 pardonnait très bien les access non alignés, mais pas le PowerPC 603e.

Le fait que le code que vous écrivez pour créer un access non aligné diffère de la mise en œuvre sur les plates-formes complique encore les choses. Par exemple, sur PowerPC, cela est simplifié par le fait que x << 32 et x >> 32 sont toujours égaux à 0 si x est 32 bits, mais sur x86 vous n'avez aucune chance.

Écrire votre code pour un alignement ssortingct de la mémoire est quand même une bonne idée. Même sur les systèmes x86 qui permettent un access non aligné, vos lectures / écritures non alignées provoqueront deux access à la mémoire et certaines performances seront perdues. Il n’est pas difficile d’écrire du code efficace qui fonctionne sur toutes les architectures de CPU. La règle simple à retenir est que le pointeur doit être aligné sur la taille de l’object que vous lisez ou écrivez. Par exemple, si vous écrivez un DWORD, alors (dest_pointer & 3 == 0). L’utilisation d’une béquille telle que “UNALIGNED_PTR” entraînera le compilateur à générer du code inefficace. Si vous avez une grande quantité de code hérité qui doit fonctionner immédiatement, il est logique d’utiliser le compilateur pour “réparer” la situation, mais s’il s’agit de votre code, écrivez-le dès le début pour qu’il fonctionne sur tous les systèmes.