Aujourd'hui, je faisais l'exercice de la bague de fil du livre EM> Erlang EM> et googlé pour d'autres solutions à comparer. J'ai constaté que la fusillade de la langue a exactement le même problème qu'un Benchmark a>. J'ai eu l'impression que c'est une zone où Erlang devrait être rapide, mais s'avère que c et C ++ sont à nouveau sur le dessus. Mon soupçon est que les programmes C / C ++ ne suivent pas les règles qui disent "passent le jeton du thread à thread". Après les avoir lu, il semble que les deux manipulent certaines de la mémoire partagée et des variables mondiales différentes du code Erlang, mais je pourrais me tromper. P>
Ma question est la suivante: font-ils la même chose ou le code C / C ++ est conceptuellement différent (et plus rapide) de l'Erlang One? P>
Et une autre question: Pourquoi Haskell est-il plus rapide que Erlang lorsque les solutions sont très similaires? P>
7 Réponses :
La version C utilise LWP, que je pense est une bibliothèque de filetage de l'espace utilisateur. Dans quelle mesure cela est "injuste", c'est à la recherche de débats: je regarderais des choses comme si elle prend en charge la vraie concurrence préventive en ce sens que vous pouvez faire des appels de système de blocage dans un fil sans bloquer tous les autres threads (vous pouvez Faites cela à Haskell, pouvez-vous à Erlang?). P>
Les threads de Haskell sont légèrement plus légers que ceux d'Erlang, car comme je le comprends, un thread Erlang est livré avec un tas local (dans la mise en œuvre standard), tandis que Haskell threads tous partagent le même tas. P>
Minimal. Vous pouvez créer des milliers de threads sans rencontrer de problèmes de mémoire.
Combien, comparer avec 300 octets d'Erlang?
Un thread de GHC est représenté par un objet State thread: Hackage .HASKELL.ORG / TRAC / GHC / Wiki / Commentaire / RTS / Stockage / ... . Vous pouvez faire les mathématiques vous-même en regardant la définition: Darcs.hakell. Org / GHC / Comprend / RTS / STOCKAGE / TSO.H
@DJV 68 octets sur une machine 32 bits, 112 octets sur une machine 64 bits (peut varier légèrement en fonction de la version de GHC). Plus la pile, qui est variable et étendue à la demande: par défaut, nous allouons des piles de 1k.
En fin de compte, la transmission de messages sur des machines modernes est mise en œuvre à l'aide d'une forme de mémoire partagée pour transmettre les messages (avec des verrous ou des instructions atomiques). Donc, toutes les implémentations C et C ++ font réellement incroyent la mise en œuvre de messages passant directement dans leur code. Un indice de référence similaire qui utilise une bibliothèque de messages rapides en C, également comparé contre Haskell et Erlang, peut être trouvé dans cet article: http://www.cs.kent.ac.uk/pubs/2009/2928/index.html (Section 5.1) P>
La vitesse des différentes approches est vraiment déterminée par les systèmes d'exécution concurrents impliqués. Haskell a eu beaucoup de bon travail dans cette région, ce qui le laisse devant Erlang. Bien entendu, la vitesse de mesure des micro-repères est souvent erronée et laisse des facteurs importants comme la lisibilité du code. Une question à garder à l'esprit pourrait être: laquelle des solutions dans la fusillade seriez-vous heureux de maintenir? P>
dépend de lequel on gagne plus d'argent :)
>> Bien entendu, la vitesse de mesure sur les micro-repères est souvent erronée et laisse des facteurs importants comme la lisibilité du code. << Quelle est l'une des raisons pour lesquelles le jeu de référence montre le code source du programme ;-)
Je ne pense pas que j'appellerais ça tricher. La différence principale et fondamentale entre plusieurs threads et plusieurs processus est que plusieurs threads partagent un espace d'adresses unique. En tant que tel, spécifier de multiples threads (plutôt que plusieurs processus) me semble que la permission tacite profite de l'espace d'adresses partagé (au moins en l'absence d'une définition très spécifique de «passage» qui l'interdisait). P>
Ce qu'il va, c'est que: Erlang n'a pas vraiment de threads, en tant que tels, il possède des processus avec des communications asynchrones. Les processus sont (intentionnellement) isolés les uns des autres dans une large mesure. D'une part, cela rend le développement considérablement plus facile - en particulier, un processus ne peut affecter que d'une autre via des canaux de communication spécifiques et bien définis. Sous la hotte, il utilise de nombreuses astuces (y compris une mémoire partagée presque certainement) pour optimiser ses processus et tirer parti de ce qui est possible dans une implémentation / une situation spécifique (telle que tous les processus exécutés dans un seul espace d'adresse partagé). Néanmoins, devoir garder toutes les astuces cachées empêche d'être aussi efficace que quelque chose comme la version C où les "astuces" sont toutes explicites et complètement exposées. P>
J'utiliserais une analogie réelle pour expliquer la différence. Pensez aux threads / processus en tant que personnes. Erlang applique une relation de travail professionnelle où les communications sont toutes soigneusement enregistrées dans les mémos. Les versions C et C ++ sont plus comme un mari et une femme qui pourraient communiquer avec un seul mot qui ne veut rien dire à personne d'autre, voire un seul coup d'œil rapide. P>
Ce dernier est extrêmement efficace quand il fonctionne - mais c'est beaucoup plus ouvert aux malentendus subtils et si les deux ont un combat, vous ne voulez probablement pas être dans la même pièce. Pour le responsable, les personnes dans des relations purement professionnelles sont beaucoup plus faciles à gérer même si leur efficacité maximale n'est pas assez élevée. P>
Ne pas suivre les règles p>
Compte tenu du nombre d'approches vraiment différentes de la programmation de la simultanation, j'ai trouvé qu'il est très difficile d'être suffisamment inclusif pour apporter différentes implémentations linguistiques et à conserver une certaine comparabilité vague. P>
Regardez maintenant. La performance des mêmes programmes mesurés avec Configuration de l'heure d'exécution différente et notez combien cela compte - p>
xxx pré> blockquote>
Pourquoi ne pas nécessiter d'utiliser une abstraction de threads entièrement préventive? (aucun fil ne peut empêcher tout autre de faire des progrès)
Sans envisager de savoir si ce serait une "bonne chose", lequel des 10 premiers programmes serait exclu?
En ce qui concerne les résultats de base plus lents pour GHC, sommes-nous autorisés à utiliser l'affinité de processeur pour améliorer cela?
Je ne sais pas combien de programmes, le cas échéant, seraient exclus en exigeant des threads préventifs. Cela semble être une exigence raisonnable, cependant, la référence a la plus de valeur si elle mesure la performance d'une abstraction utile à la majorité des utilisateurs. Les fils non préventifs ont leurs utilisations, mais je pense que la plupart des utilisateurs auraient besoin de préemption.
Les fils de GHC ne pré-nettoient toujours pas avant une allocation, n'est-ce pas?
Essayez de regarder cette table: shootout.alioth.debian.org/ U64 / ... . Cela vous indique beaucoup plus sur l'adéquation de la langue de ce type de tâches. Je pense que c'est beaucoup plus ce que la matière, la maintenabilité b>.
@Simon Marlow - autorisé à utiliser l'affinité de processeur pour améliorer cela? - C'est une bonne question. Pouvez-vous définir l'affinité de processeur et utiliser toujours + rts -n4 -rts?
@Simon Marlow >> Je ne sais pas combien de programmes, le cas échéant, seraient exclus en nécessitant des threads préventifs. << Et vous pensez que je vais être capable de le comprendre? :-)
@Ganesh c'est vrai. Je considère que c'est un bug. La plupart des programmes concernés sont des points de repère, cependant.
@igouy pour cette référence Nous avons juste besoin de réparer les threads à un seul noyau avec Forkonio, aucun drapeau ne devrait être nécessaire. Vous pouvez également corriger des threads OS aux noyaux avec + RTS -qa, ce qui pourrait aider un peu.
Le programme C ++ définit l'affinité du processeur.
@Simon Marlow >> Forkonio << Laissez-moi vous demander une question stupide - pouvez-vous avoir une partie du programme épinglé à un noyau particulier et le reste du programme à l'aide de plusieurs cœurs?
@iguoy sûr. Forkonio crée un fil de haskell qui est épinglé sur un noyau pour sa vie et Forkio crée un fil de haskell migré automatiquement par le temps d'exécution pour équilibrer la charge.
@Simon Marlow >> Sommes-nous autorisés à utiliser l'affinité de processeur pour améliorer cela? << Oui Utilisez Forkonio.
@Igouy je vois qu'il utilise déjà Forkonio :) Le ralentissement par rapport à la version à 1 cœurs est donc dû à la surcharge du verrouillage supplémentaire dans les opérations MVAR et de fil dans la version filetée du temps d'exécution.
@Simon Marlow - Oh! C'est donc une différence "authentique".
@iguoy dans un sens YES - Cependant, le programme C ici se déroule ici dans un seul fil de système d'exploitation, de sorte que si cela est autorisé, le programme HASKELL doit être autorisé à supprimer le drapeau -Hraft, apportant sa performance. Cependant, je pense qu'une autre position défendable est de disqualifier ce programme C sur le motif qu'il n'utilise pas de threads préventifs (j'ai vérifié: LWP est une abstraction de filetage coopérative) et déplacez-la aux programmes "intéressants alternatifs".
@Simon Marlow >> Le motif qu'il n'utilise pas les threads préventifs << sauf qu'il n'exige pas d'utiliser des threads préventifs.
@Simon Marlow - Au lieu de cela, quelle tâche serait-elle plus rapide des threads de préemption?
@iguoy "sauf qu'il n'exige pas d'utiliser des threads préventifs" sûr - c'était juste une suggestion. Si vous décidez de conserver le programme C tel quel, je pense que c'est raisonnable pour nous de demander que le programme HASKELL soit compilé sans-organiser.
@iguoy "à la place, quelle tâche serait la tâche des threads préemptifs performants" Je ne suis pas sûr de comprendre ce que vous demandez ici. Le point de préemption est qu'il s'agit d'une abstraction plus utile: l'abstraction donne des garanties plus fortes de l'équité, il est donc plus facile de programmer dans la pratique. Sans préemption, il est facile de rédiger accidentellement des programmes que l'impasse. La plupart des gens veulent une préemption lorsqu'ils utilisent des threads.
Je pense que votre suggestion a suffisamment de mérite (même si cela ne fait pas fonction de coïncidence un programme de haskell au sommet).
Pourquoi Haskell est-il plus rapide que Erlang lorsque la solution est très similaire? P> blockQuote>
HASKELL GHC est une implémentation de langage optimisée compilée et indigène avec des fils très rapides. Il est généralement beaucoup plus rapide que Erlang / Hipe. Erlang n'a pas de monopole sur des filets légers: -) p>
Je répondrais par une autre question: comment est-ce que le temps d'exécution Erlang est mis en œuvre sous la hotte? p>
Il y a des chances qu'il est mis en œuvre en C ou un langage de système similaire (je doute qu'ils ont fait tout dans l'assemblage). Ou à tout le moins, que les concepts qu'ils expriment puissent être exprimés aussi efficacement en c. P>
Maintenant, pourquoi le trouvez-vous si étrange qu'une version C optimisée (la fusillade ne montre certainement pas le code de niveau moyen) battrait la version erlang, en considérant que Erlang ajoute son propre niveau de complexité / indirection? P>
Quel que soit le type de référence, il est toujours possible pour une implémentation C de battre le programme le plus poli dans une autre langue ... construit sur C, simplement parce que vous prenez la version C qu'il génère alors supprime les bits que vous avez pas besoin. p>
D'autre part: p>
parfois "plus rapide" ne vaut pas le coût. P>
Oui, c'était ma question si les différentes solutions sont au même niveau d'abstraction. Je pense que nous sommes tous d'accord pour dire qu'ils ne sont pas dans ce cas. Quant à vos questions - ce n'est certainement pas les programmes C / C ++.
Une chose à noter dans cette référence est qu'il n'ya qu'un seul jeton à transmettre. Ce qui signifie que dans la pratique, il s'agit d'un seul programme fileté de lecture et d'écriture de / à la mémoire. P>
Je m'attendrais à ce que le résultat soit différé différent sur une machine multiprocesseur (ou faire une grappe) où les threads / processus doivent transmettre des jetons M autour de certains ordre aléatoire. P>
hmm. Donnez également aux développeurs des solutions de référence correspondant au nombre d'heures pour terminer leur solution. Ensuite, je m'attendrais à ce que Erlang sort sur le dessus (ou près du sommet au moins). P>
"Une chose à noter" +1 "puis je m'attendrais à" -1
Vous avez probablement raison. Cela suppose un peu trop. Merci pour votre commentaire.
Les repères qui peuvent sembler être simultanés sont souvent séquentiels. Le point de repère en est, par exemple, est entièrement séquentiel. Ainsi est aussi la mise en œuvre la plus courante du "Benchmark"; Habituellement, un processus est actif, tandis que les autres attendent dans une déclaration de réception. ' erlang.org/doc/efficient_guide/processes.html#id66266
Certaines de ces comparaisons sont celles des pommes et des oranges. La raison en est que si vous voulez seulement que les choses puissent aller rond très rapide, alors bien sûr, vous pouvez le faire autant plus rapide en C et C ++. Le point IMHO est que Erlang gère beaucoup plus encore alors simplement "envoyer un jeton entre les processus" (ce sont des processus BTW non threads;)). J'ai écrit un article sur certaines des différences il y a un moment; Vérifiez la peine de choisir: forum.Trapexit.org/viewtopic.php?t=15194 et Trapexit.org/process_ring_across_nodes
C'est un bon exemple et la définition que vous donnez est quelque chose que je voudrais attendre de toutes les solutions. Quelqu'un a-t-il essayé de le réécrire dans une autre langue, des résultats? De plus, pouvez-vous penser à une référence à monoconque où Erlang pourrait exceller en termes de vitesse?
Le point Joe Armstrong faisait était simplement "le passage du message entre les processus de sommeil à Erlang est très rapide. ... Java est d'environ 5 à 10 fois plus lent." (Les programmeurs Java ont réduit cette différence.) [Mesures de performance des threads en Java et processus à Erlang 1998-11-02]