IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

C11 : la normalisation est achevée


Sujet :

C

  1. #21
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 603
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 603
    Points : 17 913
    Points
    17 913
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Bousk Voir le message
    http://en.wikipedia.org/wiki/Restrict
    je connaissais pas ce mot-clé
    c'est finalement pas stupide du tout, un tel exemple ne me serait certainement jamais apparu

    l'exmple donné est stupide et disparaîr dès que l'on se penche sur l'optimisation d'un code : n'importe qui qui voit une valeur à calculer apparaître 2 fois la factorise..

    Et cela aurrait dû être fait par le compilateur, automatiquement dans les options O1 , O2, ou O3

    Je ne vois pas ce que ajouter un élément d'optimisation à la norme d'un langage ajooute, à part le fait de le rendre moins général... et compliquer la vie de ceux qui l'apprennent..

    Et si un pomgrammeur était assez stupide pour faire pointer par B ou A le même buffer que val (dans le bon ordre), alors il n'a rien à faire à programmer des choses complexes...

    C'estcomme pour la rotation d'un point x,y.. Si le programmeur est assez stupide pour ne pas se servir des coordonnées AVANT les opérations, ben..il n'y a plus rien à faire pour lui..

    Je troue cela aberrant : alors ça marche pour un truc,mais pas si on complexifie la tâche (comme l'exemple de la rotation)..

    Et , par rapport au Fortran, la chose ne corrige rien : si on fait la même chose en Fortran, on obtiendra la même incertitude...
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  2. #22
    Membre éclairé Avatar de rt15
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2005
    Messages
    262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2005
    Messages : 262
    Points : 665
    Points
    665
    Par défaut
    Citation Envoyé par souviron34 Voir le message
    l'exmple donné est stupide et disparaîr dès que l'on se penche sur l'optimisation d'un code : n'importe qui qui voit une valeur à calculer apparaître 2 fois la factorise..
    Si on est soucieux des perfs, il me semble que l'on ne peut pas trouver ça stupide.
    Et le compilo n'y peut rien. Si on ne lui dit pas que ça n'overlap pas, il ne peut pas deviner. Il est obligé de prendre en compte le cas de l'overlap...

    On peut faire le parallèle avec memcpy/memmove.
    D'un côté, memcpy est souvent plus rapide car il n'y a pas d'overlap (C'est du moins ce qu'est censé garantir le programmeur, comme pour restrict). De l'autre memmove gère l'overlap et est souvent plus lent.

    De ce fait, memcpy est en quelque sorte l'implémentation de memmove avec des arguments restrict.

    Bref, ajouter restrict permet littéralement de modifier le comportement d'une fonction et donc les optimisation possibles pour le compilo.

  3. #23
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 603
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 603
    Points : 17 913
    Points
    17 913
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par rt15 Voir le message
    Si on est soucieux des perfs, il me semble que l'on ne peut pas trouver ça stupide.
    Et le compilo n'y peut rien. Si on ne lui dit pas que ça n'overlap pas, il ne peut pas deviner. Il est obligé de prendre en compte le cas de l'overlap...

    On peut faire le parallèle avec memcpy/memmove.
    D'un côté, memcpy est souvent plus rapide car il n'y a pas d'overlap (C'est du moins ce qu'est censé garantir le programmeur, comme pour restrict). De l'autre memmove gère l'overlap et est souvent plus lent.

    De ce fait, memcpy est en quelque sorte l'implémentation de memmove avec des arguments restrict.

    Bref, ajouter restrict permet littéralement de modifier le comportement d'une fonction et donc les optimisation possibles pour le compilo.
    Je répète :

    • ajouter restrict vient du programmeur
    • utiliser correctement memcpy ou memmove vient du programmeur
    • Faire une idiotie vient du programmeur..


    Faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    memmove ( &(tab[2]), &(tab1), (n-2)*sizeof(...));
    est tout à fait légal, de même que faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    memmove ( &(tab[1]), &(tab2), (n-2)*sizeof(...));
    Sauf qu'il faut avoir lu la doc et savoir ce qu'on fait..

    Pourquoi faudrait-il avoir un paramètre à un compilo pour empêcher de faire ça ??

    Et si la fonction est f(a,b,c), et que a, b, et c sont des tableaux, ou alors on les programmeurs sont des c.ns de pas avoir lu la doc ou alors c'est qu'on n'a pas fourni la bonne doc...


    Je trouve que c'est vraiment inutile (surtout dans une norme de langage), et que cela va à l'encontre et de la souplesse / rigueur qu'implique le C et de la symbolique des options de compil -O1, -O2, ou -O3, qui "optimisent" de la meilleure manière, sans que le programmeur ait à savoir quoi : on peut même s'apercvoir que, dans d'assez nombreux cas justement -O3 optimise trop, avec le genre de mécanisme décrit dans le lien..

    Sauf que justement c'est une option de compil...


    Et comme je disais, cela ne rapproche en rien du Fortran..

    Si une routine Fortran est f(a,b,c), et que la doc dit que A, B, et C dovent être différents, ce n'est pas Fortran qui va râler en tant que tel si le programmeur utilise le même tableau..

    Ce genre de manip est aux risques et périls de l'utilisateur, il faut savoir ce qu'on fait..

    Il y a plein d'exemples en C : les fonctions de tris de qsort, les sockets, les unions, les doubles pointeurs, les void*, etc etc..
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  4. #24
    Membre éclairé Avatar de rt15
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2005
    Messages
    262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2005
    Messages : 262
    Points : 665
    Points
    665
    Par défaut
    Le but de restrict n'est pas du tout d'empêcher le développeur ou le compilateur de faire des bêtises.
    Le but est de préciser au compilo qu'il n'y a pas d'overlap.
    Cette information permet au compilo de générer un code plus rapide dans certains cas.
    Et le compilo ne peut généralement pas deviner cette information que l'on utilise -O3 ou pas. Pour le déterminer, il faut qu'il vérifie dans le source que lors des appels (Ou de l'appel, si la fonction est inliné) qu'il ne peut pas y avoir d'overlap car les paramètres sont "différents". Jolie casse tête. Et dans certains cas, c'est même tout à fait impossible car il ne peut pas connaître tous les appels d'avance (Cas d'une fonction exportée par une librairie).

    Considérons une fonction plus proche de ce qui peut exister, c'est à dire une fonction updatePoints qui prend en paramètre :
    1/ Un tableau de points(Coordonnées x et y).
    2/ Le nombre d'élément du tableau.
    3/ Un pointeur vers un point delta.

    Le but de la fonction est d'ajouter à tous les points du tableau le delta contenu dans le paramètre trois.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    pour i de 0 à n - 1
      points[i].x += delta.x;
      points[i].y += delta.y;
    fait;
    Ca revient à peu près à une bête translation d'un nuage de points dans un espace 2D. D'un point de vue fonctionnel, il n'y aura a jamais d'overlap entre "points" et "delta". Le programmeur le sait. Le compilo ne le sait pas forcément si le programmeur ne lui indique pas.

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #include <stdio.h>
     
    #define POINT_COUNT 4
     
    typedef struct
    {
      int x;
      int y;
    }
    MY_POINT;
     
    void updatePoints1(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      int i;
     
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints[i].x += lpDelta->x;
        lpPoints[i].y += lpDelta->y;
      }
    }
     
    void updatePoints2(MY_POINT *restrict lpPoints, int nPointCount, MY_POINT *restrict lpDelta)
    {
      int i;
     
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints[i].x += lpDelta->x;
        lpPoints[i].y += lpDelta->y;
      }
    }
     
    int main()
    {
      int i;
      MY_POINT lpPoints[4];
      MY_POINT delta;
     
      for (i = 0; i < POINT_COUNT; i++)
      {
        lpPoints[i].x = i;
        lpPoints[i].y = 100 + i;
      }
      delta.x = 1;
      delta.y = 1;
      updatePoints1(lpPoints, POINT_COUNT, &delta);
      updatePoints2(lpPoints, POINT_COUNT, &delta);
     
      for (i = 0; i < POINT_COUNT; i++)
      {
        printf("%d %d\n", lpPoints[i].x, lpPoints[i].y);
      }
     
      return 0;
    }

    La différence entre updatePoints1 et updatePoints2 est simplement l'ajout du mot clé restrict. La différence se fait dans le code généré. En effet, comme restrict indique que delta n'est pas modifié quand lpPoints est modifié, la valeur de delta n'a pas à être lue à chaque tour de boucle.

    Code sans restrict :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    loop:
    mov     ecx,[esi]               # ecx = delta.x
    mov     eax,[esi+0x4]           # eax = delta.y
    add     [ebx+edx*8],ecx         # points[i].x += ecx
    add     [ebx+edx*8+0x4],eax     # points[i].y += eax
    inc     edx                     # i++
    cmp     edx,0x4                 # si i < 4 alors ...
    jl      loop                    # ... saute à loop
    Code avec restrict :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    mov     ecx,[esi]
    mov     eax,[esi+0x4]
    loop:
    add     [ebx+edx*8],ecx
    add     [ebx+edx*8+0x4],eax
    inc     edx
    cmp     edx,0x4
    jl      loop
    Le corps de la boucle (Ce qui est exécuté 1000 fois s'il y a 1000 éléments) a 2 instructions de moins. D'autre part, toujours dans le cas de restrict, on peut inverser la boucle et ainsi remplacer inc/cmp/jl par dec/jnz et gagner une autre instruction. On ne peut pas faire cette inversion sans restrict car si delta peut être un élément de points, le résultat peut dépendre du sens de parcourt.

    Bref, restrict, c'est une information qui en théorie peut bien aider le compilo à générer du code plus rapide. Il semble donc intéressant de l'avoir dans la norme.

    [ ************** edit ************** ]
    A noter que dans les faits, avec les flags de compilation :
    gcc -O3 test.c -o test.exe -std=c99 -fstrict-aliasing
    gcc 3.4.5 (Version ancienne... Il semble qu'il y ait eu des amélioration de restrict en 4.5) ne réalise pas l'optimisation permise par restrict ci-dessus.

    Par contre, on peut forcer gcc 3.4.5 à réaliser l'optimisation en faisant une copie du contenu de lpDelta dans une variable locale. Le code C paraît alors moins optimisé, mais le code machine généré est comme celui ci-dessus "avec restrict".

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void updatePoints1(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      int i;
      MY_POINT delta;
     
      delta = *lpDelta;
     
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints[i].x += delta.x;
        lpPoints[i].y += delta.y;
      }
    }

    [ ************** edit 2 ************** ]
    gcc 4.6.1 réalise correctement l'optimisation avec le mot clé restrict.
    (Et il fait aussi l'optimisation en cas de copie locale de delta comme ci-dessus)

  5. #25
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Points : 28 119
    Points
    28 119
    Par défaut
    Citation Envoyé par rt15 Voir le message
    Bref, restrict, c'est une information qui en théorie peut bien aider le compilo à générer du code plus rapide. Il semble donc intéressant de l'avoir dans la norme.
    Je suis d'accord avec tes arguments sur l'utilite d'un tel outil pour un compilateur, mais pas dans un langage. Les directives de compilation, ca existe deja, mais je ne vois pas en quoi il est interessant d'ajouter au langage un mot-clef reserve aux optimisations du compilateur.

    Un autre exemple serait, par exemple, le fait de preciser si un test de boucle est probablement pris ou non (si mes souvenirs sont bons : pnt pour ceux qui ont fait de l'assembleur Sparc).
    Par exemple, dans le cas du test du retour d'un appel a malloc/calloc, il est peu probable que le code de traitement de l'erreur soit appele (ou dans ce cas, il est probable que l'on quitte le programme), et donc le fait que le code "probablement execute" soit proche et le code "probablement pas execute" soit loin est une information interessante, et les gains ne sont pas negligeables (non, je n'ai plus les informations).
    Mais est-ce que, pour autant, cette option de compilation doit etre introduite dans le langage sous forme de l'ajout d'un mot-clef ?
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  6. #26
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par gangsoleil Voir le message
    Mais est-ce que, pour autant, cette option de compilation doit etre introduite dans le langage sous forme de l'ajout d'un mot-clef ?
    Je me pose la question inverse : est-ce que ça pourrait être introduit avec seulement une option de compilation ?
    Ca semble bien être une option du langage sur les paramètres des fonctions de notre choix, pour indiquer au compilo l'optimisation à réaliser. Je vois pas comment le compilo pourrait en être conscient avec une simple option de compilation, tout en gardant le fonctionnement possible sans le restrict de temps en temps (ou inversement).

    edit : jean-marc ci-dessous a parfaitement réécrit ma pensée.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #27
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par gangsoleil Voir le message
    Mais est-ce que, pour autant, cette option de compilation doit etre introduite dans le langage sous forme de l'ajout d'un mot-clef ?
    Ajouter restrict peut changer un programme valide en un programme non conforme (supprimer un restrict ne devrait pas le faire), ça peut donc sembler du ressort du langage plus que des annotations.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  8. #28
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Points : 28 119
    Points
    28 119
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    Ajouter restrict peut changer un programme valide en un programme non conforme
    Je comprends bien cet argument, et suis relativement d'accord avec. Mais le C est, a la base, un langage permettant de faire tout ce que l'on veut (y compris le pire), sans verification.
    On peut citer par exemple realloc, souvent mal utilise au point que si l'allocation echoue, il y a une fuite memoire, ou bien encore le fait que les commandes memcpy et memmov existent en parallele.

    Oui, il est tout a fait possible de modifier le C pour que realloc se comporte a ne pas modifier le pointeur si l'allocation echoue, ou bien encore de supprimer memcpy pour ne garder que memmov qui fonctionne dans tous les cas, ou bien d'introduire de nouveaux mots-clef pour que le developpeur puisse ajouter une restriction ici ou la.
    Dans certains cas, comme avec restrict, ce gain sera visible dans l'executable, ce qui peut sembler une bonne chose. Dans d'autres cas, ce gain serait visible en refusant par exemple de compiler certaines erreurs classiques, ce qui eviterait de voir ici des posts du genre "mon programme segfault alors qu'il est correct".
    Mais est-ce que le gain est reel ?
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  9. #29
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 603
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 603
    Points : 17 913
    Points
    17 913
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par gangsoleil Voir le message
    Mais est-ce que le gain est reel ?
    That is the question...

    Et c'est bien ça qui m'embête : à ajouter inline, restrict, etc etc, on finit par transformer un langage de programmation en un langage où il y a mélange entre programmation et instructions d'optimisation de compilateur..

    Ce qui, de mon point de vue, est une extrême mauvaise chose, ne povant qu'entraîner non seulement un usage abusif, non compatible, additionné d'un mélange des concepts, et surtout une fainéantise intellectuelle qui va à l'encotre de la philosophie du C..

    Je ré-itère que c'est bien pour discuter des options d'optimisation qu'il y a des flags O2 ou O3..

    Si je parlais en "OO", je dirais que la classe "programme" n'a pas à se soucier de la classe "exécutable" ni de sa méthode assoiciée "build"...
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  10. #30
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    J'ai du mal à comprendre pourquoi certains ne "veulent" pas que ça soit un mot-clé du langage, car je ne vois pas comment le compilateur pourrait deviner le non chevauchement des entités... Si on souhaite avoir un tel mécanisme, il doit obligatoirement passer par un mot clé du langage, non ?

    Après, j'entends parfaitement les arguments disant que ce n'est pas le rôle du langage de faire des optimisations mais au compilateur de se débrouiller. En même temps, il existe bien des directives #pragma pour influencer le compilateur (je ne m'en suis jamais servi perso ). On a aussi la possibilité de mettre un mot clé const à l'un des paramètres d'une fonction pour bloquer à la compilation quelque chose comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void maFonction(int const *a)
    {
    	(*a)++;
    }
    Ce genre de petits bonus dans le code me fait penser aux hints qu'on peut mettre dans le code SQL. Normalement, c'est le SGBD qui décide des plans d'exécutions mais il est parfois très utile de forcer un comportement quand on sait qu'il sera bénéfique.

    Je trouve dommage de se priver d'"outils" pour faciliter la programmation ou améliorer la performance sous couvert d'une envie d'excellence, voire d'élitisme :
    Citation Envoyé par souviron34
    Ce qui, de mon point de vue, est une extrême mauvaise chose, ne povant qu'entraîner non seulement un usage abusif, non compatible, additionné d'un mélange des concepts, et surtout une fainéantise intellectuelle qui va à l'encotre de la philosophie du C..
    Je suis d'accord qu'il faut savoir ce qu'on fait et être exigeant. Je ne suis pas sûr qu'ajouter un mot-clé comme restrict (et au contraire de const ?) amène à la fainéantise, bien au contraire : tu ne peux pas le mettre au hasard et te disant "au pire ça fera rien" (comme le disait TJean-Marc.Bourguet).

  11. #31
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 150
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 150
    Points : 28 119
    Points
    28 119
    Par défaut
    Citation Envoyé par Bktero Voir le message
    J'ai du mal à comprendre pourquoi certains ne "veulent" pas que ça soit un mot-clé du langage [...]
    A mon sens, la question n'est pas de savoir si cela doit etre ou non un mot-clef du langage, mais si cela fait sens d'ajouter des mots-clefs pour surcharger le C.

    Si on veut lister tous les concepts que l'on pourrait ajouter au C, on peut discuter longtemps : pourquoi ne pas ajouter un systeme de gestion des listes chainees, c'est quand meme vachement pratique. On pourrait aussi, pourquoi pas, ajouter la surcharge des operateurs, il y a des gens qui ont l'air de trouver ca vraiment genial. ...

    Oui, une norme peut evoluer, ce n'est pas ce que je critique. Ce que je critique, ce sont les evolutions proposees dans cette nouvelle version, car je trouve que la plupart n'ont pas de raisons d'etre, et que d'autres manquent. Par exemple, pour la gestion des threads, que va-t-il se passer avec la gestion des signaux, sachant que threads et signaux ne font pas du tout bon menage ? C'est au compilateur de s'en debrouiller, ou bien au developpeur, ou bien au systeme ?

    [troll]
    Et puis lorsqu'on voit comment a ete implementee C99, et que l'on se dit que C11 est dans la droite ligne de son predecesseur, on se dit que de toute maniere, C11 sera aussi inimplementee que C99, et que donc ce n'etait meme pas la peine de la sortir
    [/troll]
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  12. #32
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Bktero Voir le message
    J'ai du mal à comprendre pourquoi certains ne "veulent" pas que ça soit un mot-clé du langage,
    Moi ce que j'ai parfois du mal à comprendre, c'est l'objet des interventions. On peut, sur restrict par exemple,
    - ne pas comprendre à quoi ça sert,
    - comprendre à quoi ça sert et préférer que l'objectif soit atteint d'une autre manière
    - comprendre à quoi ça sert et trouver que ce n'est pas assez important pour être normalisé.
    et certains s'expriment -- pour donner force à leur propos ? -- de manière ambigue ("je ne comprends pas pourquoi on a ajouté restrict" est quelque chose qui peut être écrit pour signifier les trois positions).

    Ma position est la troisième (C99 a eu un très important accent numérique -- restrict, complex, des fonctions mathématiques -- qui a un intérêt relativement faible dans son domaine historique par rapport à la complexité que ça ajoute). Mais à partir du moment où c'est normalisé, le mot clé me semble la bonne méthode.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  13. #33
    Membre éclairé Avatar de rt15
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2005
    Messages
    262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2005
    Messages : 262
    Points : 665
    Points
    665
    Par défaut
    Les développeurs C++ bavaient devant C++11. Ils trépignent en attendant que les compilos se mettent à jour.

    Les développeurs C en sont encore à critiquer C99. Certains se demandent même si une quelconque évolution est nécessaire voire bénéfique.


    Perso, je ne sais pas ce que j'aurai aimé voire dans C11.

  14. #34
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 603
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 603
    Points : 17 913
    Points
    17 913
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par rt15 Voir le message
    Considérons une fonction plus proche de ce qui peut exister, c'est à dire une fonction updatePoints qui prend en paramètre :
    1/ Un tableau de points(Coordonnées x et y).
    2/ Le nombre d'élément du tableau.
    3/ Un pointeur vers un point delta.
    Et dis-moi ce que ton compilo te donnes en assembleur pour


    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void updatePoints1(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      int i, j, k;
     
       j = lpDelta->x ;
       k = lpdelta->y ;
     
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints->x += j;
        lpPoints->y += k;
        lpPoints++ ;
      }
    }

    ou bien

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void updatePoints1(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      register int i, j, k;
      register MY_POINT *ptr = lpPoints ;
     
       j = lpDelta->x ;
       k = lpdelta->y ;
     
      for (i = 0; i < nPointCount; i++)
      {
        ptr->x += j;
        ptr->y += k;
        ptr++ ;
      }
    }



    PS : ton "optimisation" montrée avec restrict est détruite par l'usage des indices dans le tableau : à chaque point tu fais deux multiplications et une addition pour calculer l'adresse, ce qui est nettement plus lent que l'optimisation montrée par l'usage de restrict...

    Donc, c'est comme vouloir otimiser des if ou des switch.. .

    L'optimisation est principalement dans l'algo et la réflexion (comme pour le calcul d'adresse ci-dessus : tu dois le faire 5 milliards de fois, ça coûte 10 milliards de multiplications de plus qu'incrémenter un pointeur...)



    Citation Envoyé par Bktero Voir le message
    Je trouve dommage de se priver d'"outils" pour faciliter la programmation ou améliorer la performance sous couvert d'une envie d'excellence, voire d'élitisme :
    Je trouve simplement dommage, comme le disait gangsoleil, de mofieir une norme afin d'ajouter des choses qui d'une part n'ont rien à faire dans un langage de programmation et qui d'autre part peuvent être faites autrement ou ailleurs, là où c'est leur place normale....

    Maintenant, je vais pas me batre dessus, mais je trouve simplement qu'on dérive vers une fois de plus (ce que ça n'était pas) un truc de geeks..c'est dans l'air du temps, ça ne m'empêche pas de ne pas être d'accord..


    Citation Envoyé par rt15 Voir le message
    Les développeurs C++ bavaient devant C++11. Ils trépignent en attendant que les compilos se mettent à jour.
    Ben c'est peut-être ça le problème : des gens habtiués au C++ qui veulent changer C pour se sentir à l'aise
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  15. #35
    Membre éclairé Avatar de rt15
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2005
    Messages
    262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2005
    Messages : 262
    Points : 665
    Points
    665
    Par défaut
    Citation Envoyé par souviron34 Voir le message
    Et dis-moi ce que ton compilo te donnes en assembleur pour
    Bin... Avec gcc 4.6.1, il me génère exactement la même chose qu'avec restrict. Il n'inclus bien sûr pas la lecture de lpDelta à chaque tour de boucle du fait de la copie. Et il génère les mêmes adds (Alors qu'on pourrait s'attendre à ce qu'il incrémente les "pointeurs") :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    add     [ebx+eax*8],edx
    add     [ebx+eax*8+0x4],ecx
    Je verrais plus tard si déplacer les pointeurs comme tu le fais dans ton C est plus rapide une fois retranscrite tel quel assembleur. Mais ce n'est pas dit... En effet, le eax * 8 a un cout probablement très faible. "8" est le scale qui ne peut valoir que 1, 2, 4 ou 8. Cette "multiplication" ne coûte peut être strictement rien...

  16. #36
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 603
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 603
    Points : 17 913
    Points
    17 913
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par rt15 Voir le message
    Bin... Avec gcc 4.6.1, il me génère exactement la même chose qu'avec restrict....


    Exactement ce que je disais..

    Donc il n'y avait pas besoin de ce mot, pusiue avec ce qui existait on peut le faire
    "Un homme sage ne croit que la moitié de ce qu’il lit. Plus sage encore, il sait laquelle".

    Consultant indépendant.
    Architecture systèmes complexes. Programmation grosses applications critiques. Ergonomie.
    C, Fortran, XWindow/Motif, Java

    Je ne réponds pas aux MP techniques

  17. #37
    Membre éclairé Avatar de rt15
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2005
    Messages
    262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2005
    Messages : 262
    Points : 665
    Points
    665
    Par défaut
    Bench. Six écritures différentes de la fonction.
    1/ C, implémentation innocente.
    2/ C, copie de lpDelta (Ce qui revient à utiliser restrict).
    3/ C, déplacement du pointeur de souviron34.
    4/ ASM, stupide, relecture de lpDelta à chaque tour de boucle.
    5/ ASM, en utilisant le scale.
    6/ ASM, en déplaçant le pointeur.

    Eh bin sous VC6, paramètre par défaut de compilation en release (/O2 apparemment), processeur intel core 2 cadencé à 2.33Ghz, ça donne égalité "parfaite". Le compilo a l'air pas mauvais et le processeur semble effacer les petites différences. Pour espérer gratter, faudrait regarder du côté des threads ou des d'instructions SIMD (Les gains ou pertes dépendrait beaucoup du nombre de points).

    C'est vrai que vu sous cet angle, restrict, c'est pas franchement indispensable...

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    #include <stdio.h>
    #include <windows.h>
     
    #define POINT_COUNT 40000000
    #define FUNCTION_TO_TEST_COUNT 6
    #define TEST_COUNT 20
     
    typedef struct
    {
      int x;
      int y;
    }
    MY_POINT;
     
    /* Proto des fonctions à tester */
    typedef void (__stdcall* FUNCTION_TO_TEST) (MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta);
     
    /* Contient le nom d'une fonction à tester et un pointeur sur celle-ci */
    typedef struct _FUNCTION_TO_TEST_INFO
    {
      char name[64];
      FUNCTION_TO_TEST function;
      int duration;
    }
    FUNCTION_TO_TEST_INFO;
     
    /* Implémentation candide */
    void __stdcall UpdatePointsSimple(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      int i;
     
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints[i].x += lpDelta->x;
        lpPoints[i].y += lpDelta->y;
      }
    }
     
    /* Implémentation avec copie de lpDelta (Revient à utiliser restrict) */
    void __stdcall UpdatePointsCopyDelta(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      MY_POINT delta;
      int i;
     
      delta = *lpDelta;
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints[i].x += delta.x;
        lpPoints[i].y += delta.y;
      }
    }
     
    /* Plutôt que d'accèder par indice à lpPoints, on fait progresser un pointeur */
    void __stdcall UpdatePointsMovePointer(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      register int i, j, k;
      register MY_POINT *ptr = lpPoints ;
     
       j = lpDelta->x ;
       k = lpDelta->y ;
     
      for (i = 0; i < nPointCount; i++)
      {
        ptr->x += j;
        ptr->y += k;
        ptr++ ;
      }
    }
     
    /* Code assembleur rechargeant depuis lpDelta */
    __declspec(naked) void __stdcall UpdatePointsAsmReloadDelta(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      __asm
      {
        push edi
        push esi
        mov ecx, [esp + 16]            /* ecx = nPointCount    */
        mov esi, [esp + 20]            /* esi = lpDelta        */
        mov edi, [esp + 12]            /* edi = lpPoints       */
    next:
        jecxz the_end                  /* Fin de loop si i = 0 */
        dec ecx                        /* i--                  */
        mov eax, [esi]                 /* eax = lpDelta.x      */
        mov edx, [esi + 4]             /* edx = lpDelta.y      */
        add [edi + ecx * 8], eax       /* lpPoints[i].x = eax  */
        add [edi + ecx * 8 + 4], edx   /* lpPoints[i].y = edx  */
        jmp next
    the_end:
        pop esi
        pop edi
        ret 12
      }
    }
     
    /* Code assembleur utilisant le scale */
    __declspec(naked) void __stdcall UpdatePointsAsmWithScale(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      __asm
      {
        push edi
        mov ecx, [esp + 12]            /* ecx = nPointCount    */
        mov edi, [esp + 16]            /* edi = lpDelta        */
        mov eax, [edi]                 /* eax = lpDelta.x      */
        mov edx, [edi + 4]             /* edx = lpDelta.y      */
        mov edi, [esp + 8]             /* edi = lpPoints       */
    next:
        jecxz the_end                  /* Fin de loop si i = 0 */
        dec ecx                        /* i--                  */
        add [edi + ecx * 8], eax       /* lpPoints[i].x = eax  */
        add [edi + ecx * 8 + 4], edx   /* lpPoints[i].y = edx  */
        jmp next
    the_end:
        pop edi
        ret 12
      }
    }
     
    /* Code assembleur faisant progresser le pointeur */
    __declspec(naked) void __stdcall UpdatePointsAsmMovePointer(MY_POINT *lpPoints, int nPointCount, MY_POINT *lpDelta)
    {
      __asm
      {
        mov ecx, [esp + 8]             /* ecx = nPointCount        */
        test ecx, ecx                  /* Vérification nPointCount */
        jz the_end
     
        push edi
     
        mov edi, [esp + 16]            /* edi = lpDelta            */
        mov eax, [edi]                 /* eax = lpDelta.x          */
        mov edx, [edi + 4]             /* edx = lpDelta.y          */
        mov edi, [esp + 8]             /* edi = lpPoints           */
    next:
        add [edi], eax                 /* lpPoints->x = eax        */
        add [edi + 4], edx             /* lpPoints->y = edx        */
        add edi, 8                     /* lpPoints++               */
        dec ecx                        /* i--                      */
        jnz next                       /* loop si i > 0            */
     
        pop edi
    the_end:
        ret 12
      }
    }
     
    /* Liste des fonctions à tester */
    FUNCTION_TO_TEST_INFO updatePoints[FUNCTION_TO_TEST_COUNT] =
    {
      {"UpdatePointsSimple",         UpdatePointsSimple, 0},
      {"UpdatePointsCopyDelta",      UpdatePointsCopyDelta, 0},
      {"UpdatePointsMovePointer",    UpdatePointsMovePointer, 0},
      {"UpdatePointsAsmReloadDelta", UpdatePointsAsmReloadDelta, 0},
      {"UpdatePointsAsmWithScale",   UpdatePointsAsmWithScale, 0},
      {"UpdatePointsAsmMovePointer", UpdatePointsAsmMovePointer, 0}
    };
     
    void InitPoints(MY_POINT *lpPoints, int nPointCount)
    {
      int i;
      for (i = 0; i < nPointCount; i++)
      {
        lpPoints[i].x = i;
        lpPoints[i].y = 100 + i;
      }
    }
     
    int main()
    {
      MY_POINT* lpPointsReference;
      MY_POINT* lpPoints;
      MY_POINT delta;
      DWORD nBeginning;
      DWORD nDuration;
      int nResult;
      int i, j, k;
     
      nResult = 1;
     
      lpPointsReference = (MY_POINT*)malloc(POINT_COUNT * sizeof(MY_POINT));
      if (!lpPointsReference)
      {
        puts("Malloc failed");
        goto the_end;
      }
      lpPoints = (MY_POINT*)malloc(POINT_COUNT * sizeof(MY_POINT));
      if (!lpPoints)
      {
        puts("Malloc failed");
        goto free_reference;
      }
     
      delta.x = 1;
      delta.y = 2;
     
      /* On fait une référence pour s'assurer des résultats des différentes fonctions */
      InitPoints(lpPointsReference, POINT_COUNT);
      UpdatePointsSimple(lpPointsReference, POINT_COUNT, &delta);
     
      /* On réalise plusieurs fois le test pour limiter les effets de bords */
      for (k = 0; k < TEST_COUNT; k++)
      {
        printf("*** Test %d ***\n", k);
     
        /* Pour toutes les fonctions à tester */
        for (i = 0; i < FUNCTION_TO_TEST_COUNT; i++)
        {
          InitPoints(lpPoints, POINT_COUNT);
          printf("Testing %s\n", updatePoints[i].name);
     
          nBeginning = GetTickCount();
          updatePoints[i].function(lpPoints, POINT_COUNT, &delta);
          nDuration = GetTickCount() - nBeginning;
          updatePoints[i].duration += nDuration;
          printf("%d\n", nDuration);
     
          /* Vérification du résultat */
          for (j = 0; j < POINT_COUNT; j++)
            if ((lpPointsReference[j].x != lpPoints[j].x) || (lpPointsReference[j].y != lpPoints[j].y))
            {
              puts("Difference between result and reference");
              goto free_points;
            }
        }
        puts("");
      }
      /* Affichage des temps moyens */
      for (i = 0; i < FUNCTION_TO_TEST_COUNT; i++)
      {
        printf("%s average duration = %d\n", updatePoints[i].name, updatePoints[i].duration / TEST_COUNT);
      }
      nResult = 0;
     
    free_points:
      free(lpPoints);
    free_reference:
      free(lpPointsReference);
    the_end:
      return nResult;
    }

  18. #38
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    La page Wikipédia mentionne les préfixes de chaîne littérales pour Unicode.
    J'espère qu'on va pouvoir les chaîner sans répéter sans arrêt le préfixe maintenant...

    Edit: Oui enfin, on peut (Chapitre 6.4.5 "string literals", versets 5 et 9)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  19. #39
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    News: Microsoft a confirmé il y a deux mois que les développeurs C99 pouvaient aller se faire voir.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  20. #40
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Si GCC a réussi à le faire, est-ce que c'est si compliqué pour Microsoft de le faire ?

    J'ai du mal à comprendre pourquoi les gens (car malheureusement, ce n'est pas que Microsoft) restent bloqués au C ANSI, qui a plus de 20 ans.

Discussions similaires

  1. Qu'est ce qu'un grand projet ?
    Par Geronimo dans le forum Débats sur le développement - Le Best Of
    Réponses: 62
    Dernier message: 04/04/2013, 14h52
  2. Réponses: 33
    Dernier message: 14/11/2011, 09h27
  3. [bash] découper un fichier -> agir sur un fichier dés qu'il est achevé
    Par AnozerOne dans le forum Shell et commandes GNU
    Réponses: 5
    Dernier message: 21/06/2011, 20h31
  4. Réponses: 17
    Dernier message: 30/10/2010, 17h49
  5. Quelle est la fiabilité du protocole SSL ?
    Par Anonymous dans le forum Développement
    Réponses: 5
    Dernier message: 05/09/2002, 13h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo