Quel est le meilleur (ou le plus rapide), une boucle C++ for
ou l'opérateur foreach
fourni par Qt? Par exemple, la condition suivante
QList<QString> listofstrings;
Ce qui est mieux?
foreach(QString str, listofstrings)
{
//code
}
ou
int count = listofstrings.count();
QString str = QString();
for(int i=0;i<count;i++)
{
str = listofstrings.at(i);
//Code
}
Cela n'a vraiment pas d'importance dans la plupart des cas.
Le grand nombre de questions sur StackOverflow quant à savoir si cette méthode ou cette méthode est plus rapide, démentent le fait que, dans la grande majorité des cas, le code passe la plupart de son temps à attendre que les utilisateurs fassent quelque chose.
Si vous êtes vraiment inquiet, profilez-le par vous-même et agissez en fonction de ce que vous trouvez.
Mais je pense que vous trouverez très probablement que ce n'est que dans le travail le plus intense de traitement de données que cette question est importante. La différence peut bien n'être que de quelques secondes et même alors, uniquement lors du traitement d'un grand nombre d'éléments.
Faites fonctionner votre code premier. Ensuite, faites-le fonctionner rapide (et seulement si vous trouvez un problème de performances réel).
Le temps consacré à l'optimisation avant que vous ayez terminé la fonctionnalité et que vous puissiez profiler correctement, est principalement du temps perdu.
Tout d'abord, je voudrais simplement dire que je suis d'accord avec Pax et que la vitesse n'y entre probablement pas. foreach gagne haut la main en fonction de la lisibilité, et c'est suffisant dans 98% des cas.
Mais bien sûr, les gars de Qt l'ont étudié et ont fait du profilage: http://blog.qt.io/blog/2009/01/23/iterating-efficiently/
La principale leçon à tirer de cela est: utilisez les références const dans les boucles en lecture seule car cela évite la création d'instances temporaires. Cela rend également le but de la boucle plus explicite, quelle que soit la méthode de bouclage que vous utilisez.
Cela n'a vraiment pas d'importance. Les chances sont que si votre programme est lent, ce n'est pas le problème. Cependant, il convient de noter que vous ne faites pas une comparaison complètement égale. foreach
de Qt est plus similaire à ceci (cet exemple utilisera QList<QString>
):
for(QList<QString>::iterator it = Con.begin(); it != Con.end(); ++it) {
QString &str = *it;
// your code here
}
La macro est capable de le faire en utilisant des extensions de compilateur (comme __typeof__
De GCC) pour obtenir le type du conteneur passé. Imaginez également que le BOOST_FOREACH
De boost est très similaire dans son concept.
La raison pour laquelle votre exemple n'est pas juste est que votre version non-Qt ajoute du travail supplémentaire.
Vous indexez au lieu de vraiment itérer. Si vous utilisez un type avec une allocation non contiguë (je soupçonne que cela pourrait être le cas avec QList<>
), Alors l'indexation sera plus coûteuse car le code doit calculer "où" le n-ième élément est.
Cela étant dit. Cela encore n'a pas d'importance. La différence de synchronisation entre ces deux morceaux de code sera négligeable, voire inexistante. Ne perdez pas votre temps à vous en soucier. Écrivez ce que vous trouvez le plus clair et le plus compréhensible.
EDIT: En prime, actuellement je suis fortement en faveur de la version C++ 11 de l'itération de conteneur, elle est propre, concise et simple:
for(QString &s : Con) {
// you code here
}
Je ne veux pas répondre à la question qui est plus rapide, mais je veux dire laquelle est meilleure.
Le plus gros problème avec foreach de Qt est le fait qu'il faut une copie de votre conteneur avant d'itérer dessus. Vous pourriez dire "cela n'a pas d'importance parce que les classes Qt sont recomptées" mais parce qu'une copie est utilisée, vous ne changez pas du tout votre conteneur d'origine.
En résumé, foreach de Qt ne peut être utilisé que pour les boucles en lecture seule et doit donc être évité. Qt se fera un plaisir de vous laisser écrire une boucle foreach qui, selon vous, mettra à jour/modifiera votre conteneur mais à la fin toutes les modifications seront rejetées.
Puisque Qt 5.7 la macro foreach
est déconseillée, Qt vous encourage à utiliser à la place le C++ 11 for
.
http://doc.qt.io/qt-5/qtglobal.html#foreach
(plus de détails sur la différence ici: https://www.kdab.com/goodbye-q_foreach/ )
Premièrement, je suis entièrement d'accord avec la réponse selon laquelle "cela n'a pas d'importance". Choisissez la solution la plus propre et optimisez si elle devient un problème.
Mais une autre façon de voir les choses est que souvent, la solution la plus rapide est celle qui décrit le mieux votre intention. Dans ce cas, foreach de QT indique que vous souhaitez appliquer une action pour chaque élément du conteneur.
Une boucle simple pour dire que vous souhaitez un compteur i
. Vous souhaitez en ajouter plusieurs à cette valeur i, et tant qu'il est inférieur au nombre d'éléments dans le conteneur, vous souhaitez effectuer une action.
En d'autres termes, la boucle simple pour sur-spécifie le problème. Cela ajoute beaucoup d'exigences qui ne font pas réellement partie de ce que vous essayez de faire. Vous ne faites pas attention à propos du compteur de boucles. Mais dès que vous écrivez une boucle for, elle doit être là.
D'un autre côté, les personnes QT n'ont fait aucune promesse supplémentaire susceptible d'affecter les performances. Ils garantissent simplement d'itérer à travers le conteneur et d'appliquer une action à chacun.
En d'autres termes, la solution la plus propre et la plus élégante est souvent la plus rapide.
Le foreach de Qt a une syntaxe plus claire pour la boucle for IMHO, c'est donc mieux dans ce sens. En termes de performances, je doute qu'il y ait quelque chose dedans.
Vous pourriez envisager d'utiliser à la place BOOST_FOREACH , car c'est une fantaisie bien pensée pour la boucle, et elle est portable (et probablement fera son chemin en C++ un jour et est également à l'épreuve du temps).
Une référence, et ses résultats, à ce sujet peuvent être trouvés à http://richelbilderbeek.nl/CppExerciseAddOneAnswer.htm
À mon humble avis (et bien d'autres ici), cela (c'est la vitesse) n'a pas d'importance.
Mais n'hésitez pas à tirer vos propres conclusions.
Pour les petites collections, cela devrait être important et pour chaque tendance à être plus claire.
Cependant, pour les collections plus importantes, for commencera à battre foreach à un moment donné. (en supposant que l'opérateur 'at ()' est efficace.
Si c'est vraiment important (et je suppose que c'est puisque vous le demandez), alors la meilleure chose à faire est de le mesurer. Un profileur devrait faire l'affaire, ou vous pourriez créer une version de test avec une certaine instrumentation.
Je m'attendrais à ce que foreach fonctionne nominalement plus rapidement dans certains cas, et à peu près la même chose dans d'autres, sauf dans les cas où les éléments sont un tableau réel, auquel cas la différence de performance est négligeable.
S'il est implémenté au-dessus d'un énumérateur, il peut être plus efficace qu'une indexation simple, selon l'implémentation. Il est peu probable qu'il soit moins efficace. Par exemple, si quelqu'un expose un arbre équilibré à la fois indexable et énumérable, alors foreach sera décemment plus rapide. En effet, chaque index devra rechercher indépendamment l'élément référencé, tandis qu'un énumérateur dispose du contexte du nœud actuel pour naviguer plus efficacement vers le suivant.
Si vous avez un tableau réel, cela dépend de l'implémentation du langage et de la classe si foreach sera plus rapide pour le même que pour.
Si l'indexation est un décalage de mémoire littéral (tel que C++), alors for devrait être légèrement plus rapide car vous évitez un appel de fonction. Si l'indexation est une indirection tout comme un appel, alors elle devrait être la même.
Tout cela étant dit ... J'ai du mal à trouver un cas de généralisation ici. Il s'agit du dernier type d'optimisation que vous devriez rechercher, même en cas de problème de performances dans votre application. Si vous avez un problème de performances qui peut être résolu en modifiant la façon dont vous itérez, vous n'avez pas vraiment de problème de performances. Vous avez un BUG, parce que quelqu'un a écrit soit un itérateur vraiment merdique, soit un indexeur vraiment merdique.
Vous pourriez regarder la fonction for_each de la STL. Je ne sais pas si ce sera plus rapide que les deux options que vous présentez, mais il est plus standardisé que le Qt foreach et évite certains des problèmes que vous pouvez rencontrer avec une boucle for régulière (à savoir l'indexation hors limites et les difficultés avec la traduction de la boucle dans une structure de données différente).