En cherchant les performances à tout prix je me retrouve très vite devant la question "Dois-je faire de la programmation parallèle ?" et donc aussi "Dois-je utiliser mon GPU ?"
Je me demandais si le simple fait d'avoir un algorithme pouvant être parallélisé est une raison suffisante pour justifier l'utilisation de opencl/cuda (sans compter le temps du transfert des données CPU<->GPU)
Exemple très simple, j'ai un tableau représentant un rectangle, j'aimerai changer la couleur de chaque pixel pour correspondre à une texture, l'algorithme est très simple (et court) et peut être parallélisé, ça suffit ?
De plus je voudrai avoir plus d'informations sur ce qu'est un thread niveau CPU (hardware, pas ceux de l'OS), hormis le fait de ne pas bloquer le thread principal quand est-ce qu'utiliser différents threads peut me donner un boost de performance ? J'ai cru comprendre que l'OS cherche de lui même à repartir les taches sur les cores disponibles (J'ai du mal à comprendre pourquoi utiliser deux threads ne double pas les performances et ainsi de suite)
La question est complique. Mon travail est de faire ca; je fais du calcul parallele dans la vie, que ca soit sur CPU, GPU, different accelerateur, a memoire distribue ou whatever.
La question fondamentale qu'il faut que tu te pose est: est ce que ce code est assez rapide pour moi? Si la reponse est "oui", alors passe ton chemin et regarde le prochain truc sur ta liste. C'est le meme conseil que "premature optimization is the root of all evil".
Le calcul parallele ne sert qu'a trois choses:
1/ Permettre a plusieurs calcul de se passe en meme temps pour recouvrir des temps d'attente. Ca ne vient pas du calcul parallele, mais du calcul concurrent. C'est par exemple ce que font plein d'application asynchrone que ca soit dans ton navigatuer ou ton service de gestion des evenment sur ton appliaction graphique lourde.
2/ Diminuer le temps de calcul d'un calcul en particulier. Souvent les gens parlent de diminution de la latence ou diminution du makespan. C'est ce que tu as quand un deuxieme mecano vient travailleur sur ta voiture, le but est de faire sortir ta voiture plus vite. Typiquement c'est du scaling vertical.
3/ Diminuer le temps d'attente en aggrege. Souvent on parle d'augmentation du debit, c'est du scaling horizontal. Et c'est ce que fait Auchan quand il rajoute une caissiere. Un client en particuliere ne passe pas plus vite, mais comme il y a une caissiere de plus, en moyenne tu attends moins.
Si tu n'as pas un de ces trois problemes, ce n'est pas la peine de te faire chier.
La question de est ce qu'il faut faire du cuda, de l'opencl, ou whatever est une question technologique et donc qui est bien plus complique. Si tu programme en C ou C++ et que tu as deja bien regarder ton code, je te conseille de regarder d'abors OpenMP, c'est relativement facil a programmer, il y a plein de ressources en ligne. Ca sert a programmer principalement les differents coeurs de ton CPU. Et dans la plupart des cas, ca te donne les performances dont tu as besoin. Si tu penses que tu as vraiment besoin de programmer ton GPU, alors regarde du cote de OpenACC, c'est un modele de programmation proche de ce que fait OpenMP, c'est fail a apprendre et ca va te donner la plupart du temps les performance dont tu as besoin et que ton GPU peut donner. Note que eventuellment OpenMP fonctionnera pour programmer les GPU aussi. La derniere norme OpenMP a du support pour GPU, mais les compilateurs ne l'implementent pas pour l'instant.
Si tu pense que OpenACC est trop haut niveau, alors regarde Cuda ou OpenCL a du sens. Avant ca, ca n'a pas de sens.
Si tu n'as pas regarder ton code de facon trop precise pour les questions de performances, commence par faire ca. En pratique la difference entre un code standard et un code qui a ete regarde pour des problemes de perf, il y a un facteur 10 a gagner quelques part si les algos sont correcte, un facteur 100 ou plus si les algos ne le sont pas.
Si tu ne programme pas en C ou C++, reecrit ton code en C++ et tes problemes de performance disparaitront probablement.
Si tu as des questinos plus precise: demande! Encore une fois c'est ce que je fais dans la vie.
Des reponses plus precise:
Je me demandais si le simple fait d'avoir un algorithme pouvant être parallélisé est une raison suffisante pour justifier l'utilisation de opencl/cuda (sans compter le temps du transfert des données CPU<->GPU)
En pratique le temps de transfert CPU<->GPU est le probleme principale a regler pour utiliser un GPU.
Exemple très simple, j'ai un tableau représentant un rectangle, j'aimerai changer la couleur de chaque pixel pour correspondre à une texture, l'algorithme est très simple (et court) et peut être parallélisé, ça suffit ?
Pas forcement. Faire une calcul en parallele a un cout fixe, si ce cout est plus elever que le tmeps de calcul alors tu perds du temps en le faisant en parallele.
De plus je voudrai avoir plus d'informations sur ce qu'est un thread niveau CPU (hardware, pas ceux de l'OS), hormis le fait de ne pas bloquer le thread principal quand est-ce qu'utiliser différents threads peut me donner un boost de performance ? J'ai cru comprendre que l'OS cherche de lui même à repartir les taches sur les cores disponibles (J'ai du mal à comprendre pourquoi utiliser deux threads ne double pas les performances et ainsi de suite)
Materiellement, une machine peut avoir plusieurs processeur (le machin physique que tu branche sur la carte mere), un processeur avoir plusieurs coeur (le machin qui a des uniter de calcul), le coeur peut avoir plusieurs contexte d'execution physique (C'est ce que intel appele l'hyperthreading, techniquement, ca s'appelle faire du SMT et il y a plusieurs facon de faire ca).
Le systeme d'exploitation cree un thread noyau pour chaque contexte d'execution physique. Les threads utilisateur (les different processus et ce qui est cree par pthread_create) se retrouve mapper par le systeme d'exploitatin sur les thread noyau, qui sont uex meme mapper sur les contextes physiques.
Si ton calcul est completement borne par la vitesse d'execution des instructions, et que le calcul est fait par deux threads qui sont mappe sur des coeurs different, alors tu aura un calcul qui va deux fois plus vite. C'est le cas ideal: X coeurs va X fois plus vite. Mais ca n'arrive pas si souvent que ca parceque:
1/ les contextes d'execution physique partagent les resources des coeurs pour l'execution des instructions, et donc a pleine vitesse deux threads mapper sur le meme coeurs ne vont pas plus vite que un seul thread mappe sur le coeur.
2/ les coeurs ont des caches L1, et L2 independent, mais partagent le meme cache L3 et le meme controlleur memoire. Donc si le calcul est borne par le controlleur memoire, alors tu deviens borne par le controlleur et rajouter des coeurs ne change rien.
3/ Les different processeurs partages toujours les bus memoire vers la DRAM (c'est complique en fait, mais faisons simple). Encore une fois, si tu es borne par la vitesse de la DRAM, rajouter des coeurs ne change rien.
4/ Les processeurs ont une limite physique a combien d'electricite ils peuvent utiliser, et donc, si tous les coeurs sont a 100%, le processeurs diminue la frequence pour limite la consommation electrique. Et donc avoir X coeurs a 100%% d'utilisation va diminuer la frequence et tu n'auras pas une amelioration d'un facteur X.
La question fondamentale qu'il faut que tu te pose est: est ce que ce code est assez rapide pour moi? Si la reponse est "oui", alors passe ton chemin et regarde le prochain truc sur ta liste. C'est le meme conseil que "premature optimization is the root of all evil".
Mon projet actuel est de créer une API graphique (2D) pour générer une image ou un GIF le plus rapidement possible, donc je suppose pouvoir me dire que mon code ne sera jamais assez rapide ?
La question de est ce qu'il faut faire du cuda, de l'opencl, ou whatever est une question technologique et donc qui est bien plus complique. Si tu programme en C ou C++ et que tu as deja bien regarder ton code, je te conseille de regarder d'abors OpenMP, c'est relativement facil a programmer, il y a plein de ressources en ligne. Ca sert a programmer principalement les differents coeurs de ton CPU. Et dans la plupart des cas, ca te donne les performances dont tu as besoin. Si tu penses que tu as vraiment besoin de programmer ton GPU, alors regarde du cote de OpenACC, c'est un modele de programmation proche de ce que fait OpenMP, c'est fail a apprendre et ca va te donner la plupart du temps les performance dont tu as besoin et que ton GPU peut donner. Note que eventuellment OpenMP fonctionnera pour programmer les GPU aussi. La derniere norme OpenMP a du support pour GPU, mais les compilateurs ne l'implementent pas pour l'instant.
Pour le moment je ne m'inquiète pas de la difficulté de la techno, j'ai mentionné OpenCL dans le titre étant donné que ça me paraissait être le choix le plus judicieux étant donné la documentation et le support de plusieurs GPU contrairement à CUDA.
Concernant le "ai-je vraiment besoin d'utiliser le gpu ?", je compte intégrer à ma lib une implémentation CPU (software rendering) et une autre GPU.
Je me dis naïvement que tout ce qui touche au graphisme bénéficie énormément des calculs parallèles (j'ai quand même voulu avoir des informations complémentaires sur ce topic)
Si tu ne programme pas en C ou C++, reecrit ton code en C++ et tes problemes de performance disparaitront probablement.
Pour le moment histoire d'être plus productif je fais tout en java (évidemment à cause de la JVM les performances ne sont pas les mêmes) mais je pense profiter du fait que le code kernel marchera pareil en c++ pour quand j'aurai un POC assez crédible
Pas forcement. Faire une calcul en parallele a un cout fixe, si ce cout est plus elever que le tmeps de calcul alors tu perds du temps en le faisant en parallele.
Par "coût fixe" vous voulez dire le transfert GPU<->CPU ?
Ce qui voudrait dire que mon exemple avec le rectangle texturé ne sera sûrement pas rentable ? (Gros transfert de données pour des calculs très court) Enfin je suppose que c'est le genre de chose que je dois vérifier manuellement en essayant les deux méthodes (CPU et GPU)
1/ les contextes d'execution physique partagent les resources des coeurs pour l'execution des instructions, et donc a pleine vitesse deux threads mapper sur le meme coeurs ne vont pas plus vite que un seul thread mappe sur le coeur.
Il faudrait donc que je précise vouloir un thread venant d'un différent cœur, c'est possible ?
Le 08 juillet 2019 à 02:16:46 JeanAsk a écrit :
La question fondamentale qu'il faut que tu te pose est: est ce que ce code est assez rapide pour moi? Si la reponse est "oui", alors passe ton chemin et regarde le prochain truc sur ta liste. C'est le meme conseil que "premature optimization is the root of all evil".
Mon projet actuel est de créer une API graphique (2D) pour générer une image ou un GIF le plus rapidement possible, donc je suppose pouvoir me dire que mon code ne sera jamais assez rapide ?
Bah ca depend de tes utilisateurs ca. Eventuellement ecrire le GIF sur le disque sera le bottleneck, a ce moment la, on s'en fout du temps de calcul
Pour le moment je ne m'inquiète pas de la difficulté de la techno, j'ai mentionné OpenCL dans le titre étant donné que ça me paraissait être le choix le plus judicieux étant donné la documentation et le support de plusieurs GPU contrairement à CUDA.
Concernant le "ai-je vraiment besoin d'utiliser le gpu ?", je compte intégrer à ma lib une implémentation CPU (software rendering) et une autre GPU.
Je me dis naïvement que tout ce qui touche au graphisme bénéficie énormément des calculs parallèles (j'ai quand même voulu avoir des informations complémentaires sur ce topic)
Si ce que tu fais, c'est de la generation d'image et de video, je regarderai ce que halide peut faire pour moi. C'est ce que adobe utilise pour generer les plugins dans photoshop. Et c'est tout open source. OpenCL est un standard qui va probablement mourrir, parceque NVidia prefere cuda, et parceque AMD commence a preferer Vulkan.
A la fin des comptes, la difficulte d'une technologie est toujours importante, sinon tu ecrirai tout en assembleur et pas en Java. OpenMP et OpenACC ont de bonnes perf et sont simple a ecrire. Deployer du cuda ou de l'opencl sans raison c'est juste utilise le mauvais outils.
Si tu ne programme pas en C ou C++, reecrit ton code en C++ et tes problemes de performance disparaitront probablement.
Pour le moment histoire d'être plus productif je fais tout en java (évidemment à cause de la JVM les performances ne sont pas les mêmes) mais je pense profiter du fait que le code kernel marchera pareil en c++ pour quand j'aurai un POC assez crédible
En pratique, les optimizations en terme de placement et representation memoire sont critiques.
Pas forcement. Faire une calcul en parallele a un cout fixe, si ce cout est plus elever que le tmeps de calcul alors tu perds du temps en le faisant en parallele.
Par "coût fixe" vous voulez dire le transfert GPU<->CPU ?
Pas seulement le cout du transfert. Faire un truc en parallele est un evenement systeme, il faut creer des threads, causer avec des threads, parler sur the bus PCI-e, what ever. Il faut passer en kernel mode et faire des trucs. Du fait, ca a un cout en temps. Sur ma machine lancer un kernel cuda ca prends 6 micro secondes. Mon processeur est a 3ghz, donc si le calcul fait moins de 18000 cycle sur ma machine, demarer le kernel cuda est plus cher que faire le calcul sur le CPU avant de compter le temps de calcul du kernel sur le GPU en lui meme.
Ce qui voudrait dire que mon exemple avec le rectangle texturé ne sera sûrement pas rentable ? (Gros transfert de données pour des calculs très court) Enfin je suppose que c'est le genre de chose que je dois vérifier manuellement en essayant les deux méthodes (CPU et GPU)
Voila tu as compris, il y a une question du ratio entre le calcul et le mouvement de donnees. A partir de quand le transfert devient petit par rapport au temps de transfert va dependre de la machine. Mais en gros, si ton calcul demande de deplacer O(N) bytes et que le calcul est en O(N) alors probablement tu veux le laisser sur le CPU. Il y a des cas extreme ou les constantes sont enorme, mais c'est un bon discriminant en premiere approximation.
1/ les contextes d'execution physique partagent les resources des coeurs pour l'execution des instructions, et donc a pleine vitesse deux threads mapper sur le meme coeurs ne vont pas plus vite que un seul thread mappe sur le coeur.
Il faudrait donc que je précise vouloir un thread venant d'un différent cœur, c'est possible ?
Ca depend de ton modele de programmation. Quand tu cree un thread avec pthread create, le systeme d'exploitation fait le mapping automatiquement en utilisant une heuristique d'equilibrage de charge. Tu peux forcer le mapping manuellement, mais c'est un peu casse couille a faire. Lesoutisl de calculs parallele comme OpenMP ont des options pour faire le placement en fonction de quelques pattern classique.