web-dev-qa-db-fra.com

Mon application iPhone subira-t-elle un impact sur les performances si j'utilise Objective-C pour du code de bas niveau?

Lors de la programmation d'une application gourmande en CPU ou en GPU sur l'iPhone ou tout autre matériel portable, vous devez prendre des décisions algorithmiques judicieuses pour rendre votre code rapide.

Mais même de bons choix d'algorithmes peuvent être lents si le langage que vous utilisez fonctionne moins bien qu'un autre.

Existe-t-il des données fiables comparant Objective-C à C++, en particulier sur l'iPhone mais peut-être uniquement sur le bureau Mac, pour les performances de divers aspects linguistiques similaires? Je connais très bien cet article comparant C et Objective-C , mais il s'agit d'une question plus large de comparer deux langages orientés objet l'un à l'autre.

Par exemple, une recherche vtable C++ est-elle vraiment plus rapide qu'un message Obj-C? Combien plus rapide? Filetage, polymorphisme, tri, etc. Avant de partir à la recherche d'un projet avec des modèles d'objets en double et divers codes de test, je veux savoir si quelqu'un l'a déjà fait et quels sont les résultats. Ce type de test et de comparaison est un projet en soi et peut prendre un temps considérable. Ce n'est peut-être pas un projet, mais deux et seules les sorties peuvent être comparées.

Je recherche des données matérielles , pas de l'évangélisation. Comme beaucoup d'entre vous, j'aime et je déteste les deux langues pour diverses raisons. De plus, s'il y a quelqu'un là-bas qui poursuit activement la même chose, je serais intéressant de lancer un code pour voir les résultats finaux, et je suis sûr que d'autres aideraient aussi. Je suppose qu'ils ont tous deux des forces et des faiblesses, mon objectif est de savoir précisément ce qu'ils sont afin qu'ils puissent être évités/exploités dans des scénarios du monde réel.

68
slf

Mike Ash a quelques chiffres pour les performances de divers appels de méthode Objective-C par rapport à C et C++ dans son article "Comparaisons de performances des opérations courantes" . En outre, cet article par Savoy Software est une lecture intéressante quand il s'agit de régler les performances d'une application iPhone en utilisant Objective-C++.

J'ai tendance à préférer la syntaxe claire et descriptive d'Objective-C à Objective-C++, et je n'ai pas trouvé que le langage lui-même était la source de mes goulots d'étranglement de performance. J'ai même tendance à faire des choses que je sais sacrifier un peu de performance si elles rendent mon code beaucoup plus maintenable.

58
Brad Larson

Oui, un C++ bien écrit est considérablement plus rapide. Si vous écrivez des programmes critiques pour les performances et que votre C++ n'est pas aussi rapide que C (ou dans quelques pour cent), quelque chose ne va pas. Si votre implémentation ObjC est aussi rapide que C, alors quelque chose est généralement incorrect - c'est-à-dire que le programme est probablement un mauvais exemple d'ObjC OOD car il utilise probablement certains des astuces "sales" pour passer sous la couche d'abstraction dans laquelle il opère, telles que les accès directs à l'ivar.

La `` comparaison '' de Mike Ash est très trompeuse - je ne recommanderais jamais l'approche pour comparer les temps d'exécution des programmes que vous avez écrits, ni la recommander pour comparer C vs C++ vs ObjC. Les résultats présentés proviennent d'un test avec les optimisations du compilateur désactivées . Un programme compilé avec des optimisations désactivées est rarement pertinent lorsque vous mesurez les temps d'exécution. Le voir comme une référence qui compare C++ à Objective-C est défectueux. Le test compare également des fonctionnalités individuelles, plutôt que des implémentations optimisées du monde réel - les fonctionnalités individuelles sont combinées de manière très différente avec les deux langues. Ceci est loin d'être une référence de performance réaliste pour des implémentations optimisées. Exemples: avec les optimisations activées , le cache IMP est aussi lent que les appels de fonction virtuelle. La répartition statique (par opposition à la répartition dynamique, par exemple en utilisant virtual) et les appels à des types C++ connus (où la répartition dynamique peut être contournée) peuvent être optimisés de manière agressive. Ce processus est appelé dévirtualisation, et lorsqu'il est utilisé, une fonction membre qui est déclarée virtual peut même être inlined. Dans le cas du test Mike Ash où de nombreux appels sont effectués vers des fonctions membres qui ont été déclarées virtual et ont des corps vides: ces appels sont entièrement optimisés entièrement lorsque le type est connu car le compilateur voit l'implémentation et est capable de déterminer la répartition dynamique n'est pas nécessaire. Le compilateur peut également éliminer les appels à malloc dans les versions optimisées (favorisant le stockage de pile). Ainsi, l'activation des optimisations du compilateur dans n'importe lequel de C, C++ ou Objective-C peut produire des différences dramatiques dans les temps d'exécution.

Cela ne veut pas dire que les résultats présentés sont entièrement inutiles. Vous pouvez obtenir des informations utiles sur les API externes si vous souhaitez déterminer s'il existe des différences mesurables entre les temps qu'ils passent dans pthread_create ou +[NSObject alloc] sur une plate-forme ou une architecture par rapport à une autre. Bien sûr, ces deux exemples utiliseront des implémentations optimisées dans votre test (sauf si vous les développez). Mais pour comparer une langue à une autre dans les programmes que vous compilez… les résultats présentés sont inutiles avec les optimisations désactivées.

Création d'objet

Pensez également à la création d'objets dans ObjC - chaque objet est alloué dynamiquement (par exemple sur le tas). Avec C++, les objets peuvent être créés sur la pile (par exemple, à peu près aussi rapidement que la création d'une structure C et l'appel d'une fonction simple dans de nombreux cas), sur le tas ou en tant qu'éléments de types de données abstraits. Chaque fois que vous allouez et libérez (par exemple via malloc/free), vous pouvez introduire un verrou. Lorsque vous créez une structure C ou un objet C++ sur la pile, aucun verrou n'est requis (bien que les membres intérieurs puissent utiliser des allocations de segment de mémoire) et cela ne coûte souvent que quelques instructions ou quelques instructions plus un appel de fonction.

De plus, les objets ObjC sont des instances comptées par référence. Le besoin réel d'un objet d'être un std::shared_ptr en performance critique C++ est très rare. Il n'est ni nécessaire ni souhaitable en C++ de faire de chaque instance une instance partagée et comptée par référence. Vous avez beaucoup plus de contrôle sur la propriété et la durée de vie avec C++.

Tableaux et collections

Les tableaux et de nombreuses collections en C et C++ utilisent également des conteneurs fortement typés et une mémoire contiguë. Étant donné que l'adresse des membres de l'élément suivant est souvent connue, l'optimiseur peut faire beaucoup plus et vous disposez d'une excellente mémoire cache et d'une excellente mémoire. Avec ObjC, c'est loin de la réalité pour les objets standard (par exemple NSObject).

Envoi

En ce qui concerne les méthodes, de nombreuses implémentations C++ utilisent peu d'appels virtuels/dynamiques, en particulier dans les programmes hautement optimisés. Ce sont des appels de méthode statiques et du fourrage pour les optimiseurs.

Avec les méthodes ObjC, chaque appel de méthode (envoi de message objc) est dynamique et constitue par conséquent un pare-feu pour l'optimiseur. En fin de compte, cela entraîne de nombreuses restrictions ou inconvénients concernant ce que vous pouvez et ne pouvez pas faire pour maintenir les performances au minimum lors de l'écriture d'ObjC critiques. Cela peut entraîner des méthodes plus importantes, la mise en cache IMP, une utilisation fréquente de C.

Certaines applications en temps réel ne peuvent pas utiliser tout la messagerie ObjC dans leurs chemins de rendu. Aucun - le rendu audio en est un bon exemple. La répartition ObjC n'est tout simplement pas conçue à des fins en temps réel; Des allocations et des verrous peuvent se produire en arrière-plan lors de la messagerie d'objets, rendant la complexité/la durée de la messagerie objc suffisamment imprévisible pour que le rendu audio puisse manquer sa date limite.

Autres fonctionnalités

C++ fournit également des implémentations génériques/modèles pour bon nombre de ses bibliothèques. Ceux-ci optimisent très bien. Ils sont sûrs pour les types, et beaucoup d'inline et d'optimisations peuvent être faites avec des modèles (considérez-le comme le polymorphisme, l'optimisation et la spécialisation qui ont lieu lors de la compilation). C++ ajoute plusieurs fonctionnalités qui ne sont tout simplement pas disponibles ou comparables dans ObjC strict. Essayer de comparer directement des langs, des objets et des bibliothèques qui sont très différents n'est pas si utile - c'est un très petit sous-ensemble de réalisations réelles. Il est préférable d'étendre la question à une bibliothèque/un cadre ou à un programme réel, compte tenu de nombreux aspects de la conception et de la mise en œuvre.

Autres points

Les symboles C et C++ peuvent être plus facilement supprimés et optimisés à différentes étapes de la construction (suppression, élimination du code mort, inline et inlining précoce, ainsi que Link Time Optimization). Les avantages de ceci incluent des tailles binaires réduites, des temps de lancement/chargement réduits, une consommation de mémoire réduite, etc. Pour une seule application, ce n'est peut-être pas si grave; mais si vous réutilisez beaucoup de code, et vous le devriez, vos bibliothèques partagées pourraient ajouter beaucoup de poids inutile au programme, si elles sont implémentées ObjC - à moins que vous ne soyez prêt à sauter à travers des cerceaux enflammés. L'évolutivité et la réutilisation sont donc également des facteurs dans les projets de moyenne/grande envergure et dans les groupes où la réutilisation est élevée.

Bibliothèques incluses

Les implémenteurs de bibliothèque ObjC optimisent également l'environnement, de sorte que ses implémenteurs de bibliothèque peuvent utiliser certaines fonctionnalités de langage et d'environnement pour offrir des implémentations optimisées. Bien qu'il existe des restrictions assez importantes lors de l'écriture d'un programme optimisé en ObjC pur, certaines implémentations hautement optimisées existent dans Cocoa. C'est l'un des points forts de Cocoa, bien que la bibliothèque standard C++ (ce que certains appellent la STL) ne soit pas en reste non plus. Cocoa fonctionne à un niveau d'abstraction beaucoup plus élevé que C++ - si vous ne savez pas bien ce que vous faites (ou devriez faire), opérer plus près du métal peut vraiment vous coûter cher . Revenir à une bonne implémentation de bibliothèque si vous n'êtes pas un expert dans un domaine est une bonne chose, à moins que vous ne soyez vraiment prêt à apprendre. De plus, les environnements de Cocoa sont limités; vous pouvez trouver des implémentations/optimisations qui font un meilleur usage du système d'exploitation.

Si vous écrivez des programmes optimisés et avez de l'expérience dans C++ et ObjC, les implémentations propres C++ seront souvent deux fois plus rapides ou plus rapides que clean ObjC (oui, vous pouvez comparer avec Cocoa). Si vous savez comment optimiser, vous pouvez souvent faire mieux que des abstractions générales de niveau supérieur. Cependant, certaines implémentations C++ optimisées seront aussi rapides ou plus lentes que celles de Cocoa (par exemple, ma tentative initiale d'E/S de fichiers était plus lente que Cocoa - principalement parce que l'implémentation C++ initialise sa mémoire).

Cela dépend en grande partie des fonctionnalités linguistiques que vous connaissez. J'utilise les deux langs, ils ont tous deux des forces et des modèles/modèles différents. Ils se complètent assez bien et il y a d'excellentes bibliothèques pour les deux. Si vous implémentez un programme complexe et essentiel aux performances, une utilisation correcte des fonctionnalités et des bibliothèques de C++ vous donnera beaucoup plus de contrôle et fournira des avantages importants pour l'optimisation, de sorte que, entre de bonnes mains, "plusieurs fois plus vite" est une bonne attente par défaut ( ne vous attendez pas à gagner à chaque fois, ou sans travail, cependant). N'oubliez pas qu'il faut des années pour comprendre suffisamment le C++ pour vraiment atteindre ce point.

Je garde la majorité de mes chemins critiques de performances en C++, mais je reconnais également qu'ObjC est également une très bonne solution pour certains problèmes et qu'il existe de très bonnes bibliothèques disponibles.

55
justin

Il est très difficile de collecter des "données matérielles" pour ce qui n'est pas erroné.

Le plus gros problème avec une comparaison de fonctionnalité à fonctionnalité comme vous le suggérez est que les deux langues encouragent des styles de codage très différents. Objective-C est un langage dynamique avec typage canard, où l'utilisation typique de C++ est statique. Le même problème d'architecture orientée objet aurait probablement des solutions idéales très différentes en utilisant C++ ou Objective-C.

Mon sentiment (comme j'ai beaucoup programmé dans les deux langages, principalement sur des projets énormes): pour maximiser les performances d'Objective-C, il doit être écrit très près de C. Alors qu'avec C++, il est possible d'utiliser beaucoup plus le langage sans toute pénalité de performance par rapport à C.

Quel est le meilleur? Je ne sais pas. Pour des performances pures, C++ aura toujours l'Edge. Mais le style OOP d'Objective-C a certainement ses mérites. Je pense vraiment qu'il est plus facile de garder une architecture saine avec lui.

31
Johan Kotlinski

Ce n'est vraiment pas quelque chose auquel on peut répondre en général car cela dépend vraiment de la façon dont vous utilisez les fonctionnalités du langage. Les deux langues auront des choses dans lesquelles elles sont rapides, des choses dans lesquelles elles sont lentes et des choses qui sont parfois rapides et parfois lentes. Cela dépend vraiment de ce que vous utilisez et de la façon dont vous l'utilisez. La seule façon d'être certain est de profiler votre code.

Dans Objective C, vous pouvez également écrire du code c ++, donc il pourrait être plus facile de coder dans Objective C pour la plupart, et si vous trouvez quelque chose qui ne fonctionne pas bien, alors vous pouvez essayer d'écrire une version c ++ et voir si cela aide (C++ a tendance à mieux optimiser au moment de la compilation). L'objectif C sera plus facile à utiliser si les API avec lesquelles vous vous connectez sont également écrites dessus, et vous pourriez trouver son style OOP est plus facile ou plus flexible.

En fin de compte, vous devriez aller avec ce que vous savez que vous pouvez écrire du code sûr et robuste et si vous trouvez un domaine qui nécessite une attention particulière de l'autre langue, alors vous pouvez échanger. X-Code vous permet de compiler les deux dans le même projet.

7
Grant Peters

Je n'ai pas de données matérielles pour Objective C, mais j'ai un bon endroit pour rechercher C++.

C++ a commencé comme C avec Classes selon Bjarne Stroustroup dans sa réflexion sur les premières années de C++ ( http://www2.research.att.com/~bs/hopl2.pdf ), donc C++ peut être considéré (comme l'Objectif C) comme poussant C à ses limites pour l'orientation de l'objet.

Quelles sont ces limites? Au cours de la période 1994-1997, de nombreux chercheurs ont compris que l'orientation de l'objet avait un coût en raison de la liaison dynamique, par exemple lorsque les fonctions C++ sont marquées virtuelles et qu'il peut/peut ne pas y avoir de classes enfants qui remplacent ces fonctions. (Dans Java et C #, toutes les fonctions s'attendent à ce que les ctors soient intrinsèquement virtuels, et il n'y a pas grand-chose que vous puissiez faire à ce sujet.) Dans "A Study of Devirtualization Techniques for a Java Just-In-Time Compiler "des chercheurs d'IBM Research Tokyo, ils contrastent les techniques utilisées pour y faire face, y compris celle d'Urz Hölzle et de Gerald Aigner. Urz Hölzle, dans un article séparé avec Karel Driesen, avait montré qu'en moyenne 5,7% du temps dans les programmes C++ (et jusqu'à ~ 50%) était passé à appeler des fonctions virtuelles (par exemple vtables + thunks). Il a ensuite travaillé avec des chercheurs de Smalltalk dans ce qui a fini par Java HotSpot VM pour résoudre ces problèmes dans OO. Certaines de ces fonctionnalités sont rétroportées en C++ (par exemple 'protégées' et gestion des exceptions).

Comme je l'ai mentionné, C++ est de type statique où Objective C est de type canard. La différence de performances dans l'exécution (mais pas les lignes de code) est probablement le résultat de cette différence.

3
aalok

J'ai fait quelques tests que j'ai faits sur un iPhone 3G il y a presque 2 ans, il n'y avait pas de documentation ou de chiffres précis à l'époque. Je ne sais pas à quel point ils sont valides mais le code source est publié et joint.

Ce n'est pas un test très poussé, j'étais principalement intéressé par NSArray vs C Array pour itérer un grand nombre d'objets.

http://memo.tv/nsarray_vs_c_array_performance_comparison

http://memo.tv/nsarray_vs_c_array_performance_comparison_part_ii_makeobjectsperformselector

Vous pouvez voir que le tableau C est beaucoup plus rapide à des itérations élevées. Depuis lors, je me suis rendu compte que le goulot d'étranglement n'est probablement pas l'itération du NSArray mais l'envoi du message. Je voulais essayer methodForSelector et appeler les méthodes directement pour voir l'ampleur de la différence, mais je n'y suis jamais parvenu. Selon les références de Mike Ash, c'est un peu plus de 5 fois plus rapide.

3
memo

Cette étude dit que pour vraiment obtenir les performances dans un jeu gourmand en CPU, vous devez utiliser C. L'article lié est complet avec un projet XCode que vous pouvez exécuter.

Je crois que la ligne de fond est: Utilisez Objective-C où vous devez interagir avec les fonctions de l'iPhone (après tout, mettre des trampolines partout peut ' t ne convient à personne ), mais en ce qui concerne les boucles, des choses comme les classes d'objets vectorielles ou l'accès intensif aux tableaux, restez avec les tableaux C++ STL ou C pour obtenir de bonnes performances.

Je veux dire qu'il serait totalement idiot de voir position = [[Vector3 alloc] init] ;. Vous demandez simplement un résultat de performance si vous utilisez des comptages de références sur des objets de base comme un vecteur de position.

1
bobobobo