10
votes

Quel est un exemple d'une fonction C simple qui est plus rapide implémentée dans l'assemblage en ligne?

J'ai du mal à battre mon compilateur à l'aide de l'assemblage en ligne.

Quels sont les exemples bons et non artificiels d'une fonction que le compilateur a du mal à rendre vraiment, vraiment rapide et simple? Mais c'est relativement simple à faire avec l'assemblage en ligne.


5 commentaires

Ne pas choisir sur vous, mais il y a énormément de personnes sur une optimisation et des questions de rapidité, et très peu en disent qu'ils en ont besoin parce qu'ils ne répondent pas aux exigences. Apparemment, nous n'avons pas battu "l'optimisation prématurée, c'est la racine de tout le mal" mantra assez :)


Ce qui a incité mes questions était que je faisais la dicking avec l'assemblée en ligne sur l'iPhone et j'allais écrire un article de blog à ce sujet. Mais je ne pouvais pas pour la vie de moi sur mon compilateur. J'ai donc été curieux de voir s'il existe des cas de bord connus où les compilateurs produisent du code inefficace.


L'assemblage du bras est l'un des ensembles d'instructions «nettoyeurs». Une partie de la philosophie des processeurs de RISC est de ne pas ajouter d'instructions qui ne sont pas facilement utilisées par le compilateur. Vous devrez examiner l'ensemble des instructions de variante de bras particulière et trouvez des opcodes qui n'ont pas de traduction Clear C.


"L'optimisation prématurée est la racine de tout mal" ne s'applique pas à la diapositive ou à l'apprentissage. Il vaut la peine d'être assemblée si, sans autre raison que de vous mettre au défi. Vous venez rarement d'accrocher les cas où vous en avez besoin, mais le sida dans votre compréhension du code généré du compilateur.


"" L'optimisation prématurée est la racine de tous les maladies "ne s'applique pas à la diapositive ou à l'apprentissage. Cela vaut la peine d'écrire un assemblage si, sans autre raison que de vous mettre au défi." AMEN À CELA!


7 Réponses :


0
votes

Ma meilleure victoire sur un compilateur était sur une simple routine memcpy ... J'ai sauté beaucoup de choses de la configuration de base (par exemple, je n'avais pas besoin d'une grande image de pile, alors je sauve quelques cycles là-bas) et a fait quelques petites choses velues.

C'était il y a environ 6 ans, avec un compilateur propriétaire de qualité inconnue. Je vais devoir creuser le code que j'avais et l'essayer contre GCC maintenant; Je ne sais pas que cela pourrait être plus rapide, mais je ne l'excluais pas.

À la fin, même si mon memcpy était en moyenne environ 15 fois plus rapide que celui de notre bibliothèque C, je viens de le garder dans ma poche arrière au cas où j'en avais besoin. C'était un jouet pour moi de jouer avec l'Assemblée PPC et le boost de vitesse n'était pas nécessaire dans notre application.


0 commentaires

2
votes

Si vous voulez faire des trucs comme des opérations SIMD, vous pourrez peut-être battre un compilateur. Cela nécessitera une bonne connaissance de l'architecture et du jeu d'instructions cependant.


1 commentaires

Vous ne pouvez vraiment pas minimiser l'importance de comprendre l'architecture et l'instruction définie lors de la gestion de l'assemblage. J'évite généralement l'ASM, mais je pense toujours à apprendre les capacités de l'architecture afin que je puisse avoir une idée des performances théoriques disponibles.



8
votes

Si vous ne considérez pas la triche des opérations SIMD, vous pouvez généralement écrire une assemblée SIMD qui fonctionne beaucoup mieux que vos compilateurs AutoVectorisation Capacités (si elle a même une autocellation!)

Voici une SSE très basique (un des ensembles d'instructions SIMD de X86). C'est pour l'ensemble Visual C ++ en ligne. P>

Edit: Voici une petite paire de fonctions si vous voulez essayer pour vous-même. C'est le calcul d'un produit de points N longueur. L'une utilise des instructions SSE 2 en ligne (syntaxe en ligne GCC) L'autre est très basique C. P>

C'est très très simple et je serais très surpris si un bon compilateur ne pouvait pas vendre le Simple C boucle, mais si ce n'est pas, vous devriez voir une vitesse dans la SSE2. La version SSE 2 pourrait probablement être plus rapide si j'avais utilisé plus de registres, mais je ne veux pas étirer mes compétences SSE très faibles :). P>

 float dot_asm(float *a, float*b, int n)
{
  float ans = 0;
  int i; 
  // I'm not doing checking for size % 8 != 0 arrays.
  while( n > 0) {
    float tmp[4] __attribute__ ((aligned(16)));

     __asm__ __volatile__(
            "xorps      %%xmm0, %%xmm0\n\t"
            "movups     (%0), %%xmm1\n\t"
            "movups     16(%0), %%xmm2\n\t"
            "movups     (%1), %%xmm3\n\t"
            "movups     16(%1), %%xmm4\n\t"
            "add        $32,%0\n\t"
            "add        $32,%1\n\t"
            "mulps      %%xmm3, %%xmm1\n\t"
            "mulps      %%xmm4, %%xmm2\n\t"
            "addps      %%xmm2, %%xmm1\n\t"
            "addps      %%xmm1, %%xmm0"
            :"+r" (a), "+r" (b)
            :
            :"xmm0", "xmm1", "xmm2", "xmm3", "xmm4");

    __asm__ __volatile__(
        "movaps     %%xmm0, %0"
        : "=m" (tmp)
        : 
        :"xmm0", "memory" );             

   for(i = 0; i < 4; i++) {
      ans += tmp[i];
   }
   n -= 8;
  }
  return ans;
}

float dot_c(float *a, float *b, int n) {

  float ans = 0;
  int i;
  for(i = 0;i < n; i++) {
    ans += a[i]*b[i];
  }
  return ans;
}


3 commentaires

SIMD n'est certainement pas tricher. Il fournit un cas clair de l'endroit où les compilateurs n'ont pas suivi de matériel. C ne gère pas bien le parallélizisme de niveau d'instruction. Peut-être que cela peut dérouler des boucles ici et là, mais plus de routines anticipées ont besoin de sérieux peaufinants.


Il y a beaucoup de compilateurs qui produiront des instructions SIMD.


Ils le feront, pour des cas limités. Fondamentalement tant que votre code est écrit avec une technique ou un algorithme commun. Une fois que l'ensemble d'instructions augmente trop gros, une utilisation optimale de nombreuses instructions commence à se perdre dans le lavage lors de l'écriture d'un compilateur ou d'un optimiseur simplement en raison de la complexité. C'était une grande partie de la base du concept de processeur "RISC". L'optimisation est Simalar aux échecs, un ordinateur peut battre la population majoritaire, mais il faut beaucoup plus qu'un bureau pour battre un grand maître.



6
votes

sauf si vous êtes un gourou d'assemblage Les chances de battre le compilateur sont très faible .

Un fragment de la liaison ci-dessus,

Par exemple, l'orienté binaire "xor % EAX,% EAX "L'instruction était la moyen le plus rapide de définir un registre à zéro Dans les premières générations du X86, Mais la plupart du code est généré par compilateurs et compilateurs rarement Création d'une instruction XOR. Donc l'IA Les designers, ont décidé de déplacer le Compilateur fréquemment survenant Instructions générées jusqu'à l'avant de la logique de décodage combinante Faire le littéral "MOVL $ 0,% EAX" Les instructions exécutent plus vite que la XOR Instruction.


6 commentaires

Je ne suis pas un gourou de montage et j'ai battu le compilateur. Je recours très rarement à l'assemblée. C'était un dernier recours quand je devais. Cela semble juste comme un dicton nay. Et il ignore sa question. Il admet que ce n'est pas facile dans la question.


Je n'ai pas dit que c'est impossible. Si vous grocez le jeu d'instructions, vous pouvez essayer d'écrire un code plus rapide ou de presser la routine pour moins d'instructions. Si vous n'avez qu'un compilateur pas très sophistiqué ou que le compilateur ne gère pas le SSE, 3DNOW Ensems, l'assemblage peut être le approprié moyen de mettre en œuvre certaines routines.


Vous avez raison, comprendre l'ensemble d'instructions est une nécessité absolue si vous souhaitez avoir un espoir de battre un complier. Mais même avec un bon compilateur, vous pouvez trouver des instructions qui n'ont pas de constructions C qui leur correspondent bien sur des architectures modernes. Il y a encore des "lacunes" dans les abstractions qui grandissent à mesure que le paradigme multicore devient la norme. Et dans le marché actuel conscient de la puissance et axés sur les mobiles, nous ne pouvons pas assumer une vitesse principale de la CPU plus rapide dans nos applications. Les processeurs ont frappé à 1 GHz en 1999 et les nouvelles applications sont en cours d'exécution sur le Hard "le plus chaud" classant à 400 MHz aujourd'hui.


Par le matériel "le plus chaud", je veux dire des choses comme l'iPhone et ce qui n'est pas. La durée de vie de la batterie rend les compromis entre l'efficacité et le temps de développement inclinent dans une direction complètement nouvelle.


Pete, je ne discute pas. Et ces espaces sont un autre exemple de la notion une notion :), EN.Wikipedia.org/wiki/leaky_abstraction


En ce qui concerne la matrice de langues pour le matériel, le problème est plus fondental que le développement logiciel général. Autant que j'aime C et C ++, très peu de langues capturent bien la notion le parallélizisme. J'aimerais vraiment voir plus d'influence informatique et de croix dans les langages de description du matériel tels que VHDL. Ils font bien le parallélizisme, mais aspirent aux abstractions. La pollenisation croisée aiderait les deux côtés de la clôture.



5
votes

J'ai mis en place une simple corrélation croisée à l'aide d'une mise en œuvre générique "détroit C". Et puis, lorsqu'il a fallu plus de temps que la franchise que j'ai disponible, j'ai eu recours à la parallélisation explicite de l'algorithme et à utiliser le processeur intrinsèque pour forcer les instructions spécifiques à utiliser dans les calculs. Pour ce cas particulier, le temps de calcul a été réduit de> 30 ms à un peu plus de 4 ms. J'ai eu une fenêtre de 15 ms pour terminer le traitement avant que l'acquisition de données suivante ne s'est produite.

C'était une optimisation de type SIMD sur un processeur VLWI. Cela n'exige que 4 ou deux des intrinsions du processeur, qui sont essentiellement des instructions de langue d'assemblage qui donnent l'apparence d'un appel de fonction dans le code source. Vous pouvez faire la même chose avec l'assemblage en ligne, mais la gestion de la syntaxe et du registre est un peu plus agréable avec les intrinsions du processeur.

Autre que si la taille compte, l'assembleur est roi. Je suis allé à l'école avec un gars qui a écrit un éditeur de texte en plein écran en moins de 512 octets.


2 commentaires

Ceci est un cas classique où l'assembleur est sensible. Le code a été écrit en C; travaillé, mais pas assez vite. Recovering dans l'assembleur a fait fonctionner assez vite - c'était une bonne raison de tomber dans l'assembleur.


J'ai été déçu par la performance que je suis sorti de la version du détroit C, la propagande du vendeur de la puce s'est vantée sur la qualité de son compilateur C. Et ils sont la plus récente de lachaise à outils ne fait aucun meilleur travail l'optimisant non plus. Malheureusement, les DSP avec Vlwi ne sont pas faciles à écrire un optimiseur.



5
votes

J'ai un algorithme de contrôle qui nécessite une rotation des mots par un certain nombre de bits. Pour la mettre en œuvre, j'ai cette macro:

sum ^= _rotr16(val,pos);


1 commentaires

J'ai essayé cela dans GCC (4.0.1) avec -O4. Il diffuse une instruction ROR pour une rotation 32 bits, mais pas pour 16 bits.



7
votes

Comme il est lié à l'iPhone et le code assembleur alors je vais vous donner un exemple qui serait pertinente dans le monde iPhone (et non pas un sse ou x86 asm). Si quelqu'un décide d'écrire du code pour une application assemblée du monde réel, il est fort probable que cela va être une sorte de traitement ou de manipulation d'image signal numérique. Exemples: la conversion de pixels RVB colorspace codant pour des images au format JPEG / PNG format ou le codage de son mp3, amr ou G729 pour les applications voip. En cas de son encodage il y a beaucoup de routines qui ne peuvent pas être traduits par le compilateur à code asm efficace, ils ont tout simplement pas d'équivalent en C. Des exemples de la substance couramment utilisés dans le traitement du son: mathématiques saturé, les routines de multiplication-accumulation, la multiplication de matrices.

Exemple d'ajouter saturé: int signé 32 bits a une portée: 0x8000 0000

res1 = a + b1*c1;
res2 = a + b2*c2;
res3 = a + b3*c3;


0 commentaires