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
la source
Réponses:
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
la source
Vous devez comparer les versions de débogage. Je viens de compiler votre code C et j'ai
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:
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.
la source
i
, etsqrt
c'est donc ce qui est mesuré.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.
la source
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).
la source
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!
la source
Pour voir si la boucle est optimisée, essayez de changer votre code en
ans de la même manière dans le code C, puis affichez la valeur de root en dehors de la boucle.
la source
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.
la source
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
la source
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:
En C #:
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.
la source
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.
la source
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.
la source
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.
la source
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:
À 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:
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?
la source