C # vs C - Grande différence de performances

94

Je trouve d'énormes différences de performances entre du code similaire en C et C #.

Le code C est:

#include <stdio.h>
#include <time.h>
#include <math.h>

main()
{
    int i;
    double root;

    clock_t start = clock();
    for (i = 0 ; i <= 100000000; i++){
        root = sqrt(i);
    }
    printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);   

}

Et le C # (application console) est:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime startTime = DateTime.Now;
            double root;
            for (int i = 0; i <= 100000000; i++)
            {
                root = Math.Sqrt(i);
            }
            TimeSpan runTime = DateTime.Now - startTime;
            Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds/1000));
        }
    }
}

Avec le code ci-dessus, le C # se termine en 0,328125 secondes (version finale) et le C prend 11,14 secondes pour s'exécuter.

Le c est en cours de compilation dans un exécutable Windows à l'aide de mingw.

J'ai toujours supposé que C / C ++ était plus rapide ou au moins comparable à C # .net. Qu'est-ce qui fait exactement fonctionner le C plus de 30 fois plus lentement?

EDIT: Il semble que l'optimiseur C # supprimait la racine car elle n'était pas utilisée. J'ai changé l'affectation racine en root + = et j'ai imprimé le total à la fin. J'ai également compilé le C en utilisant cl.exe avec l'indicateur / O2 défini pour la vitesse maximale.

Les résultats sont maintenant: 3,75 secondes pour le C 2,61 secondes pour le C #

Le C prend encore plus de temps, mais c'est acceptable

John
la source
18
Je vous suggère d'utiliser un StopWatch au lieu d'un simple DateTime.
Alex Fort
2
Quels drapeaux de compilateur? Les deux sont-ils compilés avec les optimisations activées?
jalf
2
Qu'en est-il lorsque vous utilisez -ffast-math avec le compilateur C ++?
Dan McClain
10
Quelle question fascinante!
Robert S.
4
Peut-être que la fonction C sqrt n'est pas aussi bonne que celle-ci en C #. Ensuite, ce ne serait pas un problème avec C, mais avec une bibliothèque attachée. Essayez quelques calculs sans fonctions mathématiques.
klew

Réponses:

61

Puisque vous n'utilisez jamais «root», le compilateur a peut-être supprimé l'appel pour optimiser votre méthode.

Vous pouvez essayer d'accumuler les valeurs de racine carrée dans un accumulateur, de l'imprimer à la fin de la méthode et de voir ce qui se passe.

Edit: voir la réponse de Jalf ci-dessous

Brann
la source
1
Un peu d'expérimentation suggère que ce n'est pas le cas. Le code de la boucle est généré, même si le runtime est peut-être assez intelligent pour l'ignorer. Même en s'accumulant, C # bat toujours le pantalon de C.
Dana
3
Il semble que le problème se situe à l'autre bout. C # se comporte raisonnablement dans tous les cas. Son code C est apparemment compilé sans optimisations
jalf
2
Beaucoup d'entre vous manquent le point ici. J'ai lu de nombreux cas similaires où c # surpasse c / c ++ et la réfutation est toujours d'utiliser une optimisation de niveau expert. 99% des programmeurs n'ont pas les connaissances nécessaires pour utiliser de telles techniques d'optimisation juste pour que leur code s'exécute légèrement plus rapidement que le code c #. Les cas d'utilisation pour c / c ++ se rétrécissent.
167

Vous devez comparer les versions de débogage. Je viens de compiler votre code C et j'ai

Time elapsed: 0.000000

Si vous n'activez pas les optimisations, toute analyse comparative que vous effectuez est totalement inutile. (Et si vous activez les optimisations, la boucle est optimisée. Donc, votre code d'analyse comparative est également défectueux. Vous devez le forcer à exécuter la boucle, généralement en additionnant le résultat ou similaire, et en l'imprimant à la fin)

Il semble que ce que vous mesurez est essentiellement "quel compilateur insère le plus de frais généraux de débogage". Et il s'avère que la réponse est C. Mais cela ne nous dit pas quel programme est le plus rapide. Parce que lorsque vous voulez de la vitesse, vous activez les optimisations.

En passant, vous vous épargnerez beaucoup de maux de tête à long terme si vous abandonnez toute notion de langues «plus rapides» les unes que les autres. C # n'a pas plus de vitesse que l'anglais.

Il y a certaines choses dans le langage C qui seraient efficaces même dans un compilateur naïf non optimisant, et il y en a d'autres qui dépendent fortement d'un compilateur pour tout optimiser. Et bien sûr, il en va de même pour C # ou tout autre langage.

La vitesse d'exécution est déterminée par:

  • la plate-forme sur laquelle vous utilisez (système d'exploitation, matériel, autres logiciels exécutés sur le système)
  • le compilateur
  • votre code source

Un bon compilateur C # produira un code efficace. Un mauvais compilateur C générera du code lent. Qu'en est-il d'un compilateur C qui a généré du code C #, que vous pouvez ensuite exécuter via un compilateur C #? À quelle vitesse cela fonctionnerait-il? Les langues n'ont pas de vitesse. Votre code le fait.

jalf
la source
Beaucoup plus de lecture intéressante ici: blogs.msdn.com/ricom/archive/2005/05/10/416151.aspx
Daniel Earwicker
18
Bonne réponse, mais je ne suis pas d'accord sur la vitesse de la langue, du moins par analogie: il a été constaté que le welsch est une langue plus lente que la plupart à cause de la fréquence élevée des voyelles longues. De plus, les gens se souviennent mieux des mots (et des listes de mots) s'ils sont plus rapides à dire. web.missouri.edu/~cowann/docs/articles/before%201993/... en.wikipedia.org/wiki/Vowel_length en.wikipedia.org/wiki/Welsh_language
exceptionerror
1
Cela ne dépend-il pas de ce que vous dites à Welsch? Je trouve peu probable que tout soit plus lent.
jalf
5
++ Hé les gars, ne vous laissez pas distraire ici. Si le même programme s'exécute plus rapidement dans une langue que dans une autre, c'est parce qu'un code d'assemblage différent est généré. Dans cet exemple particulier, 99% ou plus du temps passera au flottant i, et sqrtc'est donc ce qui est mesuré.
Mike Dunlavey
116

Je vais être bref, il est déjà marqué comme répondu. C # a le grand avantage d'avoir un modèle en virgule flottante bien défini. Cela correspond au mode de fonctionnement natif du jeu d'instructions FPU et SSE sur les processeurs x86 et x64. Aucune coïncidence là-bas. Le JITter compile Math.Sqrt () en quelques instructions en ligne.

Le C / C ++ natif est doté d'années de compatibilité ascendante. Les options de compilation / fp: precise, / fp: fast et / fp: strict sont les plus visibles. En conséquence, il doit appeler une fonction CRT qui implémente sqrt () et vérifie les options de virgule flottante sélectionnées pour ajuster le résultat. C'est lent.

Hans Passant
la source
66
C'est une conviction étrange parmi les programmeurs C ++, ils semblent penser que le code machine généré par C # est en quelque sorte différent du code machine généré par un compilateur natif. Il n'y en a qu'un seul. Quel que soit le commutateur de compilateur gcc que vous utilisez ou l'assembly en ligne que vous écrivez, il n'y a toujours qu'une seule instruction FSQRT. Ce n'est pas toujours plus rapide car une langue native l'a généré, le processeur s'en fiche.
Hans Passant
16
C'est ce que résout le pré-jitting avec ngen.exe. Nous parlons de C #, pas de Java.
Hans Passant
20
@ user877329 - vraiment? Sensationnel.
Andras Zoltan
7
Non, la gigue x64 utilise SSE. Math.Sqrt () est traduit en instruction de code machine sqrtsd.
Hans Passant
6
Bien que ce ne soit techniquement pas une différence entre les langages, le .net JITter effectue des optimisations plutôt limitées par rapport à un compilateur C / C ++ typique. L'une des plus grandes limitations est le manque de support SIMD, ce qui rend le code souvent 4x plus lent. Ne pas exposer de nombreux éléments intrinsèques peut également être un gros malus, mais cela dépend beaucoup de ce que vous faites.
CodesInChaos
57

Je suis développeur C ++ et C #. J'ai développé des applications C # depuis la première version bêta du framework .NET et j'ai plus de 20 ans d'expérience dans le développement d'applications C ++. Premièrement, le code C # ne sera JAMAIS plus rapide qu'une application C ++, mais je ne passerai pas par une longue discussion sur le code managé, son fonctionnement, la couche interopératoire, les internes de gestion de la mémoire, le système de type dynamique et le ramasse-miettes. Néanmoins, permettez-moi de continuer en disant que les repères énumérés ici produisent tous des résultats INCORRECTS.

Laissez-moi vous expliquer: la première chose que nous devons considérer est le compilateur JIT pour C # (.NET Framework 4). Maintenant, le JIT produit du code natif pour le processeur à l'aide de divers algorithmes d'optimisation (qui ont tendance à être plus agressifs que l'optimiseur C ++ par défaut fourni avec Visual Studio) et le jeu d'instructions utilisé par le compilateur .NET JIT reflète de plus près le processeur réel sur la machine afin que certaines substitutions dans le code machine puissent être effectuées pour réduire les cycles d'horloge et améliorer le taux de réussite dans le cache du pipeline CPU et produire d'autres optimisations d'hyper-threading telles que la réorganisation des instructions et les améliorations relatives à la prédiction de branchement.

Cela signifie qu'à moins que vous ne compiliez votre application C ++ en utilisant les paramètres corrects pour la version RELEASE (pas la version DEBUG), votre application C ++ peut s'exécuter plus lentement que l'application C # ou .NET correspondante. Lorsque vous spécifiez les propriétés du projet sur votre application C ++, assurez-vous d'activer "l'optimisation complète" et "favoriser le code rapide". Si vous avez une machine 64 bits, vous DEVEZ spécifier de générer x64 comme plate-forme cible, sinon votre code sera exécuté via une sous-couche de conversion (WOW64) ce qui réduira considérablement les performances.

Une fois que vous avez effectué les optimisations correctes dans le compilateur, j'obtiens 0,72 seconde pour l'application C ++ et 1,16 seconde pour l'application C # (les deux dans la version de version). Étant donné que l'application C # est très basique et alloue la mémoire utilisée dans la boucle sur la pile et non sur le tas, elle fonctionne en fait beaucoup mieux qu'une application réelle impliquée dans des objets, des calculs lourds et avec des ensembles de données plus volumineux. Les chiffres fournis sont donc des chiffres optimistes biaisés vers C # et le framework .NET. Même avec ce biais, l'application C ++ se termine en un peu plus de la moitié du temps que l'application C # équivalente. Gardez à l'esprit que le compilateur Microsoft C ++ que j'ai utilisé n'avait pas le bon pipeline et les optimisations d'hyperthreading (en utilisant WinDBG pour afficher les instructions d'assemblage).

Maintenant, si nous utilisons le compilateur Intel (qui est d'ailleurs un secret de l'industrie pour générer des applications hautes performances sur des processeurs AMD / Intel), le même code s'exécute en 0,54 seconde pour l'exécutable C ++ contre 0,72 seconde en utilisant Microsoft Visual Studio 2010 Au final, les résultats finaux sont de 0,54 seconde pour C ++ et 1,16 seconde pour C #. Ainsi, le code produit par le compilateur .NET JIT prend 214% plus de temps que l'exécutable C ++. La plupart du temps passé dans les .54 secondes était pour obtenir l'heure du système et non dans la boucle elle-même!

Ce qui manque également dans les statistiques, ce sont les heures de démarrage et de nettoyage qui ne sont pas incluses dans les horaires. Les applications C # ont tendance à passer beaucoup plus de temps au démarrage et à l'arrêt que les applications C ++. La raison derrière cela est compliquée et a à voir avec les routines de validation du code d'exécution .NET et le sous-système de gestion de la mémoire qui effectue beaucoup de travail au début (et par conséquent, à la fin) du programme pour optimiser les allocations de mémoire et les déchets collectionneur.

Lors de la mesure des performances de C ++ et .NET IL, il est important de regarder le code d'assembly pour s'assurer que TOUS les calculs sont là. Ce que j'ai trouvé, c'est que sans mettre de code supplémentaire en C #, la plupart du code dans les exemples ci-dessus ont en fait été supprimés du binaire. C'était également le cas avec C ++ lorsque vous utilisiez un optimiseur plus agressif tel que celui fourni avec le compilateur Intel C ++. Les résultats que j'ai fournis ci-dessus sont 100% corrects et validés au niveau de l'assemblage.

Le principal problème avec beaucoup de forums sur Internet est que beaucoup de débutants écoutent la propagande marketing de Microsoft sans comprendre la technologie et font de fausses déclarations selon lesquelles C # est plus rapide que C ++. La prétention est qu'en théorie, C # est plus rapide que C ++ car le compilateur JIT peut optimiser le code pour le processeur. Le problème avec cette théorie est qu'il y a beaucoup de plomberie qui existe dans le framework .NET qui ralentit les performances; plomberie qui n'existe pas dans l'application C ++. De plus, un développeur expérimenté connaîtra le bon compilateur à utiliser pour la plate-forme donnée et utilisera les indicateurs appropriés lors de la compilation de l'application. Sur les plates-formes Linux ou open source, ce n'est pas un problème car vous pouvez distribuer votre source et créer des scripts d'installation qui compilent le code en utilisant l'optimisation appropriée. Sur les fenêtres ou la plate-forme source fermée, vous devrez distribuer plusieurs exécutables, chacun avec des optimisations spécifiques. Les binaires Windows qui seront déployés sont basés sur le processeur détecté par le programme d'installation msi (à l'aide d'actions personnalisées).

Richard
la source
22
1. Microsoft n'a jamais fait ces affirmations selon lesquelles C # était plus rapide, ses affirmations sont environ 90% de la vitesse, plus rapide à développer (et donc plus de temps pour régler) et plus exempt de bogues en raison de la sécurité de la mémoire et des types. Tout cela est vrai (j'ai 20 ans en C ++ et 10 en C #) 2. Les performances de démarrage n'ont pas de sens dans la plupart des cas. 3. Il existe également des compilateurs C # plus rapides comme LLVM (donc sortir Intel n'est pas Apples to Apples)
ben
13
Les performances de démarrage ne sont pas dénuées de sens. Il est très important dans la plupart des applications Web d'entreprise, c'est pourquoi Microsoft a introduit les pages Web à précharger (démarrage automatique) dans .NET 4.0. Lorsque le pool d'applications est recyclé de temps en temps, le premier chargement de chaque page ajoutera un délai important pour les pages complexes et entraînera des délais d'expiration sur le navigateur.
Richard
8
Microsoft a déclaré que les performances de .NET étaient plus rapides dans les supports marketing antérieurs. Ils ont également fait diverses affirmations sur le ramasse-miettes qui avaient peu ou pas d'impact sur les performances. Certaines de ces affirmations en ont fait divers livres (sur ASP.NET et .NET) dans leurs éditions antérieures. Bien que Microsoft ne dise pas spécifiquement que votre application C # sera plus rapide que votre application C ++, ils peuvent balayer les commentaires génériques et les slogans marketing tels que "Just-In-Time signifie Run-It-Fast" ( msdn.microsoft.com/ fr-fr / bibliothèque / ms973894.aspx ).
Richard
71
-1, cette diatribe est pleine d'énoncés incorrects et trompeurs tels que le whopper évident "Le code C # ne sera JAMAIS plus rapide qu'une application C ++"
BCoates
32
-1. Vous devriez lire la bataille de performances C # contre C de Rico Mariani contre Raymond Chen: blogs.msdn.com/b/ricom/archive/2005/05/16/418051.aspx . En bref: il a fallu beaucoup d'optimisation à l'un des gars les plus intelligents de Microsoft pour rendre la version C plus rapide qu'une simple version C #.
Rolf Bjarne Kvinge le
10

ma première hypothèse est une optimisation du compilateur car vous n'utilisez jamais root. Vous venez de l'attribuer, puis de l'écraser encore et encore.

Edit: putain, battu de 9 secondes!

Neil N
la source
2
Je dis que vous avez raison. La variable réelle est écrasée et n'est jamais utilisée au-delà de cela. Le csc renoncerait probablement à toute la boucle alors que le compilateur c ++ la laissait probablement dedans. Un test plus précis serait d'accumuler les résultats et ensuite d'imprimer ce résultat à la fin. De plus, il ne faut pas coder en dur la valeur de départ, mais plutôt la laisser définie par l'utilisateur. Cela ne donnerait aucune place au compilateur c # pour laisser des éléments de côté.
7

Pour voir si la boucle est optimisée, essayez de changer votre code en

root += Math.Sqrt(i);

ans de la même manière dans le code C, puis affichez la valeur de root en dehors de la boucle.


la source
6

Peut-être que le compilateur c # remarque que vous n'utilisez root nulle part, donc il saute juste toute la boucle for. :)

Ce n'est peut-être pas le cas, mais je soupçonne que quelle qu'en soit la cause, cela dépend de l'implémentation du compilateur. Essayez de compiler votre programme C avec le compilateur Microsoft (cl.exe, disponible dans le cadre du sdk win32) avec des optimisations et le mode Release. Je parie que vous verrez une amélioration des performances par rapport à l'autre compilateur.

EDIT: Je ne pense pas que le compilateur puisse simplement optimiser la boucle for, car il devrait savoir que Math.Sqrt () n'a pas d'effets secondaires.

i_am_jorf
la source
2
Peut-être qu'il le sait.
2
@Neil, @jeff: D'accord, il pourrait le savoir assez facilement. Selon l'implémentation, l'analyse statique sur Math.Sqrt () peut ne pas être si difficile, même si je ne suis pas sûr des optimisations spécifiquement effectuées.
John Feminella
5

Quelle que soit l'heure diff. peut-être que le "temps écoulé" est invalide. Ce ne serait valide que si vous pouvez garantir que les deux programmes s'exécutent exactement dans les mêmes conditions.

Vous devriez peut-être essayer de gagner. équivalent à $ / usr / bin / time my_cprog; / usr / bin / time my_csprog

À M
la source
1
Pourquoi ce vote est-il défavorable? Quelqu'un suppose-t-il que les interruptions et les changements de contexte n'affectent pas les performances? Quelqu'un peut-il faire des hypothèses sur les échecs de TLB, l'échange de pages, etc.?
Tom
5

J'ai rassemblé (en fonction de votre code) deux autres tests comparables en C et C #. Ces deux écrivent un tableau plus petit en utilisant l'opérateur de module pour l'indexation (cela ajoute un peu de surcharge, mais bon, nous essayons de comparer les performances [à un niveau brut]).

Code C:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

void main()
{
    int count = (int)1e8;
    int subcount = 1000;
    double* roots = (double*)malloc(sizeof(double) * subcount);
    clock_t start = clock();
    for (int i = 0 ; i < count; i++)
    {
        roots[i % subcount] = sqrt((double)i);
    }
    clock_t end = clock();
    double length = ((double)end - start) / CLOCKS_PER_SEC;
    printf("Time elapsed: %f\n", length);
}

En C #:

using System;

namespace CsPerfTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = (int)1e8;
            int subcount = 1000;
            double[] roots = new double[subcount];
            DateTime startTime = DateTime.Now;
            for (int i = 0; i < count; i++)
            {
                roots[i % subcount] = Math.Sqrt(i);
            }
            TimeSpan runTime = DateTime.Now - startTime;
            Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds / 1000));
        }
    }
}

Ces tests écrivent des données dans un tableau (le runtime .NET ne devrait donc pas être autorisé à supprimer l'opération sqrt) bien que le tableau soit nettement plus petit (ne voulait pas utiliser de mémoire excessive). Je les ai compilés dans la configuration de la version et les ai exécutés à partir d'une fenêtre de console (au lieu de démarrer via VS).

Sur mon ordinateur, le programme C # varie entre 6,2 et 6,9 secondes, tandis que la version C varie entre 6,9 ​​et 7,1.

Cecil a un nom
la source
5

Si vous ne faites qu'une seule étape du code au niveau de l'assembly, y compris la procédure de racine carrée, vous obtiendrez probablement la réponse à votre question.

Pas besoin de devinettes éclairées.

Mike Dunlavey
la source
J'aimerais savoir comment faire ça
Josh Stodola
Dépend de votre IDE ou de votre débogueur. Pause au début du pgm. Affichez la fenêtre de désassemblage et démarrez en une seule étape Si vous utilisez GDB, il existe des commandes pour avancer une instruction à la fois.
Mike Dunlavey
Maintenant que c'est un bon conseil, cela aide à mieux comprendre ce qui se passe réellement là-bas. Cela montre-t-il également les optimisations JIT comme les appels en ligne et les appels de fin?
gjvdkamp
FYI: pour moi, cela montrait VC ++ en utilisant fadd et fsqrt tandis que C # utilisait cvtsi2sd et sqrtsd qui, si je comprends bien, sont des instructions SSE2 et donc considérablement plus rapides lorsqu'elles sont prises en charge.
danio
2

L'autre facteur qui peut être un problème ici est que le compilateur C se compile en code natif générique pour la famille de processeurs que vous ciblez, alors que le MSIL généré lorsque vous compilez le code C # est alors compilé JIT pour cibler le processeur exact que vous avez complet avec tout optimisations possibles. Ainsi, le code natif généré à partir du C # peut être considérablement plus rapide que le C.

David M
la source
En théorie, oui. En pratique, cela ne fait pratiquement jamais de différence mesurable. Un pour cent ou deux, peut-être, si vous avez de la chance.
jalf
ou - si vous avez un certain type de code qui utilise des extensions qui ne sont pas dans la liste autorisée pour le processeur «générique». Des choses comme les saveurs SSE. Essayez avec la cible de processeur définie plus haut, pour voir quelles différences vous obtenez.
gbjbaanb
1

Il me semble que cela n'a rien à voir avec les langages eux-mêmes, mais plutôt avec les différentes implémentations de la fonction racine carrée.

Jack Ryan
la source
Je doute fortement que des implémentations de sqrt différentes causent autant de disparité.
Alex Fort
D'autant que, même en C #, la plupart des fonctions mathématiques sont toujours considérées comme critiques pour les performances et sont implémentées en tant que telles.
Matthew Olenik
fsqrt est une instruction de processeur IA-32, donc l'implémentation du langage n'est plus pertinente de nos jours.
Pas sûr
Entrez dans la fonction sqrt de MSVC avec un débogueur. Cela fait beaucoup plus que simplement exécuter l'instruction fsqrt.
bk1e
1

En fait les gars, la boucle n'est PAS optimisée. J'ai compilé le code de John et examiné le fichier .exe résultant. Les tripes de la boucle sont les suivantes:

 IL_0005:  stloc.0
 IL_0006:  ldc.i4.0
 IL_0007:  stloc.1
 IL_0008:  br.s       IL_0016
 IL_000a:  ldloc.1
 IL_000b:  conv.r8
 IL_000c:  call       float64 [mscorlib]System.Math::Sqrt(float64)
 IL_0011:  pop
 IL_0012:  ldloc.1
 IL_0013:  ldc.i4.1
 IL_0014:  add
 IL_0015:  stloc.1
 IL_0016:  ldloc.1
 IL_0017:  ldc.i4     0x5f5e100
 IL_001c:  ble.s      IL_000a

À moins que le runtime ne soit suffisamment intelligent pour réaliser que la boucle ne fait rien et l'ignore?

Edit: Changer le C # pour être:

 static void Main(string[] args)
 {
      DateTime startTime = DateTime.Now;
      double root = 0.0;
      for (int i = 0; i <= 100000000; i++)
      {
           root += Math.Sqrt(i);
      }
      System.Console.WriteLine(root);
      TimeSpan runTime = DateTime.Now - startTime;
      Console.WriteLine("Time elapsed: " +
          Convert.ToString(runTime.TotalMilliseconds / 1000));
 }

Résultats dans le temps écoulé (sur ma machine) allant de 0,047 à 2,17. Mais est-ce juste la surcharge de l'ajout de 100 millions d'opérateurs supplémentaires?

Dana
la source
3
L'examen de l'IL ne vous en dit pas beaucoup sur les optimisations, car bien que le compilateur C # fasse certaines choses comme le pliage constant et la suppression du code mort, l'IL prend alors le relais et fait le reste au moment du chargement.
Daniel Earwicker
C'est ce que je pensais être le cas. Même en le forçant à fonctionner, il est toujours 9 secondes plus rapide que la version C. (Je ne m'attendais pas du tout à cela)
Dana