Ne mélange pas ce qui existe et ce qu'il est possible de faire. Lisaac produit du C89 car en l'état actuelle des choses, il n'y a aucun intérêt à produire autre chose sauf à être incompatible.
Je pense que si il produit autre chose, cela sera du "C99 gcc" avec les bonnes extensions.
Je ne mélange pas tout, je dit juste que, à mon avis, si votre objectif est de produire du code rapide, il faut mettre des priorités sur les optimisation à implémenter, et si ce n'est pas votre objectif, ça ne sert à rien de continuer la discution.
Implémenter des optimisation extrêmement complexes de ce type c'est prendre le risque d'introduire des erreurs dans le code généré et de rendre le compilateur très complexe pour des cas relativement rare.
Par contre la vectorisation, bien que complexe, à fait l'objet de bien plus de recherche et va s'appliquer dans beaucoup plus de cas. Donc moin de risque pour un plus gros gain. Il me semble plus pertinent de commencer par la.
Et donc je soulevais le problème du fait que Lisaac pour l'instant produit du code c89 donc la vectorisation n'est pas directement possible.
Concernant la vectorisation automatique, gcc en fait déjà pas mal tout seul comme un grand. Je penche plutôt sur le fait de faire des boucles qui s'automatise facilement. Vu la quantité de matière grise mis sur ce genre de problématique, je ne pense pas que l'on puisse faire une grosse différence.
Lisaac à une vision plus haut niveau du code, à mon avis il doit y avoir plein de cas ou gcc ne peut pas vectoriser le code mais ou lissac possède lui les informations pour le faire.
Même si gcc est très bon pour ce genre de choses, les motifs qu'il est capable de paralléliser restent très limités, et bien souvent il suffit qu'il y ait un pointeur qui traine pour tout foutre en l'air. Dans la pratique, écrire du code que gcc peut paralléliser est souvent au moins aussi complexe que d'écrire directement le code parallèle avec les intrinsics. Et le plus embêtant c'est que la moindre petite modification anodine peut rendre le code non parallélisable, donc un patch par quelqu'un qui ne fait pas super gaffe peut-être catastrophique.
Concernant la taille de blocs en fonction du cache, on peut faire 2 stratégies : l'une dynamique en utilisant une lib qui donne la taille des caches, l'autre qui prend une taille "catch all" : 32 Ko qui correspond à une taille très courante de cache L1.
Le problème c'est qu'optimiser pour le cache est à double tranchant. Il y a trois cas à considérer :
- tu as la bonne taille pour remplir exactement le cache comme il faut et le code est très rapide ;
- tu as exactement la bonne taille pour que le cache soit pourit à mort, et la tu as un code affreusement lent ;
- le reste ou tu as une quantité classique de cache miss et un code de vitesse normale.
Et mon experience à ce niveau la me fait dire que quand tu ne fais rien de spécial, tu est à peu près toujours dans le cas 3, mais des que tu cherche à faire des trucs un peu intelligent tu tombe le plus souvent dans le cas 2 et il faut faire pas mal d'essais avec différentes tailles pour réussir à tomber dans le cas 1.
Donc si tu ne connais pas la taille du cache, il vaut mieux ne rien faire, et si tu connais la taille du cache, trouver la bonne taile pour tes blocs est souvent possible uniquement de manière empirique.
Je suis loin d'etre persuader qu'un compilateur puisse le faire automatquement avant un bon bout de temps, les éléments à prendre en compte sont tellement nombreux et différents dans chaque cas. Pour plus d'infos tu peut regarder les manuels d'optimisation d'agner qui décrit bien le problème.
[^] # Re: Surprise
Posté par beagf . En réponse au journal Lisaac: sorti de la 0.39beta. Évalué à 2.
Je pense que si il produit autre chose, cela sera du "C99 gcc" avec les bonnes extensions.
Je ne mélange pas tout, je dit juste que, à mon avis, si votre objectif est de produire du code rapide, il faut mettre des priorités sur les optimisation à implémenter, et si ce n'est pas votre objectif, ça ne sert à rien de continuer la discution.
Implémenter des optimisation extrêmement complexes de ce type c'est prendre le risque d'introduire des erreurs dans le code généré et de rendre le compilateur très complexe pour des cas relativement rare.
Par contre la vectorisation, bien que complexe, à fait l'objet de bien plus de recherche et va s'appliquer dans beaucoup plus de cas. Donc moin de risque pour un plus gros gain. Il me semble plus pertinent de commencer par la.
Et donc je soulevais le problème du fait que Lisaac pour l'instant produit du code c89 donc la vectorisation n'est pas directement possible.
Concernant la vectorisation automatique, gcc en fait déjà pas mal tout seul comme un grand. Je penche plutôt sur le fait de faire des boucles qui s'automatise facilement. Vu la quantité de matière grise mis sur ce genre de problématique, je ne pense pas que l'on puisse faire une grosse différence.
Lisaac à une vision plus haut niveau du code, à mon avis il doit y avoir plein de cas ou gcc ne peut pas vectoriser le code mais ou lissac possède lui les informations pour le faire.
Même si gcc est très bon pour ce genre de choses, les motifs qu'il est capable de paralléliser restent très limités, et bien souvent il suffit qu'il y ait un pointeur qui traine pour tout foutre en l'air. Dans la pratique, écrire du code que gcc peut paralléliser est souvent au moins aussi complexe que d'écrire directement le code parallèle avec les intrinsics. Et le plus embêtant c'est que la moindre petite modification anodine peut rendre le code non parallélisable, donc un patch par quelqu'un qui ne fait pas super gaffe peut-être catastrophique.
Concernant la taille de blocs en fonction du cache, on peut faire 2 stratégies : l'une dynamique en utilisant une lib qui donne la taille des caches, l'autre qui prend une taille "catch all" : 32 Ko qui correspond à une taille très courante de cache L1.
Le problème c'est qu'optimiser pour le cache est à double tranchant. Il y a trois cas à considérer :
- tu as la bonne taille pour remplir exactement le cache comme il faut et le code est très rapide ;
- tu as exactement la bonne taille pour que le cache soit pourit à mort, et la tu as un code affreusement lent ;
- le reste ou tu as une quantité classique de cache miss et un code de vitesse normale.
Et mon experience à ce niveau la me fait dire que quand tu ne fais rien de spécial, tu est à peu près toujours dans le cas 3, mais des que tu cherche à faire des trucs un peu intelligent tu tombe le plus souvent dans le cas 2 et il faut faire pas mal d'essais avec différentes tailles pour réussir à tomber dans le cas 1.
Donc si tu ne connais pas la taille du cache, il vaut mieux ne rien faire, et si tu connais la taille du cache, trouver la bonne taile pour tes blocs est souvent possible uniquement de manière empirique.
Je suis loin d'etre persuader qu'un compilateur puisse le faire automatquement avant un bon bout de temps, les éléments à prendre en compte sont tellement nombreux et différents dans chaque cas. Pour plus d'infos tu peut regarder les manuels d'optimisation d'agner qui décrit bien le problème.