web-dev-qa-db-fra.com

Optimisation G ++ au-delà de -O3/-Ofast

Le problème

Nous avons un programme de taille moyenne pour une tâche de simulation, que nous devons optimiser. Nous avons déjà fait de notre mieux en optimisant les sources au maximum de nos compétences en programmation, y compris le profilage avec Gprof et Valgrind .

À la fin, nous voulons exécuter le programme sur plusieurs systèmes probablement pendant quelques mois. Nous sommes donc vraiment intéressés à pousser l'optimisation à l'extrême. 

Tous les systèmes exécuteront Debian/Linux sur du matériel relativement nouveau (Intel i5 ou i7). 

La question

Quelles sont les options d'optimisation possibles avec une version récente de g ++, allant au-delà de -O3/-Ofast?

Nous sommes également intéressés par une optimisation mineure coûteuse, qui sera rentable à long terme. 

Ce que nous utilisons maintenant

À l'heure actuelle, nous utilisons les options d'optimisation g ++ suivantes:

  • -Ofast: niveau d'optimisation "standard" le plus élevé. Le -ffast-math inclus ne posant aucun problème dans nos calculs, nous avons donc décidé de nous lancer malgré le non-respect des normes.
  • -march=native: permet d'utiliser toutes les instructions spécifiques à la CPU.
  • -flto pour permettre l'optimisation du temps de liaison, à travers différentes unités de compilation.
49
Haatschii

matériel relativement nouveau (Intel i5 ou i7)

Pourquoi ne pas investir dans une copie du compilateur Intel et des bibliothèques hautes performances? Il peut surperformer GCC en ce qui concerne les optimisations par une marge significative, généralement comprise entre 10% et 30%, voire davantage, et même davantage pour les programmes de calculs complexes. De plus, Intel fournit également un certain nombre d’extensions et de bibliothèques pour les applications de traitement des données (parallèles) hautes performances, si vous pouvez vous permettre de les intégrer à votre code. Cela pourrait rapporter gros si cela finissait par vous faire gagner des mois de temps d'exécution.

Nous avons déjà fait de notre mieux en optimisant la source à la limite de nos compétences en programmation.

D'après mon expérience, le type d'optimisation micro et nano que vous effectuez généralement à l'aide d'un profileur a tendance à générer un faible retour sur investissement en temps par rapport aux macro-optimisations (rationalisation de la structure du code) et, plus important encore, et souvent négligé, les optimisations d'accès à la mémoire (par exemple, la localité de référence, la traversée dans l'ordre, la minimisation de l'indirection, le port de cache-cache, etc.). Cette dernière consiste généralement à concevoir les structures de la mémoire pour mieux refléter la manière dont la mémoire est utilisée (traversée). Parfois, cela peut être aussi simple que de changer de type de conteneur et d’améliorer considérablement ses performances. Souvent, avec les profileurs, vous vous perdez dans les détails des optimisations d’instruction en instruction, et les problèmes d’agencement de la mémoire ne s’affichent pas et sont généralement omis lorsqu’on oublie de regarder l’ensemble. C'est une bien meilleure façon d'investir votre temps, et les retombées peuvent être énormes (par exemple, de nombreux algorithmes O(logN) finissent par exécuter presque aussi lentement que O(N) uniquement à cause de la mauvaise configuration de la mémoire. (par exemple, l’utilisation d’une liste chaînée ou d’une arborescence liée est l’un des principaux responsables de problèmes de performances énormes par rapport à une stratégie de stockage contigu)).

19
Mikael Persson

Si vous pouvez vous le permettre, essayez VTune . Il fournit BEAUCOUP plus d'informations qu'un simple échantillonnage (fourni par gprof, pour autant que je sache). Vous pouvez donner au Code Analyst a try. Latter est un logiciel libre et décent, mais il risque de ne pas fonctionner correctement (ou pas du tout) avec les processeurs Intel.

Equipé d’un tel outil, il vous permet de vérifier diverses mesures telles que l’utilisation du cache (et essentiellement la disposition de la mémoire), qui, si elles sont pleinement utilisées, augmentent considérablement l’efficacité.

Lorsque vous êtes sûr que vos algorithmes et structures sont optimaux, vous devez absolument utiliser les multiples cœurs sur i5 et i7. En d’autres termes, jouez avec différents algorithmes/modèles de programmation parallèle et voyez si vous pouvez accélérer.

Lorsque vous avez vraiment des données parallèles (structures de type tableau sur lesquelles vous effectuez des opérations identiques/identiques), vous devez essayer OpenCL et SIMD instructions (plus facile à configurer).

6
Red XIII

hein, alors dernière chose que vous pouvez essayer: ACOVEA projet: Analyse des optimisations du compilateur via un algorithme évolutif - comme le montre la description, il essaie un algorithme génétique pour choisir compilez plusieurs fois et vérifiez le temps, en donnant un retour à l'algorithme :) - mais les résultats pourraient être impressionnants! :)

4
zaufi

Quelques notes sur la réponse actuellement choisie (je n’ai pas encore assez de points de réputation pour pouvoir le poster en tant que commentaire):

La réponse dit: 

-fassociative-math, -freciprocal-math, -fno-signed-zeros et -fno-trapping-math. Celles-ci ne sont pas incluses dans -Ofast et peuvent donner lieu à des améliorations supplémentaires des performances lors des calculs.

C’était peut-être vrai lorsque la réponse a été publiée, mais le documentation GCC indique que tout cela est activé par -funsafe-math-optimizations, activé par -ffast-math, activé par -Ofast. Cela peut être vérifié avec la commande gcc -c -Q -Ofast --help=optimizer, qui indique les optimisations activées par -Ofast et confirme que toutes les optimisations sont activées.

La réponse dit aussi:

d'autres indicateurs d'optimisation qui ne sont activés par aucune option "-O" ... -frename-registers

Encore une fois, la commande ci-dessus montre que, au moins avec mon GCC 5.4.0, -frename-registers est activé par défaut avec -Ofast.

2
user3708067

Il est difficile de répondre sans autre détail:

  • quel type de calcul?
  • quelles bibliothèques utilisez-vous?
  • quel degré de parallélisation?

Pouvez-vous écrire la partie de votre code qui prend le plus de temps? (Typiquement une boucle serrée)

Si vous êtes lié au CPU, la réponse sera différente de celle que vous avez IO. 

Encore une fois, veuillez fournir plus de détails. 

1
Escualo

Je recommanderais de jeter un coup d'œil sur le type d'opérations qui coûtent le gros du travail et de rechercher une bibliothèque optimisée. Il existe de nombreuses bibliothèques vectorielles rapides, optimisées pour l’Assemblage et optimisées par SIMD, pour les problèmes courants (principalement les mathématiques). Réinventer la roue est souvent tentant, mais cela ne vaut généralement pas la peine, si une solution existante peut couvrir vos besoins. Comme vous n'avez pas précisé le type de simulation utilisé, je ne peux que vous en donner quelques exemples.

http://www.yeppp.info/

http://eigen.tuxfamily.org/index.php?title=Main_Page

https://github.com/xianyi/OpenBLAS

0
uLoop