Bonjour a vous,
j'ai un probleme a vous soumettre et peut etre que vous aurez une idee sur comment ecrire du code propre.
J'ai une variable disons int x; Et j'ai besoin de passer cette variable comme un parametre template a une fonction. J'ai besoin que ce parametre soit template parceque je veux utiliser une version specialiser de la fonction pour cette valeure. Si je passe le parametre comme un parametre de fonction, les performances de la fonction s'ecroule (genre prend 40% de penalite dans la tete).
Du fait, je me retrouve avec un code du genre:
switch(x) {
case 1: f<1>(); break;
case 2: f<2>(); break;
case 3: f<3>(); break;
case 4: f<4>(); break;
case 5: f<5>(); break;
case 6: f<6>(); break;
case 7: f<7>(); break;
case 8: f<8>(); break;
case 16: f<16>(); break;
case 32: f<32>(); break;
case 64: f<64>(); break;
}
Comme vous pouvez le voir, c'est bien moche a souhait. Une idee quelconque pour obtenir du code plus propre?
Best,
Erik
(oups, je voulais poster sur le forum programmation. Bon c'est pas grave, il doit y avoir des gens famillier avec ca ici aussi.
Ouch c'est tricky.
Je ne peux pas aider, je n'utilise jamais les templates.
Pourquoi pas appeler chacune des fonctions et mettre le résultat dans un tableau, puis après utiliser le tableau pour récupérer la bonne valeur ?
Paulop, j'ai pas compris. Je ne veux appeler qu'une seule de ces fonctions, je n'ai pas une boucle sur x en amont. En gros x c'est une taille de bloc sur laquel je fais mes calculs. En fonction de mon jeu de donnee et des capacite de la machines, je fais des blocs de taille differentes.
Mais par contre, ca m'a donnee lidee de faire un tableau de pointeurs de fonction. Bon je ne sais pas si c'est beaucoup mieux...
ah, je viens de comprendre ton message. En fait, il y a des parametre a la fonction f(). Je les ai juste retires pour faire court.
Ok, je me disais aussi :D
Par contre je comprend pas pourquoi tu prends un 40% de penalité :/
Je ne suis pas completement sur non plus. Ce nombre est utilise pour calculer des offsets. Si tu connais le nombre a la compilation il y a peut etre des operations qui peuvent etre precalculee. Aussi peut etre que rajouter une variable consomme un registre de plus et que la pression registre devient trop forte. Et c'est du code CUDA donc j'ai aucune idee de ce que le compilateur fait dans mon dos.
A mon avis tu traque un bug au mauvais endroit...
Qui a parle de bug? Le code fonctionne correctement et ne rapporte pas d'erreur dans valgrind. Le code est moche, mais a priori pas bugge.
Bonjour
Je pense que ce code est mal conçus, normalement un template c'est pour un type, une classe, mais pas pour une valeur.
Cette valeur devrais être passer comme paramètre a ta fonction, genre f(64), surtout si c'est juste un offset.
Ce n'est pas mal concu, on utilise souvent des specialisations de templates pour optimiser des parties critiques de code en "applatissant" toute une structure de classe au compile-time.
Je pense notamment a des systemes de particuliers, generation de shaders dynamiques, etc. On perd un peu de memoire (code plus gros) mais on evite toutes les tables virtuelles. La seule contrainte etant de connaitre toute la structure des classes a la compilation.
Pour rendre le code un peu plus "beau", sans en changer la logique, tu peux eventuellement passer pas un tableau de pointeur sur fonctions (selon la signature, et si tu as d'autres parametres ou pas), ou utiliser le preprocesseur a coup d'operateur de concatenation (##) (histoire de faire une macro qui rende le switch moins monstrueux)
typo : systemes de particules*
LGV, c'est pas bete d'utiliser le preprocesseur pour faire dnas le moins moche. Surtout qu'en fait j'ai une dizaine de parametre a cette fonction. Donc si je peux ecrire les parametres qu'une seule fois, ca fera deja du bien.
papy, LGV, en l'occurence, ce n'est pas un applatissement de structure de donne. C'est une histoire d'un parametre qui est constant et qui permet de simplifier le code assembleur dans une partie critique. J'avais parler il y a un bout de temps d'utiliser des templates pour parametre statiquement la taille de structures de donnes [1]. Et ici je pense que c'est essentiellement le meme comportement que je vois.
Mon parametre template est le nombre de colonnes d'un jeu matrices qui sont passees en parametre. Je passe du temps a faire des calculs d'indice i*X+j. Je ne sais pas bien d'ou vient la difference de performance qui est si significative ici. Mais comme c'est du code CUDA et que je ne connais pas l'assembleur du truc (je ne suis meme pas sur qu'il soit public?) c'est difficile a dire. J'imagine que la difference vient de l'economie d'un registre par thread (une centaine de million) et que ca evite le register spilling. Aussi, il est possible que ca evite une multiplication parceque j est constant par thread. du coup tu peux faire offtab = tab+sizeof(int)*j en amont. et il te reste a faire offtab+i*X*sizeof(int). Et si X est connu a la compilation tu peux precalculer X*sizeof(int) et economiser une multiplication (et un registre). Enfin bon, experimentalement, ca va plus vite
[1] http://erik.deblan.org/blog/index.php?article6/quand-les-conteneurs-standards-deviennent-trop-lents
A ok, c'est vraiment spécifique car je n'est jamais vu ça avant. La spécialisation de templates étant toujours présenté avec des types et pas des valeurs.
M'enfin on apprend tous les jours.
La performance c'écroule peut être car au niveau du GPU quand tu a une structure compiler dans le code elle est peut être directement dans le GPU, alors que si tu le fait en cour d'exécution (par paramètre donc) ta structure est peut être compiler a ce moment par le CPU, puis streamer dans le GPU, donc perte probable de 40%.
Mais ce n'est qu'un hypothèse.
nan, la compilation du code PTX en code assembleur pour GPU est fate par le CPu dans les deux cas. Et elle n'est faites qu'une fois et cache au niveau du driver (de memoire)
Bon, bah ca ressemble a ca finalement:
switch (NB_BFS)
{
s,
d_d, d_sigma, n_count); break;
CASE(1);CASE(2);CASE(3);CASE(4);CASE(5);CASE(6);CA
SE(7);CASE(8);
CASE(16);CASE(32);CASE(64);CASE(128);
default: assert(0);
}
C'est moche, mais pas tant.
Pourquoi pas un tableau ? ca bouffera 64 x 4 octets mais en perf ca enverra le maximum...
Sinon pour rendre ton expension plus jolie :
switch (NB_BFS)
{
BOOST_PP_SEQ_FOR_EACH(MACRO, x, SEQ)
default: assert(0);
}
J'ai pense au tableau, mais concretement ca pousse juste le probleme un peu plus loin. L'initialisation du tableau va etre moche, l'appel un peu mieux, mais je peux toujours encapsuler le switch dans une fonction classique pour rendre 'vode client' plus propre.
Je ne connaissais pas cette macro de boost, il va falloir que je la regarde, je ne vois pas bien coment elle fonctionne. Merci!