39 votes

x> -1 vs x> = 0, existe-t-il une différence de performance?

J'ai entendu un professeur de déposer cette fois, et elle a été mise sur écoute moi depuis. Disons que nous voulons vérifier si l'entier x est plus grand que ou égal à 0. Il y a deux façons de vérifier ceci:

if (x > -1){
    //do stuff
}

et

if (x >= 0){
    //do stuff
} 

Selon ce professeur, > serait légèrement plus rapide >=. Dans ce cas, c'était du Java, mais selon lui, cela s'applique aussi pour C, c++ et autres langages. Est-il quelque vérité dans cette déclaration?

31voto

Graham Borland Points 27556

Cela dépend beaucoup de l'architecture sous-jacente, mais toute différence sera minime.

Si tel était le cas, je m'attendrais à ce que (x >= 0) soit légèrement plus rapide, car la comparaison avec 0 est gratuite pour certains jeux d'instructions (tels que ARM).

Bien entendu, tout compilateur avisé choisira la meilleure implémentation, quelle que soit la variante de votre source.

30voto

Michael Burr Points 181287

Il n'y a pas de différence par rapport au monde réel sens.

Prenons un oeil à quelques-code généré par les différents compilateurs pour des cibles différentes.

  • Je suis en supposant un signé int fonctionnement (ce qui semble le but de l'OP)
  • J'ai limité par l'enquête à C et à compilateurs que j'ai à portée de main (certes un joli petit échantillon - GCC, MSVC et IAR)
  • de base les optimisations activées (-O2 pour GCC, /Ox de MSVC, -Oh pour IAR)
  • en utilisant le module suivant:

    void my_puts(char const* s);
    
    void cmp_gt(int x) 
    {
        if (x > -1) {
            my_puts("non-negative");
        }
        else {
            my_puts("negative");
        }
    }
    
    void cmp_gte(int x) 
    {
        if (x >= 0) {
            my_puts("non-negative");
        }
        else {
            my_puts("negative");
        }
    }
    

Et voici ce que chacun d'eux produit pour les opérations de comparaison:

MSVC 11 ciblant les BRAS:

// if (x > -1) {...
00000        |cmp_gt| PROC
  00000 f1b0 3fff    cmp         r0,#0xFFFFFFFF
  00004 dd05         ble         |$LN2@cmp_gt|


// if (x >= 0) {...
  00024      |cmp_gte| PROC
  00024 2800         cmp         r0,#0
  00026 db05         blt         |$LN2@cmp_gte|

MSVC 11 ciblage x64:

// if (x > -1) {...
cmp_gt  PROC
  00000 83 f9 ff     cmp     ecx, -1
  00003 48 8d 0d 00 00                  // speculative load of argument to my_puts()
    00 00        lea     rcx, OFFSET FLAT:$SG1359
  0000a 7f 07        jg  SHORT $LN5@cmp_gt

// if (x >= 0) {...
cmp_gte PROC
  00000 85 c9        test    ecx, ecx
  00002 48 8d 0d 00 00                  // speculative load of argument to my_puts()
    00 00        lea     rcx, OFFSET FLAT:$SG1367
  00009 79 07        jns     SHORT $LN5@cmp_gte

MSVC 11 ciblage x86:

// if (x > -1) {...
_cmp_gt PROC
  00000 83 7c 24 04 ff   cmp     DWORD PTR _x$[esp-4], -1
  00005 7e 0d        jle     SHORT $LN2@cmp_gt


// if (x >= 0) {...
_cmp_gte PROC
  00000 83 7c 24 04 00   cmp     DWORD PTR _x$[esp-4], 0
  00005 7c 0d        jl  SHORT $LN2@cmp_gte

GCC 4.6.1 ciblage x64

// if (x > -1) {...
cmp_gt:
    .seh_endprologue
    test    ecx, ecx
    js  .L2

// if (x >= 0) {...
cmp_gte:
    .seh_endprologue
    test    ecx, ecx
    js  .L5

GCC 4.6.1 ciblage x86:

// if (x > -1) {...
_cmp_gt:
    mov eax, DWORD PTR [esp+4]
    test    eax, eax
    js  L2

// if (x >= 0) {...
_cmp_gte:
    mov edx, DWORD PTR [esp+4]
    test    edx, edx
    js  L5

GCC 4.4.1 ciblant les BRAS:

// if (x > -1) {...
cmp_gt:
    .fnstart
.LFB0:
    cmp r0, #0
    blt .L8

// if (x >= 0) {...
cmp_gte:
    .fnstart
.LFB1:
    cmp r0, #0
    blt .L2

IAR 5.20 ciblant un ARM Cortex-M3:

// if (x > -1) {...
cmp_gt:
80B5 PUSH     {R7,LR}
.... LDR.N    R1,??DataTable1  ;; `?<Constant "non-negative">`
0028 CMP      R0,#+0
01D4 BMI.N    ??cmp_gt_0

// if (x >= 0) {...
cmp_gte:
 80B5 PUSH     {R7,LR}
 .... LDR.N    R1,??DataTable1  ;; `?<Constant "non-negative">`
 0028 CMP      R0,#+0
 01D4 BMI.N    ??cmp_gte_0

Si vous êtes toujours avec moi, voici les différences à noter entre l'évaluation (x > -1) et (x >= 0) qui s'affichent:

  • MSVC ciblage BRAS utilisations cmp r0,#0xFFFFFFFF pour (x > -1) vs cmp r0,#0 pour (x >= 0). La première instruction opcode est de deux octets plus. Je suppose que peuvent introduire un peu plus de temps, donc nous allons appeler cela comme un avantage pour l' (x >= 0)
  • MSVC ciblage x86 utilise cmp ecx, -1 pour (x > -1) vs test ecx, ecx pour (x >= 0). La première instruction opcode est un octet de plus. Je suppose que peuvent introduire un peu plus de temps, donc nous allons appeler cela comme un avantage pour l' (x >= 0)

Notez que GCC et IAR généré identiques en code machine pour les deux types de comparaison (avec l'exception possible de registre qui a été utilisé). Donc, d'après cette enquête, il apparaît que (x >= 0) a un très faible chance d'être "plus rapidement". Mais quel que soit l'avantage de la peu plus courte opcode de codage octet peut avoir (et je stress peut avoir) sera certainement complètement éclipsé par d'autres facteurs.

Je serais surpris si vous avez trouvé quelque chose de différent pour le jitted sortie de Java ou C#. Je doute que vous pouvez trouver une différence de note, même pour une très petite cible de 8 bits AVR.

En bref, ne vous inquiétez pas à propos de ce micro-optimisation. Je pense que mon écriture jusqu'ici a déjà passé plus de temps que sera passé par une différence dans les performances de ces expressions accumulées dans tous les Processeurs exécutent dans ma vie. Si vous avez la capacité à mesurer la différence de performance, veuillez vous adresser vos efforts pour quelque chose de plus important comme l'étude du comportement de particules sub-atomiques ou quelque chose.

20voto

dasblinkenlight Points 264350

Votre professeur a lu de très vieux livres. C’était le cas de certaines architectures dépourvues de l’instruction greater than or equal voulant qu’évaluer > nécessite moins de cycles de la machine que >= , mais ces plates-formes sont rares de nos jours. Je suggère d'aller pour la lisibilité, et en utilisant >= 0 .

14voto

Aram Kocharyan Points 8530

Une plus grande préoccupation ici est prématuré d'optimisation. Beaucoup considèrent l'écriture lisible code plus important que l'écriture efficace de code [1, 2]. Je voudrais appliquer ces optimisations comme une dernière étape d'un faible niveau de la bibliothèque une fois la conception a été prouvée à travailler.

Vous ne devriez pas être constamment envisage de faire une minuscule optimisations dans votre code au détriment de la lisibilité, car il le fera de la lecture et de l'maintaing le code plus difficile. Si ces optimisations à effectuer, abrégé en niveau inférieur fonctions de sorte que vous êtes toujours à gauche avec un code plus facile à lire pour les humains.

Comme une folle exemple, considérer que quelqu'un qui écrit de leurs programmes en assembleur pour quelqu'un qui est prêt à renoncer supplémentaire de l'efficacité et de l'utilisation de Java pour ses prestations dans la conception, la facilité d'utilisation et la facilité de maintenance.

Comme une note de côté, si vous êtes à l'aide de C, peut-être écrire une macro qui utilise le code légèrement plus efficace est plus possible de la solution, car il permettra d'atteindre l'efficacité, la lisibilité et la maintenabilité plus que dispersés opérations.

Et bien sûr, le compromis d'efficacité et de lisibilité dépend de votre application. Si cette boucle est en cours d'exécution 10 000 fois, une deuxième, puis c'est, éventuellement, un goulot d'étranglement et vous voudrez peut-être investir du temps dans l'optimisation, mais si c'est un seul état, que l'on appelle parfois ce n'est probablement pas la peine pour la minute de gain.

9voto

Evgeniy Dorofeev Points 52031

Oui, il y a une différence, vous devriez voir le bytecode.

pour

    if (x >= 0) {
    }

le bytecode est

    ILOAD 1
    IFLT L1

pour

if (x > -1) {
}

le bytecode est

ILOAD 1
ICONST_M1
IF_ICMPLE L3

la version 1 est plus rapide, car il utilise un zéro opérande de l'opération

iflt : jump if less than zero 

Mais il est possible de voir la différence seulement en cours d'exécution de la JVM en interpréter le mode d' java -Xint ..., par exemple, ce Test

    int n = 0;       
    for (;;) {
        long t0 = System.currentTimeMillis();
        int j = 0;
        for (int i = 100000000; i >= n; i--) {
            j++;
        }
        System.out.println(System.currentTimeMillis() - t0);
    }

montre 690 ms pour n = 0 et 760 ms pour n = 1. (J'ai utilisé 1 au lieu de -1, car il est plus facile de le démontrer, l'idée reste la même)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X