web-dev-qa-db-fra.com

Performances F # en calcul scientifique

Je suis curieux de savoir comment les performances F # se comparent aux performances C++? J'ai posé une question similaire en ce qui concerne Java, et l'impression que j'ai eue était que Java ne convient pas pour les calculs de nombres importants.

J'ai lu que F # est censé être plus évolutif et plus performant, mais comment ces performances réelles se comparent-elles à C++? les questions spécifiques concernant la mise en œuvre actuelle sont:

  • Comment fonctionne-t-il en virgule flottante?
  • Permet-il des instructions vectorielles
  • dans quelle mesure est-il convivial pour l'optimisation des compilateurs?
  • Quelle est la taille d'une empreinte mémoire? Permet-il un contrôle précis sur la localité de la mémoire?
  • a-t-il une capacité pour les processeurs de mémoire distribuée, par exemple Cray?
  • quelles caractéristiques possède-t-il qui peuvent être d'intérêt pour la science informatique lorsque le traitement des nombres lourds est impliqué?
  • Existe-t-il de réelles implémentations de calcul scientifique qui l'utilisent?

Merci

72
Anycorn
  • F # effectue le calcul en virgule flottante aussi rapidement que le .NET CLR le permet. Pas beaucoup de différence par rapport à C # ou à d'autres langages .NET.
  • F # n'autorise pas les instructions vectorielles par lui-même, mais si votre CLR dispose d'une API pour celles-ci, F # ne devrait pas avoir de problèmes à l'utiliser. Voir par exemple Mono .
  • Pour autant que je sache, il n'y a qu'un seul compilateur F # pour le moment, alors la question devrait peut-être être "quelle est la qualité du compilateur F # en matière d'optimisation?". La réponse est en tout cas "potentiellement aussi bonne que le compilateur C #, probablement un peu pire pour le moment". Notez que F # diffère de par ex. C # dans son support pour l'inlining au moment de la compilation, ce qui permet potentiellement un code plus efficace qui repose sur des génériques.
  • Les empreintes de mémoire des programmes F # sont similaires à celles des autres langages .NET. Le contrôle que vous avez sur l'allocation et la récupération de place est le même que dans d'autres langages .NET.
  • Je ne connais pas le support de la mémoire distribuée.
  • F # a des primitives très agréables pour traiter les structures de données plates, par exemple tableaux et listes. Regardez par exemple le contenu du module Array: map, map2, mapi, iter, fold, Zip ... Les tableaux sont populaires dans le calcul scientifique, je suppose en raison de leurs propriétés de localisation de mémoire intrinsèquement bonnes.
  • Pour les packages de calcul scientifique utilisant F #, vous voudrez peut-être regarder ce que fait Jon Harrop.
40
Joh

Je suis curieux de savoir comment les performances F # se comparent aux performances C++?

Varie énormément selon l'application. Si vous utilisez largement des structures de données sophistiquées dans un programme multithread, alors F # est susceptible d'être une grande victoire. Si la plupart de votre temps est consacré à des tableaux de mutation de boucles numériques étroites, alors C++ pourrait être 2 à 3 fois plus rapide.

Étude de cas: Ray tracer Mon benchmark ici utilise un arbre pour l'abattage hiérarchique et le code numérique d'intersection rayon-sphère pour générer une sortie image. Cette référence a plusieurs années et le code C++ a été amélioré des dizaines de fois au fil des ans et lu par des centaines de milliers de personnes. Don Syme de Microsoft a réussi à écrire une implémentation F # légèrement plus rapide que le code C++ le plus rapide lorsqu'il a été compilé avec MSVC et parallélisé à l'aide d'OpenMP.

J'ai lu que F # est censé être plus évolutif et plus performant, mais comment ces performances réelles se comparent-elles à C++?

Développer du code est beaucoup plus facile et plus rapide avec F # qu'avec C++, et cela s'applique à l'optimisation ainsi qu'à la maintenance. Par conséquent, lorsque vous commencez à optimiser un programme, la même quantité d'efforts produira des gains de performances beaucoup plus importants si vous utilisez F # au lieu de C++. Cependant, F # est un langage de niveau supérieur et, par conséquent, impose un plafond de performances inférieur. Donc, si vous avez un temps infini à consacrer à l'optimisation, vous devriez, en théorie, toujours être en mesure de produire du code plus rapide en C++.

C'est exactement le même avantage que C++ avait sur Fortran et Fortran avait sur l'assembleur manuscrit, bien sûr.

Étude de cas: décomposition QR Il s'agit d'une méthode numérique de base de l'algèbre linéaire fournie par des bibliothèques comme LAPACK. L'implémentation de référence de LAPACK est de 2 077 lignes de Fortran. J'ai écrit ne implémentation F # en moins de 80 lignes de code qui atteint le même niveau de performance. Mais l'implémentation de référence n'est pas rapide: les implémentations réglées par les fournisseurs comme la bibliothèque Math Kernel d'Intel (MKL) sont souvent 10 fois plus rapides. Remarquablement, j'ai réussi à optimiser mon code F # bien au-delà les performances de la mise en œuvre d'Intel fonctionnant sur du matériel Intel tout en gardant mon code sous 150 lignes de code et entièrement générique (il peut gérer la précision simple et double, et matrices complexes et même symboliques!): pour les matrices hautes et minces, mon code F # est jusqu'à 3 fois plus rapide que l'Intel MKL.

Notez que la morale de cette étude de cas n'est pas que vous devriez vous attendre à ce que votre F # soit plus rapide que les bibliothèques définies par le fournisseur, mais plutôt que même des experts comme Intel manqueront des optimisations productives de haut niveau s'ils utilisent uniquement des langages de niveau inférieur. Je soupçonne que les experts en optimisation numérique d'Intel n'ont pas réussi à exploiter pleinement le parallélisme parce que leurs outils le rendent extrêmement lourd alors que F # le rend sans effort.

Comment fonctionne-t-il en virgule flottante?

Les performances sont similaires à ANSI C mais certaines fonctionnalités (par exemple les modes d'arrondi) ne sont pas disponibles à partir de .NET.

Permet-il des instructions vectorielles

Non.

dans quelle mesure est-il favorable à l'optimisation des compilateurs?

Cette question n'a pas de sens: F # est un langage .NET propriétaire de Microsoft avec un seul compilateur.

Quelle est la taille d'une empreinte mémoire?

Une application vide utilise 1,3 Mo ici.

Cela permet-il un contrôle précis sur la localité de la mémoire?

Mieux que la plupart des langages à mémoire sûre, mais pas aussi bon que C. Par exemple, vous pouvez décompresser des structures de données arbitraires en F # en les représentant comme des "structures".

a-t-il une capacité pour les processeurs de mémoire distribuée, par exemple Cray?

Cela dépend de ce que vous entendez par "capacité". Si vous pouvez exécuter .NET sur ce Cray, vous pouvez utiliser le passage de message en F # (tout comme la langue suivante), mais F # est principalement destiné aux ordinateurs de bureau multicœurs x86.

quelles caractéristiques présente-t-elle qui pourraient intéresser la science informatique lorsque le traitement des nombres lourds est impliqué?

La sécurité de la mémoire signifie que vous n'obtenez pas de défauts de segmentation et de violations d'accès. Le support du parallélisme dans .NET 4 est bon. La possibilité d'exécuter du code à la volée via la session interactive F # dans Visual Studio 2010 est extrêmement utile pour l'informatique technique interactive.

Existe-t-il de réelles implémentations de calcul scientifique qui l'utilisent?

Nos produits commerciaux pour le calcul scientifique en F # ont déjà des centaines d'utilisateurs.

Cependant, votre questionnement indique que vous considérez le calcul scientifique comme un calcul à hautes performances (par exemple Cray) et non un calcul technique interactif (par exemple MATLAB, Mathematica). F # est destiné à ce dernier.

62
Jon Harrop

En plus de ce que d'autres ont dit, il y a un point important à propos de F # et c'est le parallélisme . Les performances du code F # ordinaire sont déterminées par CLR, bien que vous puissiez utiliser LAPACK à partir de F # ou que vous puissiez faire des appels natifs en utilisant C++/CLI dans le cadre de votre projet.

Cependant, les programmes fonctionnels bien conçus ont tendance à être beaucoup plus faciles à paralléliser, ce qui signifie que vous pouvez facilement gagner en performances en utilisant des processeurs multicœurs, qui sont certainement à votre disposition si vous faites du calcul scientifique. Voici quelques liens pertinents:

En ce qui concerne l'informatique distribuée, vous pouvez utiliser n'importe quel cadre informatique distribué disponible pour la plate-forme .NET. Il existe un projet MPI.NET, qui fonctionne bien avec F #, mais vous pouvez également utiliser DryadLINQ, qui est un projet MSR.

42
Tomas Petricek

Comme pour toutes les comparaisons de langue/performances, votre kilométrage dépend grandement de la façon dont vous pouvez coder.

F # est un dérivé d'OCaml. J'ai été surpris de découvrir qu'OCaml est beaucoup utilisé dans le monde financier, où les performances de calcul des nombres sont très importantes. J'ai également été surpris de découvrir que OCaml est l'un des langages les plus rapides, avec des performances comparables aux compilateurs C et C++ les plus rapides.

F # est construit sur le CLR . Dans le CLR, le code est exprimé sous la forme d'un bytecode appelé Common Intermediate Language. En tant que tel, il bénéficie des capacités d'optimisation du JIT et a des performances comparables à C # (mais pas nécessairement C++), si le code est bien écrit.

Le code CIL peut être compilé en code natif dans une étape distincte avant l'exécution en utilisant le générateur d'images natives (NGEN). Cela accélère toutes les exécutions ultérieures du logiciel, car la compilation CIL-native n'est plus nécessaire.

Une chose à considérer est que les langages fonctionnels comme F # bénéficient d'un style de programmation plus déclaratif. Dans un sens, vous spécifiez trop la solution dans des langages impératifs tels que C++, ce qui limite la capacité du compilateur à optimiser. Un style de programmation plus déclaratif peut théoriquement donner au compilateur des opportunités supplémentaires d'optimisation algorithmique.

16
Robert Harvey

Cela dépend du type de calcul scientifique que vous faites.

Si vous faites traditional heavy computing, par exemple. l'algèbre linéaire, diverses optimisations, alors vous ne devriez pas mettre votre code dans le framework .Net, du moins pas adapté en F #. Parce que c'est au niveau de l'algorithme, la plupart des algorithmes doivent être codés dans des langages impératifs pour avoir de bonnes performances en termes de temps d'exécution et d'utilisation de la mémoire. D'autres ont mentionné parallèle, je dois dire que c'est probablement inutile lorsque vous faites des trucs de bas niveau comme une implémentation SVD parallèle. Parce que lorsque vous savez comment mettre en parallèle un SVD, vous n'utiliserez simplement pas de langages de haut niveau, Fortran, C ou C modifié (par exemple cilk ) sont vos amis.

Cependant, une grande partie du calcul scientifique d'aujourd'hui n'est pas de ce type, qui est une sorte d'applications de haut niveau, par exemple calcul statistique et exploration de données. Dans ces tâches, outre une algèbre linéaire ou une optimisation, il y a aussi beaucoup de flux de données, d'E/S, de pré-impression, de création de graphiques, etc. Pour ces tâches, F # est vraiment puissant, pour sa brièveté, sa fonctionnalité, sa sécurité, sa facilité de parallèle, etc.

Comme d'autres l'ont mentionné, .Net prend bien en charge Platform Invoke, en fait, plusieurs projets à l'intérieur de MS utilisent ensemble .Net et P/Invoke pour améliorer les performances au niveau du goulot de la bouteille.

9
Yin Zhu

Je ne pense pas que vous trouverez malheureusement beaucoup d'informations fiables. F # est toujours un langage très nouveau, donc même s'il était idéalement adapté aux charges de travail lourdes en termes de performances, il n'y aurait toujours pas beaucoup de personnes ayant une expérience significative à signaler. De plus, les performances sont très difficiles à évaluer avec précision et les microbenchmarks sont difficiles à généraliser. Même dans C++, vous pouvez voir des différences dramatiques entre les compilateurs - vous demandez-vous si F # est compétitif avec n'importe quel compilateur C++, ou avec l'hypothétique "meilleur possible" Exécutable C++?

En ce qui concerne les benchmarks spécifiques contre C++, voici quelques liens éventuellement pertinents: O'Caml vs. F #: décomposition QR ; F # vs C++ non managé pour les nombres parallèles . Notez qu'en tant qu'auteur de matériel lié à F # et en tant que vendeur d'outils F #, l'auteur a un intérêt direct dans le succès de F #, alors prenez ces allégations avec un grain de sel.

Je pense qu'il est prudent de dire qu'il y aura certaines applications où F # est compétitif sur le temps d'exécution et probablement d'autres où il ne l'est pas. F # nécessitera probablement plus de mémoire dans la plupart des cas. Bien sûr, la performance ultime dépendra également fortement de la compétence du programmeur - je pense que F # sera presque certainement un langage plus productif à programmer pour un programmeur moyennement compétent. De plus, je pense qu'en ce moment, le CLR sur Windows fonctionne mieux que Mono sur la plupart des OS pour la plupart des tâches, ce qui peut également affecter vos décisions. Bien sûr, puisque F # est probablement plus facile à paralléliser que C++, cela dépendra également du type de matériel sur lequel vous prévoyez de fonctionner.

En fin de compte, je pense que la seule façon de vraiment répondre à cette question est d'écrire du code F # et C++ représentatif du type de calculs que vous souhaitez effectuer et de les comparer.

7
kvb

Voici deux exemples que je peux partager:

  1. Multiplication matricielle: J'ai un article de blog comparant différentes implémentations de multiplication matricielle .

  2. LBFGS

J'ai un solveur de régression logistique à grande échelle utilisant l'optimisation LBFGS, qui est codé en C++. La mise en œuvre est bien réglée. J'ai modifié du code pour coder en C++/CLI, c'est-à-dire que j'ai compilé le code dans .Net. La version .Net est 3 à 5 fois plus lente que la version naïve compilée sur différents jeux de données. Si vous codez LBFGS en F #, les performances ne peuvent pas être meilleures que C++/CLI ou C # (mais seraient très proches).

J'ai un autre article sur Pourquoi F # est le langage pour l'exploration de données , bien qu'il ne soit pas tout à fait lié au problème de performance qui vous intéresse ici, il est assez lié au calcul scientifique en F #.

4
Yin Zhu

Si je dis "redemande dans 2-3 ans", je pense que cela répondra complètement à votre question :-)

Tout d'abord, ne vous attendez pas à ce que F # soit différent de C # en termes de performances, à moins que vous ne fassiez délibérément des récursions compliquées et je suppose que vous ne l'êtes pas depuis que vous avez posé des questions sur les chiffres.

En virgule flottante, il est forcément meilleur que Java puisque CLR ne vise pas l'uniformité multiplateforme, ce qui signifie que JIT passera à 80 bits chaque fois qu'il le pourra. De l'autre côté vous ne contrôlez pas cela au-delà de regarder le nombre de variables pour vous assurer qu'il y a suffisamment de registres FP.

En ce qui concerne les vecteurs, si vous criez assez fort, il se peut que quelque chose se produise dans 2-3 ans puisque Direct3D entre de toute façon dans .NET en tant qu'API générale et que le code C # fait dans XNA fonctionne sur Xbox qui est aussi proche du métal nu que vous pouvez obtenir avec CLR . Cela signifie toujours que vous auriez besoin de le faire vous-même avec du code intermédiaire.

Alors ne vous attendez pas à ce que CUDA ou même la capacité de simplement relier les bibliothèques NVIDIA et de démarrer. Vous auriez beaucoup plus de chance d'essayer cette approche avec Haskell si, pour une raison quelconque, vous avez vraiment, vraiment besoin d'un langage "fonctionnel" puisque Haskell a été conçu pour être compatible avec les liens par pure nécessité.

Mono.Simd a déjà été mentionné et bien qu'il devrait être rétro-portable pour CLR, il pourrait être assez difficile de le faire.

Il y a pas mal de code dans un publication social.msdn sur l'utilisation de SSE3 dans .NET, avec C++/CLI et C #, venez le mélange de tableaux, l'injection de code SSE3 pour la perf, etc.

Il a été question d'exécuter CECIL sur C # compilé pour extraire des parties en HLSL, compiler en shaders et lier un code de colle pour le planifier (CUDA fait l'équivalent de toute façon) mais je ne pense pas qu'il y ait quoi que ce soit qui puisse en sortir.

Une chose qui pourrait vous valoir plus si vous voulez essayer quelque chose bientôt est PhysX.Net sur codeplex . Ne vous attendez pas à ce qu'il déballe et fasse la magie. Cependant, ih a actuellement un auteur actif et le code est à la fois normal C++ et C++/CLI et yopu peut probablement obtenir de l'aide de l'auteur si vous voulez entrer dans les détails et peut-être utiliser une approche similaire pour CUDA. Pour CUDA à pleine vitesse, vous devrez toujours compiler vos propres noyaux, puis simplement vous connecter à .NET afin que plus cette partie soit simple, plus vous serez heureux.

Il y a une CUDA.NET lib qui est censée être gratuite mais la page donne juste une adresse e-mail alors attendez-vous à quelques chaînes attachées, et pendant que l'auteur écrit une blog il est pas particulièrement bavard sur ce qu'il y a à l'intérieur de la bibliothèque.

Oh et si vous avez le budget, vous pourriez donner à cela Psi Lambda un look (KappaCUDAnet est la partie .NET). Apparemment, ils vont augmenter les prix en novembre (si ce n'est pas un truc de vente :-)

3
ZXX

Premièrement, C est beaucoup plus rapide que C++ .. Donc, si vous avez besoin de tant de vitesse, vous devriez faire la lib etc en c.

En ce qui concerne F #, la plupart des benchmarks utilisent Mono qui est jusqu'à 2 * plus lent que MS CLR en raison partiellement de son utilisation du boehm GC (ils ont un nouveau GC et LVVM mais ceux-ci sont encore immatures et ne prennent pas en charge les génériques, etc.).

Les langages .NEt eux-mêmes sont compilés en un IR (le CIL) qui compile en code natif aussi efficacement que C++. Il y a un ensemble de problèmes dans lequel la plupart des langages GC souffrent et c'est de grandes quantités d'écritures mutables (cela inclut C++ .NET comme mentionné ci-dessus). Et il existe un certain ensemble de problèmes scientifiques qui l'exigent, ceux-ci devraient probablement utiliser une bibliothèque native ou utiliser le modèle Flyweight pour réutiliser les objets d'un pool (ce qui réduit les écritures). La raison en est qu'il existe une barrière en écriture dans le .NET CLR où lors de la mise à jour d'un champ de référence (y compris une boîte), il définira un bit dans une table indiquant que cette table est modifiée. Si votre code se compose de nombreuses écritures de ce type, il en souffrira.

Cela dit, une application .NET comme C # utilisant beaucoup de code statique, de structures et de ref/out sur les structures peut produire des performances de type C, mais il est très difficile de coder comme cela ou de maintenir le code (comme C).

Cependant, F # brille, c'est le parralélisme sur les données immuables qui va de pair avec des problèmes plus basés sur la lecture. Il convient de noter que la plupart des références sont beaucoup plus élevées dans les écritures mutables que dans les applications réelles.

En ce qui concerne la virgule flottante, vous devez utiliser une bibliothèque alternative (c.-à-d. Celle .Net) à celles oCaml car elle est lente. C/C++ permet plus rapidement pour une précision inférieure, ce que oCaml ne fait pas par défaut.

Enfin, je soutiens qu'un langage de haut niveau comme C #, F # et un profilage approprié vous donneront une meilleure performance que c et C++ pour le même temps de développeur. Si vous changez un col de bouteille en un appel c lib pinvoke, vous vous retrouverez également avec des performances de type C pour les zones critiques. Cela dit, si vous avez un budget illimité et que vous vous souciez plus de la vitesse, la maintenance que C est la voie à suivre (pas C++).

1
ben

Pour la dernière fois que je savais, la plupart des calculs scientifiques se faisaient encore à FORTRAN. C'est encore plus rapide qu'autre chose pour les problèmes d'algèbre linéaire - pas Java, pas C, pas C++, pas C #, pas F #. LINPACK est bien optimisé.

Mais la remarque à propos de "votre kilométrage peut varier" est vraie pour toutes les références. Les déclarations générales (sauf la mienne) sont rarement vraies.

1
duffymo