|
Publicité ' | ||||||||||||||||||||||||
|
|
#1 | ||
|
Invité régulier
![]() Inscription : mai 2010 Messages : 37 ![]() |
Bonjour,
je viens de créer un programme en C qui diminue la taille d'une bitmap par 2 et la resize. Le problème c'est que je dois l'optimiser afin qu'il soit plus rapide. Je pensais utiliser les pointeurs mais je ne vois pas vraiment comment faire. Voici mon code: Code :
Merci |
||
|
|
10
|
|
|
#2 |
|
Membre expérimenté
![]() Inscription : octobre 2004 Messages : 329 ![]() |
Les pointeurs ça "n'optimise" pas, c'est juste un moyen d'exprimer des constructions, "optimiser" c'est d'abord de l'algorithmie, et ensuite une réflexion sur l'architecture visée (hérarchie de cache, blocking des boucles, ...). En tenant compte du fait que tes boucles une fois traitée par le compilateur ne ressemble plus du tout à ce que tu avais en entrée.
Déjà je m'étonne du x++ et y++ ? Est-ce que le code est correct ? |
|
|
21
|
|
|
#3 | ||
|
Membre du Club
![]() Inscription : novembre 2010 Messages : 12 ![]() |
comme ça au feeling j'aurais tendance à regarder à partir de la ligne 84 les deux boucles imbriquées, j'imagine que l'essentiel du traitement passe dedans
pour le moins tu peux sans doute éviter le recalcul à chaque ligne de choses comme "y*width", "(y+1)*width" à l'aide de variables intermédiaires/temporaires l'emploi du type "register" pour x et y peut-être aussi, et ramener les divisions par 2 et par 4 respectivement à des décalages de 1 et 2 bits vers la droite quelque chose comme ça : Code c :
mais c'est possiblement que des "bouts de ficelles", à voir si ça suffit à faire une différence significative |
||
|
|
10
|
|
|
#4 |
|
Invité régulier
![]() Inscription : mai 2010 Messages : 37 ![]() |
Bonjour,
Joker-eph, mes x++ et y++ me permettent de passer de 2 lignes en 2 lignes et 2 colonnes en 2 colonnes pour faire ma moyenne. Merci BufferBob, tes modifications me permettent diminuer légèrement mon temps d'exécution. |
|
|
00
|
|
|
#5 |
|
Expert Confirmé
![]() Pierre Ingénieur développement logiciels Inscription : juin 2007 Messages : 1 354 ![]() |
Bonjour à toi!
Pour ca, il suffit de mettre x+=2 dans l'instruction d'incrémentation de la boucle
__________________
Mes principes de bases du codeur qui veut pouvoir dormir:
|
|
10
|
|
|
#6 | ||
|
Invité régulier
![]() Inscription : mai 2010 Messages : 37 ![]() |
Merci, cela ma encore permis d'améliorer mes performances.
Je compte maintenant m'orienter vers des pointeur pour cette partie du code: Code :
|
||
|
|
00
|
|
|
#7 | ||
![]() ![]() Patrick GonordEnseignant Chercheur Inscription : juin 2005 Messages : 5 488 ![]() |
Dérivé de la dernière version de ton code (et sauf erreur)
Code :
__________________
Publication : Concepts en C Mon avatar : Glenn Gould -------------------------------------------------------------------------- Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !
|
||
|
|
10
|
|
|
#8 |
|
Invité régulier
![]() Inscription : mai 2010 Messages : 37 ![]() |
Merci diogene,
Ca fonctionne nikel et a bien optimisé mon code |
|
|
00
|
|
|
#9 |
|
Invité régulier
![]() Inscription : mai 2010 Messages : 37 ![]() |
Est-ce qu'il serait possible d'optimiser les boucles for ?
|
|
|
00
|
|
|
#10 |
|
Expert Confirmé
![]() Baptiste ROUSSELDéveloppeur Temps réel Embarqué Inscription : janvier 2011 Messages : 1 316 ![]() |
En partant de la version de diogene on peut paralléliser le traitement (exécuter le traitement en le découpant et l'exécutant dans plusieurs threads).
Mais cela ne se fait pas sans une bonne compréhension de la parallélisation de tâche. Surtout que suivant la taille de tes tableaux la parallélisation n'apportera pas forcement un gain de performance.
__________________
|
|
|
00
|
|
|
#11 | ||
|
Expert Confirmé
![]() Pierre Ingénieur développement logiciels Inscription : juin 2007 Messages : 1 354 ![]() |
D'une manière générale, le meilleur principe d'optimisation est le suivant:
"la plus rapide manière de faire un calcul, c'est encore de ne pas le faire". Autrement dit, la tache est environ optimisée si les seuls calculs sont les calculs utiles. Le code de diogene est en ce sens optimal. Tu veux réaliser remplir une image de taille N/2*M/2, et tu as N/2*(M+3)/2 affectation. Je ne vois pas qu'optimiser de plus. Tu peux éventuellement modifier les affectations de pointeurs, il est possible que sur certaines architectures, tu gagnes quelques cycles… mais tu peux aussi perdre… c'est à voir. Code :
__________________
Mes principes de bases du codeur qui veut pouvoir dormir:
|
||
|
10
|
|
|
#12 | |
|
Membre expérimenté
![]() Inscription : octobre 2004 Messages : 329 ![]() |
Citation:
Optimiser les boucles "for" ça peut être du tiling, de la vectorisation, du multithreading, du pipeline (peut-être pas ici...), ou une combinaison de tout ça. Mais ne pas oublier que le compilateur fait déjà plein de choses (à moins de compiler en -O0). |
|
|
|
10
|
|
|
#13 |
|
Expert Confirmé
![]() Pierre Ingénieur développement logiciels Inscription : juin 2007 Messages : 1 354 ![]() |
pour faire mieux, il faut connaitre précisément les caractéristiques techniques du processeur, dont: la taille des caches, le jeu d'instructions, le nombre de registres, et d'autre trucs qu'un compilo fera de toute facon bien mieux que toi…
à moi d'être implémenteur de compilateur, bien évidemment. Mon "environ" optimal signifie que l'algorithme ne peux pas faire mieux. Il ne reste que le gagne-petit, et l'artillerie lourde (multithread, assembleur…)
__________________
Mes principes de bases du codeur qui veut pouvoir dormir:
|
|
10
|
|
|
#14 |
|
Expert Confirmé
![]() Baptiste ROUSSELDéveloppeur Temps réel Embarqué Inscription : janvier 2011 Messages : 1 316 ![]() |
Le compilateur n'a pas accès à toutes les informations de ton matériel. Il ne sait pas quel type de cache est implémenté (ton OS ne le sait pas non plus d'ailleurs), à savoir si c'est un pass-through ou non par exemple ce qui peut énormément changer la donne.
Et on doit bien trouver d'autres paramètres qui peuvent changer la donne, mais je pense que déjà celui-ci est le plus flagrant. Pour ceux qui n'ont pas envie de se potasser de la doc pour comprendre ce concept : un cache implémentant une politique de pass-through ne va pas charger une donnée en cache L1, elle va transiter directement en cache L2 lors de son écriture. Si cette donnée venait à être demandée en lecture elle sera rapatriée dans le cache L1 et y restera (pour utilisation en lecture ou écriture).
__________________
|
|
|
10
|
|
|
#15 | |
|
Membre expérimenté
![]() Inscription : octobre 2004 Messages : 329 ![]() |
Citation:
Sans connaitre ces paramètres, il y a des motifs qui sont constant. Par exemple à algorithme égal tu peux toujours l'écrire d'une manière qui favorise l'utilisation d'un cache, comme parcourir des tableaux à deux dimensions suivant les lignes et non les colonnes... Ensuite tu peux déjà faire un blocking pour une taille de L1 que tu sais inférieur à ton cache L1 (en général 32k sur un proc intel). Enfin il y a la vectorisation et la parallélisation, voir les options de gcc (ou icc). Bref, sinon voir les papiers sur le compilateur source-à-source à source Pluto, qui traite ce genre de code en appliquant toutes ces optimisations et en te régénérant un code C à passer à gcc. Et pour info voilà ce que l'OS peut me dire sur mon cache : $ grep . /sys/devices/system/cpu/cpu0/cache/index*/* /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size:64 /sys/devices/system/cpu/cpu0/cache/index0/level:1 /sys/devices/system/cpu/cpu0/cache/index0/number_of_sets:64 /sys/devices/system/cpu/cpu0/cache/index0/physical_line_partition:1 /sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_list:0 /sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_map:00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001 /sys/devices/system/cpu/cpu0/cache/index0/size:32K /sys/devices/system/cpu/cpu0/cache/index0/type:Data /sys/devices/system/cpu/cpu0/cache/index0/ways_of_associativity:8 /sys/devices/system/cpu/cpu0/cache/index1/coherency_line_size:64 /sys/devices/system/cpu/cpu0/cache/index1/level:1 /sys/devices/system/cpu/cpu0/cache/index1/number_of_sets:64 /sys/devices/system/cpu/cpu0/cache/index1/physical_line_partition:1 /sys/devices/system/cpu/cpu0/cache/index1/shared_cpu_list:0 /sys/devices/system/cpu/cpu0/cache/index1/shared_cpu_map:00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001 /sys/devices/system/cpu/cpu0/cache/index1/size:32K /sys/devices/system/cpu/cpu0/cache/index1/type:Instruction /sys/devices/system/cpu/cpu0/cache/index1/ways_of_associativity:8 /sys/devices/system/cpu/cpu0/cache/index2/coherency_line_size:64 /sys/devices/system/cpu/cpu0/cache/index2/level:2 /sys/devices/system/cpu/cpu0/cache/index2/number_of_sets:4096 /sys/devices/system/cpu/cpu0/cache/index2/physical_line_partition:1 /sys/devices/system/cpu/cpu0/cache/index2/shared_cpu_list:0-1 /sys/devices/system/cpu/cpu0/cache/index2/shared_cpu_map:00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000003 /sys/devices/system/cpu/cpu0/cache/index2/size:6144K /sys/devices/system/cpu/cpu0/cache/index2/type:Unified /sys/devices/system/cpu/cpu0/cache/index2/ways_of_associativity:24 |
|
|
|
00
|
|
|
#16 | ||||||
|
Membre chevronné
![]() Auditeur informatique Inscription : avril 2009 Messages : 119 ![]() |
En suivant ce fil de discussion, tout-à-fait passionnant, j'ai eu envie de me pencher à nouveau sur l'optimisation en faisant un certain nombre de tests systématiques (compilation avec gcc -O2).
Code :
Code :
Code :
Si vous avez une idée pour expliquer ces résultats bizarres, je suis preneur !
__________________
<< Il n'est pas dans Ses intentions de tout faire, ni donc de nous dépouiller de notre libre-arbitre et de cette poignée de gloire qui nous appartient. >> (Le Prince, Nicolas Machiavel) |
||||||
|
|
00
|
|
|
#17 |
|
Membre émérite
![]() ![]() |
Bonsoir,
Pour effectuer ce genre de comparatifs, il est souvent de mise de jeter un coup d'œil au code ASM généré, ainsi que de préciser la version du compilateur. Chez moi, avec les mêmes codes et les mêmes options de compilation, j'ai des résultats exactement inverses. Bonne soirée !
__________________
Récursivité en C : épidémie ou hérésie ? "Pour être un saint dans l'Église de l'Emacs, il faut vivre une vie pure. Il faut se passer de tout logiciel propriétaire. Heureusement, être célibataire n'est pas obligé. C'est donc bien mieux que les autres églises" - Richard Stallman |
|
30
|
|
|
#18 |
|
Membre chevronné
![]() Auditeur informatique Inscription : avril 2009 Messages : 119 ![]() |
Oui, bien sûr que j'ai regardé le code assembleur, ce qui m'a évidemment confirmé que le code généré est plus rapide mais ne répond pas à ma question, qui est : pourquoi un compilateur C comme gcc avec l'option O2 a-t-il produit un code plus rapide et mieux optimisé avec ces modifications qui selon toute logique devraient produire un code plus lent (le code avec la variable superflue est deux fois plus rapide dans mes tests que celui sans variable superflue) ?
Je serais d'autant plus intéressé par une réponse sur ce sujet que j'ai obtenu peu après des résultats un peu différents avec gcc (MinGW) en version 4.7.2 sous Windows 7 par rapport à gcc en version 4.6.3 sur une distrib Mageia : gcc 4.6.3 sous Linux : 4,2 s / 2,8 s / 1,9 s gcc 4.7.2 sous Windows 7 : 3,7 s / 3,9 s / 2,4 s L'introduction de la variable superflue conduit donc encore à une meilleure optimisation mais de moindre importance, alors que le passage des variables locales en variables globales ralentit cette fois l'exécution (ce qui pour le coup me paraît plus logique ...). Au delà de ça, ce qui m'étonne et m'inquiète un tantinet, c'est la grande disparité de ces résultats d'une version de compilo à l'autre, particulièrement en tenant compte des résultats exactement inverses obtenus par Kirilenko ! Car si une optimisation, voire un simple style de programmation, peut induire des écarts de vitesse du simple au double puis les inverser selon la version du compilo, quel est alors l'intérêt de l'optimisation (hors l'optimisation algorithmique qui n'est pas ici en cause) ? Exemple : j'écris mon code puis, comme la vitesse est un point essentiel du logiciel que je veux produire, j'optimise soigneusement les parties gourmandes du source avec un compilo x en version 1.1, réussissant à multiplier par trois la vitesse d'exécution. Deux ans plus tard, j'ai besoin de modifier ce prog, ce que je fais, puis je le recompile avec le compilo x maintenant passé en version 1.3, voire avec un nouveau compilo y, et là ... patatras, mon programme super rapide devient une grosse limace ! Je suis vraiment le seul à trouver ça gênant ?
__________________
<< Il n'est pas dans Ses intentions de tout faire, ni donc de nous dépouiller de notre libre-arbitre et de cette poignée de gloire qui nous appartient. >> (Le Prince, Nicolas Machiavel) |
|
|
00
|
|
|
#19 |
|
Membre expérimenté
![]() Inscription : octobre 2004 Messages : 329 ![]() |
Amusant avec GCC 4.4 aucune différence de perf, et avec 4.6 je passe de 1.39s à 2s en remplaçant i par n.
|
|
|
00
|
|
|
#20 |
|
Expert Confirmé
![]() Baptiste ROUSSELDéveloppeur Temps réel Embarqué Inscription : janvier 2011 Messages : 1 316 ![]() |
Mais dites moi... Vous tournez sur une configuration faite pour les benchmarks ?
Non parce que vous savez faire tourner des tests de performance sur un OS de bureautique et qui plus est non temps réel et s'étonner de différences de l'ordre de la centaine de milliseconde c'est du n'importe quoi... ![]() Et de plus la moyenne n'apporte pas grand chose comme résultat, elle doit être comparée avec l'écart type au minimum. La tolérance sur ce genre de configuration est de l'ordre de 1-5 secondes pour des traitements long (et on peut descendre à l'ordre de 300-800 ms en sachant ce que l'on fait : fermer / couper tout ce que l'on peut, et c'est pas une mince affaire sur un OS de bureautique...).
__________________
|
|
|
00
|
Copyright © 2000-2013 - www.developpez.com