8
votes

Comment la performance dépend-elle des valeurs de données sous-jacentes?

J'ai l'extrait de code C ++ suivant (la partie C ++ est la classe de profileur qui est omise ici), compilée avec VS2010 (machine Intel 64 bits). Le code multiplie simplement un tableau de flotteurs ( Arr2 ) avec un scalaire et met le résultat dans un autre tableau ( arr1 ): xxx

La pièce de lecture à partir de fichier et le profilage (c'est-à-dire que la mesure du temps d'exécution) est omise ici pour la simplicité.

lorsque arrate est initialisé à des nombres aléatoires dans la plage [0 1 ], le code fonctionne environ 10 fois plus rapide par rapport à un cas où arrate est initialisé sur un tableau de race dans lequel environ 2/3 des valeurs sont des zéros. J'ai joué avec les options du compilateur / fp et / o , qui a changé le temps d'exécution un peu, mais le rapport de 1:10 était approximativement conservé.

  • Comment se fait-il que la performance dépend de la valeur réelle? Que fait la CPU différemment qui rend les données rares ~ 10 fois plus lentes?
  • existe-t-il un moyen de faire fonctionner les "données lentes" plus rapidement, ou une optimisation (vectorisant, par exemple, avoir le même effet sur les deux tableaux (c'est-à-dire que les "données lentes" seront toujours plus lentes que "vite données ")?

    edit

    code complet est ici: https: // gist .github.com / 1676742 , la ligne de commande de compilation est dans un commentaire dans test.cpp .

    Les fichiers de données sont ici:


13 commentaires

Veuillez vous fournir des versions complètes et compilables des deux tests afin que nous puissions expérimenter avec eux?


Pourrait-il s'agir que lorsque vous passez 0 à votre flotteur dans votre matrice piquante, la conversion de int sur float introduit des frais généraux?


Vous ne mettez à jour que les éléments qui ne sont pas 0? Alors, peut-il être que les zéros ne sont pas dans le cache?


@ degedl0r: cette pensée m'ont eue aussi. Cependant, cet argument s'appliquerait uniquement à la première itération de 20 000 car la boucle d'addition traverse toute la matrice.


@aix: hmm oui, vous pourriez avoir raison. Mais vous ne savez jamais que le compilateur génère :) Dans ce cas, il peut allumer la boucle pour les boucles, de sorte que la boucle Niter est la boucle intérieure, non? Peut-être qu'il génère même arr1 [n] + = niter * échelle * arr2 [n] s'il est vraiment fou;)


Je ne passe pas 0 S puisque j'ai lu des données binaires à partir de fichiers. Voir ma réponse modifiée pour les liens vers le code et les données.


@aix: En fait, je ne voulais pas accumuler la somme sur toute l'itération, j'ai juste oublié une ligne memset - mais de toute façon, cela ne modifie pas les résultats de la durée d'exécution.


Question pour les experts FPU: Pourrait-il être que le FPU considère que 0.0 un nombre sous-formel dans ce scénario? Les OPS de la FP, y compris les sous-formulaires, sont plus lents que les OP ordinaires sur certains FPU, de sorte que cela pourrait l'expliquer si 0 est considéré comme sous-formulaire.


Est-ce que ce devoir? Je demande car les chiffres des fichiers de données semblent mal diriger la manière dont vous interprétez les résultats.


@Danielfischer, les zéros exacts (0,0) ne sont pas considérés comme désormalisés et sont traités rapidement.


@Evenykluev J'espérais et prévu que, mais je ne savais pas. Merci.


@Marksynowiec: Je ne comprends pas votre commentaire - mais non, ce n'est pas des devoirs.


Drôle, une autre question avec le même sous-jacent Problème a montré seulement 3 semaines après celui-ci.


3 Réponses :


7
votes

C'est probablement parce que vos données "rapides" consistent que des nombres de points flottants normaux, mais que vos données "lentes" contiennent de nombreux nombres dénormalisés.

Quant à votre deuxième question, vous pouvez essayer d'améliorer la vitesse avec ceci (et Traitez tous les nombres dénormalisés comme zéros exacts): xxx


0 commentaires

2
votes

Je peux penser à deux raisons pour cela.

Premièrement, le prédicteur de la branche peut prendre des décisions incorrectes. Il s'agit d'une cause potentielle de lacunes de performance causées par des modifications de données sans changement de code. Cependant, dans ce cas, il semble très improbable.

La deuxième raison possible est que vos données "principalement des zéros" ne consistent pas vraiment de zéros, mais plutôt de presque zéros, ou que vous gardez arr1 dans la plage presque zéro . Voir Ce lien Wikipedia .


0 commentaires

1
votes

Il n'y a rien d'étrange que les données de i.bin prend plus de temps à traiter: vous avez beaucoup de chiffres comme '1.401e-045 # den' ou '2.214e-043 # den', où #den signifie que le nombre ne peut pas être normalisé à la précision standard du flotteur. Étant donné que vous allez le multiplier par 6.6e-14, vous aurez certainement des exceptions de sousfaction, ce qui ralentit considérablement les calculs.


0 commentaires