a) strong> La réponse est là sur ce site (question 3). Je ne peux tout simplement pas comprendre 3. A P>
blockQuote> pour (int i = 100000; i> 0; i--) {} code> p> p> P>
pour (int i = 1; i <100001; i ++) {} code> p> p> P>
16 Réponses :
sur de nombreux compilateurs, les instructions de la machine émises pour une boucle allant à l'envers sont plus efficaces, car les tests de zéro (et donc zéro de registre) sont plus rapides qu'une charge immédiate d'une valeur constante. P>
D'autre part, un bon compilateur d'optimisation doit pouvoir inspecter la boucle intérieure et déterminer qu'on descend ne causera aucun effet secondaire ... P>
BTW, c'est une terrible question d'entrevue à mon avis. Sauf si vous parlez d'une boucle qui gère 10 millions de fois et que vous avez déterminé que le léger gain n'est pas dépassé par de nombreux cas de recréer la valeur de boucle avant (N - I), tout gain de performance sera minimal. P>
comme toujours strong>, ne pas micro-optimiser sans analyse comparative de performance et au détriment du code plus difficile à comprendre. P>
Ouais, ce type de micro-optimisation peut avoir un peu de validité de C ou C ++, mais pas pour Java.
Bien que cela soit vrai, le gain de performance est si marginal que cela ne vaut pas l'effort. Si quelqu'un m'a dit que je devrais utiliser une décrémentation de boucle en raison des gains de performance, ils essaient donc trop fort, je suis donc d'accord sur une terrible question d'entrevue.
La réponse est A (comme vous l'avez probablement découvert sur le site Web) P>
Je pense que la raison est que la condition i> 0 code> pour terminer la boucle est plus rapide à tester. p>
Le résultat final est que pour toute application critique de non-performance, la différence n'est probablement pas pertinente. Comme d'autres l'ont signalé, il y a des moments où l'utilisation de ++ i au lieu de I ++ pourrait être plus rapide, cependant, en particulier dans la boucle, tout compilateur moderne devrait optimiser cette distinction. P>
Cela dit, la différence est probablement liée aux instructions sous-jacentes qui sont générées pour la comparaison. Tester si une valeur est égale à 0 est simplement une NAND Strike> NOR GATE. ATTENDU QUE les tests Si une valeur est égale à une constante arbitraire nécessite un chargement constant dans un registre, puis comparant les deux registres. (Cela nécessiterait probablement un délai supplémentaire de la porte ou deux.) Cela dit, avec une pipeline et un alus moderne, je serais surpris si la distinction était importante pour commencer. P>
"Test si une valeur est égale à 0 est simplement une porte NAND." - Une porte Nand n'est certainement pas assez! Le fait est que le test-zéro est câblé dans la plupart des processeurs; Sur X86, toute instruction arithmétique définit le drapeau zéro si le résultat de l'opération est nul, ce qui signifie qu'aucune instruction de comparaison n'est nécessaire.
Désolé, je voulais dire ni pas nand. (Vous avez raison.) Cela dit, pourquoi une porte ni une porte (compte tenu de suffisamment d'intrants) est-elle insuffisante? Ni retourne 1 si toutes les intrants sont 0, non?
Je ne pense pas que 32 entrées ni portes sont pratiques. Probablement une sorte de chaînage serait utilisé pour un système câblé. Mais ensuite, sur les transformateurs modernes, cela serait probablement effectué avec le microcode de toute façon ...
Je vois, merci. Les cours que j'ai pris à l'université ne sont pas entrés dans ce genre de détail.
Ces types de questions sont en grande partie une distraction non pertinente que certaines personnes sont obsédées par celle-ci. Appelez-le le culte de la micro-optimisation em> ou quoi que ce soit que vous aimez, mais est-ce plus rapide de boucler ou de descendre? Sérieusement? Vous utilisez ce qui est approprié pour ce que vous faites. Vous n'écrivez pas à votre code autour de l'enregistrement de deux cycles d'horloge ou quoi que ce soit. laissez le compilateur faire ce que c'est pour vous et vous faire car une concaténation excessive entraîne une fragmentation de la mémoire, mais pour une constante, le compilateur peut (et le fera) optimiser ce: P> public final static String BLAH = "This is a " + 3 + " test";
... Mais sur Gentoo, j'ai un drapeau d'utilisation pour inverser par magie toutes les boucles code> pour code> de l'application et il gagne-moi 218 ips par ghz, bébé
Êtes-vous sûr de la chose math.max (..) chose? IIRC, les JVM optimisent généralement beaucoup de mathématiques * - tournez les choses en code direct, au lieu d'appels de méthode, etc. - depuis son échange non-utilisateur ... c'est-à-dire math.max () IS - IIRC - réellement mis en œuvre de manière identique, dans une combinaison JVM / Javac décente.
@ADAM: Si vous regardez le site lié, il affirme que Math.Max () est plus lent. Ce serait à cause de la fonction d'appel de fonction, de la boxe / de la boîte de consigne (bien qu'il existe des versions de max () pour les types primitifs, donc je ne suis pas sûr que cela soit le cas) ou les deux. Quel que soit le cas, c'est la micro-optimisation.
Les boucles sont identiques, à l'exception d'une partie critique: p>
i> 0; et i <100001; p>
La vérification supérieure à zéro est effectuée en vérifiant le NZP (communément appelé code de condition ou un bit zéro négatif ou un bit positif) de l'ordinateur. p>
Le bit NZP est défini chaque fois que des fonctionnalités telles que la charge et, l'addition ECT. sont effectuées. p>
Le plus grand que la vérification ne peut pas utiliser directement ce bit (et prend donc un peu plus longtemps ...) La solution générale consiste à établir l'une des valeurs négatives (en faisant un peu pas dans le sens, puis en ajoutant 1) puis l'ajoutant à la valeur comparée. Si le résultat est égal à zéro, ils sont égaux. Positif, puis la deuxième valeur (pas la négation) est plus grande. Négatif, alors la première valeur (Neg) est plus grande. Cette vérification prend un peu plus longue que la vérification directe du NZP. p>
Je ne suis certain pas à 100% que c'est la raison derrière cela, mais cela semble être une raison possible ... P>
Lorsque vous descendez au niveau le plus bas (code de la machine, mais je vais utiliser l'assemblage puisqu'il mappe la plupart du temps), la différence entre une boucle vide décrémentant à 0 et une incrémentation à 50 (par exemple) est Souvent le long des lignes de: c'est parce que le drapeau zéro dans la plupart des processeurs saints est défini par l'instruction DECREMENTE lorsque vous atteignez zéro. Il n'arrive généralement pas à dire pour l'instruction d'incrément quand il atteint 50 (car il n'y a rien de spécial à propos de cette valeur, contrairement à zéro). Donc, vous devez comparer le registre avec 50 pour définir le drapeau zéro. P> Cependant, demandez à laquelle des deux boucles: p>
ld b,100000 dsw a
sub b,a
dsw b
Bon conseil. Je soulignerais également qu'avec la prédiction de la succursale, les instructions de montage de comptage et de comptage aura une différence de performance négligeable (mais d'accord avec vous que ce type de micro-optimisation ne vaut pas la peine de polluer le code source de).
-1 Pour ne pas répondre à la question posée du tout. La question dit spécifiquement: "en Java". Ce qui se passe dans le code de la machine est hors de propos, étant donné combien de couches de VM sont assises entre les deux.
Vous constaterez que vous avez répondu au deuxième bit, le bit qui indique que vous devriez iTER dans la direction qui a le plus de sens. Même avec Java, les calculs de la forme 100000-i code> vont presque certainement submerger tout le petit avantage que vous pouvez gagner à l'inverser de la boucle.
Paxdiablo, tout compilateur d'optimisation le fera plus vite (c'est-à-dire inversé). En fait, tout compilateur d'optimisation devrait dérouler les boucles (en Java, vous les dérouler les plus certainement déroulement, pas dans ce cas où ils ne sont tout simplement pas ignorés et ignorés complètement)
Kevin, tout environnement Java décent finira par éventuellement jit le code dans le code de la machine afin que est i> pertinent.
En ce qui concerne les tests de zéro dans la JVM: il peut apparemment être fait avec IFEQ alors que les tests de tout autre requièrent if_icmpeq qui implique également de mettre une valeur supplémentaire sur la pile. p>
Test de > 0 code>, comme dans la question, peut être fait avec IFGT , tandis que Test de
<100001 code> aurait besoin if_icmplt . p>
Ceci n'est approprié que lorsque le JVM interpréte le code d'octets, une fois qu'il est optimisé au code natif, cela ne fait aucune différence et dans le cas d'une boucle vide pourrait remplacer sans rien.
Même dans le code natif, la plupart des architectures (?) Ont une instruction comparant à zéro et à une ou deux autres manières de comparer avec tout ce qui est une tique ou deux plus ralentissement. En théorie, ce sera probablement une différence même si je dirais que la différence ne vaut pas la peine de compter et que vous aurez des chances que vous devrez faire d'autres "astuces" stupides à l'intérieur de la boucle, car vous comptez le mauvais sens. Micro optimisation typique.
@Fredrik: La plupart des architectures peuvent tester zéro tout en effectuant l'incrément / décrément. Donc, vous n'avez pas besoin d'une instruction de comparaison du tout. X86 met à jour le "drapeau zéro" (entre autres) dans le cadre d'une instruction arithmétique, tandis que le bras vous permet de spécifier si vous souhaitez que une instruction arithmétique particulière de mettre à jour les indicateurs. Cependant, cela a un effet beaucoup moins important que celui utilisé, en raison d'une meilleure opération de pipeline et de superscalar.
@Artelius: Je sais (même si je ne suis pas d'accord, il est valable pour "la plupart des architectures", mais je suppose que cela dépend de l'endroit où vous dessinez la ligne lorsque vous comptez). Cependant, il suffit de tester le drapeau zéro est presque toujours plus rapide que de le faire et autre chose. Le fait que vous puissiez faire les deux dans une seule instruction ne comporte pas vraiment que toutes les instructions n'exécutent pas dans un nombre égal de ticks d'horloge. Néanmoins, c'est plutôt non pertinent et ne fait pas beaucoup de différence en réalité.
sur une implémentation moderne Java, ce n'est pas vrai.
Résumant les chiffres jusqu'à un milliard d'un milliard comme référence: Notez que les différences de temps sont fragiles, de petits changements quelque part près des boucles peuvent les transformer. P> < Strong> Edit: strong>
Les boucles de référence sont p> et p> à l'aide d'une somme de type INT est environ trois fois plus rapide, mais alors somme débordements.
Avec BigInteger, il est plus de 50 fois plus lent: p>
Donc, pour calculer "Somme 49999999999500000000", avez-vous utilisé Longs ou Bigintegers? Ces derniers en particulier ont tellement de frais généraux qu'il mettra en maillant les différentes boucles. Considérez que commencer à l'extrémité supérieure de la plage, les chiffres deviennent très tôt très tôt et que la vitesse d'ajout de Bigintegers dépend de leur taille, cela en ferait un test très déloyal. Note, je ne discute de la manière de la performance, je dis simplement que les points de repère ne sont pas utiles que si vous détachez vos méthodes, de sorte que d'autres peuvent les examiner pour biais et reproduire les résultats pour eux-mêmes.
Ceci est à propos de la question la plus stupide que j'ai jamais vue. Le corps de la boucle est vide. Si le compilateur est bon, il n'aura qu'aucun code. Cela ne fait rien, je ne peux pas jeter une exception et ne modifie rien en dehors de sa portée. p>
Assumer que votre compilateur n'est pas si intelligent ou que vous n'aviez réellement pas de corps de boucle vide: L'argument "Compteur de boucle d'attente" est logique pour certaines langues de montage (il peut également avoir du sens au code d'octet Java, je ne le connais pas spécifiquement). Cependant, le compilateur aura très souvent la possibilité de transformer votre boucle pour utiliser des compteurs décréments. Sauf si vous avez un corps en boucle dans lequel la valeur de I est explicitement utilisée, le compilateur peut faire cette transformation. Encore une fois, vous ne voyez souvent aucune différence. P>
Une meilleure question est; p>
Ceci est beaucoup plus important qu'une différence notionnelle de performance. Personnellement, je voudrais souligner que les performances ne devraient pas être les critères de détermination de la différence ici. S'ils ne m'aimaient pas avoir contesté leur hypothèse à ce sujet, je ne serais pas malheureux de ne pas avoir le travail. ;) p>
Êtes-vous sûr que l'intervieweur qui demande une telle question s'attend à une réponse directe ("le numéro un est plus rapide" ou "numéro deux est plus rapide"), ou si cette question est invitée à provoquer une discussion, comme cela se produit dans le répond aux gens qui donnent ici? p>
En général, il est impossible de dire lequel est plus rapide, car il dépend fortement du compilateur Java, du JRE, de la CPU et d'autres facteurs. Utiliser l'un ou l'autre dans votre programme Juste parce que vous pensez que l'un des deux est plus rapide sans comprendre les détails au niveau le plus bas est programmation superstitieuse . Et même si une version est plus rapide que l'autre sur votre environnement particulier, la différence est probablement si petite que ce n'est pas pertinent. P>
écrire Clear Code au lieu d'essayer d'être intelligent. P>
Dans la page citée, l'auteur dit que la seconde est plus rapide et ne fournit pas une raison. Par conséquent, la question.
Code typiquement réel fonctionnera plus rapidement à compter vers le haut. Il y a quelques raisons pour cela: p>
Assurez-vous que la bonne chose sera généralement plus rapide. La micro-optimisation inutile est mauvaise. Je n'ai pas à desseiment écrit des boucles arrière depuis la programmation 6502 Assembleur. P>
Ces questions ont leur base sur de vieilles recommandations de la meilleure pratique. Tout est question de comparaison: la comparaison de 0 est connue pour être plus rapide. Il y a des années, cela aurait pu être considéré comme assez important. De nos jours, surtout avec Java, je préférerais laisser le compilateur et le VM faire leur travail et je me concentrerais sur la rédaction de code qui est égal à maintenir et à comprendre. P>
sauf s'il ya des raisons de le faire autrement. N'oubliez pas que les applications Java ne fonctionnent pas toujours sur Hotspot et / ou du matériel rapide. P>
Il n'y a vraiment que deux façons de répondre à cette question.
Pour vous dire que cela, cela n'a pas vraiment d'importance, et que vous perdez votre temps, vous vous demandez même. P> Li>
Pour vous dire que la seule façon de savoir est de faire fonctionner une référence fiable sur votre matériel de production actuel, votre système d'exploitation et votre installation JRE que vous vous souciez. P> LI> ol>
Donc, j'ai fait de vous une référence annoncée que vous pourriez utiliser pour essayer ici ici: p>
Bien totalement, que se passe-t-il si vous ne boucle que 2 fois ?! Si vous aviez comme 3 de ces ventouses, vous économiseriez 3NS. 3 Freakin nano secondes homme! Vous êtes juste assez hardcore, je suppose. Et oui je plaisante.
"Nous avons brisé votre lien. Priez nous ne le casserons pas plus loin" :-) En fait, lien est i> brisé à nouveau. Peut-être que si ce n'est pas trop grand, vous pouvez le poster ici i> afin qu'il ne subira pas de ruptures supplémentaires.
J'ai décidé de mordre et de dos Necro le fil. P>
Les deux boucles sont ignorées par le JVM comme no-ops. Si essentiellement, même l'une des boucles était jusqu'à 10 et l'autre jusqu'à 10000000, il n'y aurait eu aucune différence. p>
La boucle de retour à zéro est une autre chose (pour l'instruction JNN, mais encore une fois, il n'est pas compilé comme ça), le site lié est étrange (et faux). p>
Ce type de question ne correspond à aucun JVM (ni aucun autre compilateur pouvant optimiser). P>
Je fais des tests pendant environ 15 minutes maintenant, avec rien d'exécution autre que Eclipse au cas où, et j'ai vu une vraie différence, vous pouvez l'essayer.
Quand j'ai essayé de chronométrer combien de temps Java prend à Fais "rien" et il a fallu environ 500 nanosecondes juste pour avoir une idée. P>
Puis j'ai testé la durée pendant laquelle il faut exécuter un alors cinq minutes plus tard, j'ai essayé "en arrière": P> et j'ai une énorme différence (dans un petit niveau minuscule) de 16% entre le premier et Le second temps moyen pour exécuter l'instruction "croissante" Délai moyen pour exécuter la "décroissance" code utilisé pour de tels tests: strong> p> conclusion: strong>
La différence est fondamentalement rien, cela prend déjà une somme importante de "rien" pour faire "rien" par rapport au pour code> instruction où il augmente: p>
pour (i = 0; i <100; i ++) {} code> p>
pour (i = 100; i> 0; i -) code> p>
pour code> déclarations, ce dernier étant 16% plus rapide. p>
pour code> instruction de 2000: 1838 N / S EM> P>
pour code> instruction pendant 2000 tests: 1555 N / S EM> P >
pour les tests de relevé code>, faisant la différence entre eux négligeable, juste le temps pris pour importer un Une bibliothèque telle que java.util.scanner em> prend place davantage à charger que d'exécuter une instruction code> pour code>, il n'améliorera pas de manière significative la performance de votre application, mais c'est toujours vraiment cool de savoir. p> p>
Avez-vous réellement essayé de vérifier que la première version est effectivement plus rapide? Parce que je doute plutôt que c'est.
Certaines questions sont difficiles à lire et à comprendre à cause de la mauvaise qualité de l'anglais utilisé.
Manque de cette liste de questions d'entrevue: Après avoir répondu à tous ceux-ci, voulez-vous toujours travailler ici? I> Il n'y a qu'une seule réponse.
Beaucoup de questions sont trop simples pour une entrevue ou n'ont aucun sens.
Ces questions sont vraiment assez stupides et les réponses sont au mieux en erreur, mal au pire.
Belle page ... après 3 mauvaises réponses que j'ai lues assez ... "Char \ u0062 = 'B';" ne peut pas être valide du tout: "Char" peut être une classe valide mais comment l'affecter un caractère? et 'est le mauvais délimiteur, devrait être'. Sont "Public Main (Number Int) {}" et "Public statique final (String [] args) {}" Méthodes valides? Ils ne sont pas des méthodes du tout, le type de retour manquant, d'abord, un constructeur ne peut être qu'un constructeur.
@Konradr, pour une minute là-bas, je pensais que vous vouliez dire ce type de questions et ces réponses (celles-ci à ce sujet) et j'étais un peu offensé. Mais je réalise maintenant que vous vouliez dire le Q & comme sur le site lié. Il suffit de mettre cela en tant que commentaire au cas où quelqu'un d'autre vient un peu épais dans la tête, comme moi-même :-)
Wow, site Web hardcore. Il devrait être appelé "Java 1.0 pour les programmeurs de hardcore C".
Fait intéressant, le post relié dans la question n'existe plus. Peut-être que l'auteur a pris note de la taille de ce que c'était :-)