Ecrire en C pour Performance? [fermé]


32

Je sais que j'ai souvent entendu dire que le langage C offre généralement un avantage en termes de performances par rapport au C ++. Je n'y ai pas vraiment pensé avant de me rendre compte que MSVC ne semble même pas prendre en charge le dernier standard de C, mais le plus récent, le C99 (à ma connaissance).

J'avais l'intention d'écrire une bibliothèque avec du code à restituer sous OpenGL afin de pouvoir la réutiliser. J'avais l'intention d'écrire la bibliothèque en C, car toute augmentation des performances est la bienvenue pour les graphiques.

Mais cela en vaut-il vraiment la peine? Le code utilisant la bibliothèque serait probablement écrit en C ++ et je préfère coder en C ++ en général.

Cependant, si cela produisait même une petite différence de performance, j'irais probablement avec C.

Il convient également de noter que cette bibliothèque serait quelque chose que je ferais pour fonctionner sous Windows / OS X / Linux et que je compilerais probablement tout en natif (MSVC pour Windows, Clang ou GCC pour OS X et GCC pour Linux. .ou éventuellement les compilateurs d’Intel pour tout).

J'ai regardé autour de moi et j'ai trouvé des points de repère et autres, mais tout ce que j'ai vu concerne GCC plutôt que MSVC et Clang. De plus, les repères ne mentionnent pas les standards des langues utilisées. Quelqu'un a un avis là dessus?

MODIFIER:Je voulais juste partager mon point de vue sur cette question après quelques années d'expérience. J'ai fini par écrire le projet pour lequel je posais cette question en C ++. J'ai commencé un autre projet à peu près au même moment en C, car nous cherchions à obtenir une petite performance. Nous avions besoin d'un lien pour le projet en C. Il y a quelques mois, j'ai atteint le point où j'avais vraiment besoin de cartes et de technologies avancées. manipulation de chaîne. Je connaissais les possibilités de cette fonctionnalité dans la bibliothèque standard C ++ et suis finalement parvenu à la conclusion que ces structures de la bibliothèque standard seraient probablement plus performantes et plus stables que les cartes et les chaînes que je pouvais implémenter en C dans un délai raisonnable. L'exigence de pouvoir être lié en C était facilement satisfaite en écrivant une interface C dans le code C ++, ce qui était fait rapidement avec des types opaques. La réécriture de la bibliothèque en C ++ semblait aller beaucoup plus vite que lors de l’écriture en C et était moins sujette aux bogues, en particulier aux fuites de mémoire. J'ai également pu utiliser la bibliothèque de threading standard de la bibliothèque, ce qui était beaucoup plus simple que d'utiliser des implémentations spécifiques à la plate-forme. En fin de compte, je pense que l’écriture de la bibliothèque en C ++ a apporté d’énormes avantages, avec éventuellement un faible coût en termes de performances. Je n'ai pas encore évalué la version C ++, mais je pense qu'il est même possible que j'aie gagné en performance en utilisant des structures de données de bibliothèque standard par rapport à celles que j'ai écrites. Je crois que l’écriture de la bibliothèque en C ++ a eu d’énormes avantages, avec éventuellement un faible coût en termes de performances. Je n'ai pas encore évalué la version C ++, mais je pense qu'il est même possible que j'aie gagné en performance en utilisant des structures de données de bibliothèque standard par rapport à celles que j'ai écrites. Je crois que l’écriture de la bibliothèque en C ++ a eu d’énormes avantages, avec éventuellement un faible coût de performances. Je n'ai pas encore évalué la version C ++, mais je pense qu'il est même possible que j'aie gagné en performance en utilisant des structures de données de bibliothèque standard par rapport à celles que j'ai écrites.


9
Le plus récent support MSVC est en réalité C89.
Détly

4
@detly Dans Visual Studio 2013, la grande majorité des fonctionnalités de C99 sont prises en charge . Ce n'est pas un support complet, mais je parierais qu'en pratique, il est correct de l'utiliser pour écrire C99.
congusbongus

4
@ danielu13 - Où avez-vous entendu dire que C bénéficie d'un avantage en termes de performances par rapport à C ++?
Ramhound

1
@ Sebastian-LaurenţiuPlesciuc Je ne pense pas que ces liens soient réellement utiles. Le premier pourrait être bien contré par presque la même question programmers.stackexchange.com/q/113295/76444 mais en faveur de c ++ au lieu de c comme dans votre lien. Pour votre deuxième lien, il s’agit simplement d’un coup de gueule de linus torvalds. J'espère que tout le monde sait maintenant qu'il aime vraiment détester le c ++ et ne le toucherait pas avec un bâton, mais ses discours sur le c ++ ne sont guère objectifs, ils sont pleins d'opinions personnelles et de préjugés et ne reflètent pas vraiment la réalité du langage. . Au moins c'est mon avis .
user1942027

1
@RaphaelMiedl J'ai aussi mentionné que cela avait été écrit en 2007, il y a assez longtemps. Les compilateurs C ++ et le langage C ++ ont évolué depuis. Quoi qu’il en soit, il appartient au programmeur de choisir la langue à utiliser.
Sebastian-Laurenţiu Plesciuc

Réponses:


89

J'imagine que les gens prétendent souvent que le C est plus rapide que le C ++, car il est plus facile de raisonner les performances en C. C ++ n'est pas nécessairement plus lent ni plus rapide, mais certains codes C ++ peuvent masquer les inconvénients des performances cachés. Par exemple, il peut exister des copies et des conversions implicites qui ne sont pas immédiatement visibles lorsque vous consultez du code C ++.

Prenons la déclaration suivante:

foo->doSomething(a + 5, *c);

Supposons en outre que doSomethingla signature soit la suivante

void doSomething(int a, long b);

Essayons maintenant d'analyser l'impact possible de cette affirmation sur les performances.

En C, les implications sont assez claires. foone peut être qu'un pointeur sur une structure et doSomethingdoit être un pointeur sur une fonction. *cdéréférence une a + 5addition longue et entière. La seule incertitude vient du type de a: Si ce n'est pas un int, il y aura une conversion. mais à part cela, il est facile de quantifier l'impact de cette déclaration sur la performance.

Passons maintenant au C ++. La même déclaration peut maintenant avoir des caractéristiques de performance très différentes:

  1. doSomethingpourrait être une fonction non membre virtuel (pas cher), la fonction de membre virtuel (un peu plus cher), std::functionlambda ... etc. Ce qui est pire, foopourrait être un type de classe surcharge operator->avec une opération de complexité unkown. Donc, afin de quantifier le coût des appels doSomething, il est maintenant nécessaire de connaître la nature exacte de fooet doSomething.
  2. apourrait être un entier, ou une référence à un entier (additionnel), ou un type de classe qui implémente operator+(int). L'opérateur pourrait même renvoyer un autre type de classe auquel il est implicitement convertible int. Encore une fois, le coût de la performance ne ressort pas de la seule déclaration.
  3. cpourrait être une implémentation de type classe operator*(). Cela pourrait aussi être une référence à un long*etc.

Vous obtenez l'image. En raison des fonctionnalités de langage C ++, il est beaucoup plus difficile de quantifier les coûts de performance d'un seul énoncé qu'en C. Maintenant, en outre, des abstractions telles que std::vector, std::stringsont couramment utilisées en C ++, qui ont leurs propres performances et cachent des allocations de mémoire dynamiques ( voir aussi la réponse de @ Ian).

En résumé, les résultats sont les suivants: en général, il n’ya aucune différence entre les performances possibles en C ou C ++. Mais pour un code vraiment critique en termes de performances, les utilisateurs préfèrent souvent utiliser C parce qu'il existe beaucoup moins de pénalités de performances cachées .


1
Superbe réponse. C’est ce à quoi je faisais allusion dans ma réponse, mais vous l’avez beaucoup mieux expliquée.
Ian Goldby

4
Cela devrait vraiment être la réponse acceptée. Cela explique pourquoi des instructions telles que "C est plus rapide que C ++" existent. C peut être plus rapide ou plus lent que C ++, mais il est généralement beaucoup plus facile de comprendre pourquoi un morceau de code C spécifique est rapide / lent, ce qui facilite généralement l'optimisation.
Leo

Ne rien prendre de cette excellente réponse (pour laquelle un +1 de moi), mais le compilateur (s) peut être les pommes et les oranges dans cette comparaison. Ils peuvent générer un code identique pour C vs C ++, ou non. Bien entendu, on peut en dire autant de deux compilateurs ou de leurs options, même lors de la compilation du même programme physiquement avec les mêmes hypothèses de langage source.
JRobert

4
J'ajouterais que la plupart des runtimes C ++ sont énormes comparés à un runtime équivalent, ce qui aurait de l'importance si vous êtes limité en mémoire.
James Anderson

@JamesAnderson Si vous êtes vraiment que la mémoire limitée, vous n'avez probablement pas besoin d' un temps d' exécution du tout :)
Navin

30

Le code écrit en C ++ peut être plus rapide qu'en C, pour certains types de tâches.

Si vous préférez C ++, utilisez C ++. Tous les problèmes de performances seront insignifiants par rapport aux décisions algorithmiques de votre logiciel.


6
Cela peut être plus rapide, mais pas toujours pour la même raison.
Rob

Pouvez-vous donner quelques exemples de code optimisé écrit en C ++ plus rapide que le C optimisé?

1
@TomDworzanski: par exemple, en utilisant des modèles, les décisions concernant les chemins de code peuvent être déterminées au moment de la compilation et finalisées au codage dur dans le binaire final, plutôt que conditionnelles et branchées comme il le faudrait si elles étaient écrites en c, ainsi que la capacité pour éviter les appels de fonction via inlining.
Whatsisname

23

L'un des principes de conception du C ++ est que vous ne payez pas pour des fonctionnalités que vous n'utilisez pas. Ainsi, si vous écrivez du code en C ++ et évitez les fonctionnalités qui n'existent pas en C, le code compilé résultant devrait être équivalent en performances (vous devrez toutefois mesurer cela).

L'utilisation de classes, par exemple, présente un coût négligeable par rapport aux structures et à un ensemble de fonctions associées. Les fonctions virtuelles coûteront un peu plus cher, et il vous faudrait mesurer les performances pour voir si elles sont importantes pour votre application. Il en va de même pour toute autre fonctionnalité du langage C ++.


3
La charge de travail liée à la répartition de la fonction virtuelle est presque négligeable, à moins que vous n'ayez trop abusé de la décomposition et de la virtualisation. Les vtables seront petits comparés au reste de votre code et de vos données, et la branche indexée via vtable ajoute quelques horloges à chaque appel de routine. Étant donné que les invocations de routine, appelées à revenir, vont de quelques centaines à quelques millions d'horloges, la branche vtable sera enfouie dans le bruit.
John R. Strohm

6
Les structures sont des classes en C ++.
Droitier

2
@rightfold: Bien sûr, mais vous pouvez toujours écrire du code C ++ qui fait passer des pointeurs vers des structures sans utiliser la thisfonctionnalité de langage de pointeur. C'est tout ce que je disais.
Greg Hewgill

4
@John Le coût réel n'est pas l'indirection (bien que je sois à peu près sûr que certains processeurs de pré-extraction seront un peu foutu), mais le fait que vous ne pouvez pas utiliser de fonctions virtuelles en ligne (au moins en C ++), ce qui interdit beaucoup de choses autrement possibles optimisations. Et oui, cela peut avoir une influence gigantesque sur les performances.
Voo

2
@Voo Pour être juste, on peut en dire autant du code C équivalent (code émulant manuellement le polymorphisme à l'exécution). La plus grande différence est que je pense qu'il serait plus facile pour un compilateur de déterminer si cette fonction pourrait être intégrée au C ++.
Thomas Eding

14

L'une des raisons pour lesquelles les langages de niveau supérieur sont parfois plus lents est qu'ils peuvent cacher beaucoup plus de ressources cachées dans la gestion de la mémoire que les langages de niveau inférieur.

Toute langue (ou bibliothèque, API, etc.) qui fait abstraction des détails de bas niveau peut potentiellement masquer des opérations coûteuses. Par exemple, dans certaines langues, le simple fait de rogner les espaces à la fin d'une chaîne entraîne l'allocation de mémoire et une copie de la chaîne. L'allocation de mémoire et la copie en particulier peuvent coûter cher si elles se produisent de manière répétée dans une boucle serrée.

Si vous écriviez ce genre de code en C, cela serait flagrant. En C ++ peut-être moins, car les allocations et la copie pourraient être abstraites dans une classe quelque part. Ils peuvent même être cachés derrière un opérateur ou un constructeur de copie surchargé et d'apparence innocente.

Utilisez donc C ++ si vous le souhaitez. Mais ne soyez pas séduit par l'apparente commodité d'abstractions quand vous ne savez pas ce qui les cache.

Bien sûr, utilisez un profileur pour déterminer ce qui ralentit réellement votre code.


5

Pour ce que cela vaut, j'ai tendance à écrire mes bibliothèques en C ++ 11 pour le jeu de fonctionnalités amélioré. J'aime pouvoir tirer parti de choses telles que les pointeurs partagés, les exceptions, la programmation générique et d'autres fonctionnalités uniquement en C ++. J'aime C ++ 11 car j'ai constaté qu'une bonne partie de celle-ci est prise en charge sur toutes les plateformes qui comptent pour moi. Visual Studio 2013 dispose de nombreuses fonctionnalités de langage de base et d'implémentations de bibliothèques prêtes à l'emploi et travaille apparemment à l'ajout du reste. Comme vous le savez bien, Clang et GCC prennent également en charge l'ensemble des fonctionnalités.

Cela dit, j'ai récemment entendu parler d'une stratégie vraiment géniale en matière de développement de bibliothèques qui, selon moi, est directement pertinente pour votre requête. L'article s'intitule "Un style de gestion des erreurs CA qui joue bien avec les exceptions C ++" Stefanu Du Toit appelle cette stratégie un motif "sablier". Le premier paragraphe de l'article:

J'ai écrit beaucoup de code de bibliothèque en utilisant ce que j'appelle un motif "sablier": j'implémente une bibliothèque (généralement, en utilisant C ++), je l'enveloppe dans une API C qui devient le seul point d'entrée dans la bibliothèque, encapsulez ensuite cette API C en C ++ ou dans un autre langage pour fournir une abstraction riche et une syntaxe commode. En ce qui concerne le code multiplate-forme natif, les API C offrent une stabilité ABI inégalée et une portabilité vers d'autres langues via des FFI. Je limite même l'API à un sous-ensemble de C qui, je le sais, est portable pour une grande variété de FFI et protège la bibliothèque des fuites de modifications dans les structures de données internes - attendez-vous à cela davantage dans les prochains blogs.


Maintenant, pour répondre à votre principale préoccupation: la performance.

Comme de nombreuses autres réponses ici, je suggérerais qu'écrire du code dans l'une ou l'autre langue fonctionnerait tout aussi bien sur le plan de la performance. D'un point de vue personnel, l'écriture de code correct en C ++ est plus facile en raison des fonctionnalités du langage, mais je pense que c'est une préférence personnelle. Quoi qu'il en soit, les compilateurs sont vraiment intelligents et ont tendance à écrire de meilleurs codes que vous. Cela signifie que le compilateur optimisera probablement votre code mieux que vous ne le pourriez.

Je sais que beaucoup de programmeurs le disent, mais la première chose à faire est d’écrire votre code, puis de le profiler et d’optimiser là où votre profileur vous le suggère. Votre temps sera bien mieux utilisé pour la production de fonctionnalités, puis l'optimisation une fois que vous pourrez voir où se trouvent vos goulots d'étranglement.


Maintenant, pour quelques lectures amusantes sur la façon dont les fonctionnalités et les optimisations linguistiques peuvent vraiment jouer en votre faveur:

std :: unique_ptr a zéro overhead

constexp permet un calcul au moment de la compilation

déplacer la sémantique empêche les objets temporaires inutiles


std::unique_ptr has zero overheadCela ne peut vraisemblablement pas être vrai (techniquement parlant) car son constructeur doit être appelé si la pile se déroule à cause d'une exception. Un pointeur brut n'a pas cette surcharge et reste correct si votre code ne sera probablement pas lancé. Un compilateur ne pourra pas le prouver dans le cas général.
Thomas Eding

2
@ThomasEding Je faisais référence à la taille et à la surcharge d'exécution en ce qui concerne le code sans exception. Corrigez-moi si je me trompe, mais certains modèles d'exécution n'engendrent aucune surcharge d'exécution lorsque des exceptions ne sont pas générées et autorisent néanmoins la propagation des exceptions lorsque cela est nécessaire. Malgré tout, quand une exception pourrait-elle être lancée dans le constructeur de unique_ptr? C'est déclaré noexcept, et donc au moins, il gère toutes les exceptions, mais je ne peux pas imaginer quel type d'exception pourrait même être levé en premier lieu.
vmrob

vmrob: Excusez-moi ... je voulais écrire "destructeur" au lieu de "constructeur". Je voulais aussi écrire «ne jettera certainement pas». Eeek!
Thomas Eding

2
@ThomasEding Vous savez, je ne pense pas que ce serait même grave si le destructeur lançait une exception. Tant que le destructeur n'introduit pas de nouvelles exceptions, la destruction est toujours nulle. De plus, je pense que l’ensemble du destructeur est intégré à un seul appel supprimer / gratuit avec optimisations.
vmrob

4

La différence de performances entre C ++ et C n’est due à rien dans le langage à proprement parler, mais à ce que vous tentez de faire. C'est comme une carte de crédit contre de l'argent. Cela ne vous fait pas dépenser plus, mais vous le faites quand même, à moins d'être très discipliné.

Voici un exemple de programme écrit en C ++, qui a ensuite été optimisé en fonction des performances. Vous devez savoir comment ajuster les performances de manière agressive, quelle que soit la langue. La méthode que j'utilise est la pause aléatoire, comme le montre cette vidéo .

Les types de tâches coûteuses que le C ++ vous incite à faire sont la gestion de mémoire excessive, la programmation de type notification, la confiance de votre programme dans des bibliothèques d'abstraction multicouches (comme @Ian l'a dit), la dissimulation de lenteur, etc.


2

C n’a aucun avantage en termes de performances par rapport au C ++ si vous faites la même chose dans les deux langages. Vous pouvez utiliser n'importe quel ancien code C écrit par n'importe quel programmeur décent et le transformer en code C ++ valide et équivalent, qui s'exécutera tout aussi rapidement (à moins que vous et votre compilateur sachiez ce que fait le mot clé "restrict" et que vous l'utilisez efficacement, mais la plupart des gens ne le font pas).

Les performances du C ++ peuvent être très différentes, que ce soit plus lentement ou plus rapidement, si (1) vous utilisez la bibliothèque standard C ++ pour effectuer des tâches beaucoup plus rapidement et facilement sans utiliser la bibliothèque, ou (2) si vous utilisez la bibliothèque standard C ++ faire les choses beaucoup plus facilement et rapidement que de réimplémenter la bibliothèque dans le mauvais C.


1
cela ne semble rien offrir de substantiel par rapport à ce qui a été expliqué dans 6 réponses précédentes
gnat

Je pense que cette réponse mentionne un point important que personne d’autre n’a mentionné. À première vue, il semble que C ++ soit un sur-ensemble de C, donc si vous pouvez écrire une implémentation rapide de C, vous devriez pouvoir écrire une implémentation de C ++ équivalente. Cependant, C99 prend en charge le mot-clé restrict qui permet d'éviter les alias de pointeur non intentionnels. C ++ n'a pas un tel support. La possibilité d'éviter les alias de pointeur est une fonctionnalité importante de Fortran qui le rend utile pour les applications hautes performances. Je pense qu’il est également possible de tirer de meilleures performances de C99 que de C ++ dans des domaines similaires.
user27539
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.