Какой из них будет выполняться быстрее, если (флаг == 0) или if (0 == флаг)?

Вопрос для интервью: какой из них будет выполняться быстрее, if (flag==0) или if (0==flag)? Почему?

Ответ 1

Я еще не видел правильного ответа (и уже есть некоторые): "Наваз указал на пользовательскую ловушку. И я сожалею о том, что я поспешно бросил upvote на" самый глупый вопрос", потому что кажется, что многие не поняли его, и это дает место для приятного обсуждения оптимизации компилятора:)

Ответ:

Что такое flag type?

В случае, когда flag фактически является определяемым пользователем типом. Тогда это зависит от того, какая перегрузка operator== выбрана. Конечно, может показаться глупым, что они не будут симметричными, но это, безусловно, разрешено, и я уже видел другие злоупотребления.

Если flag является встроенным, то оба должны иметь одинаковую скорость.

Из статьи в Википедии в x86, я бы поставил для инструкции Jxx для оператора if: возможно, JNZ (Перейти, если не ноль) или какой-то эквивалент.

Я бы сомневался, что компилятор пропустил такую ​​очевидную оптимизацию, даже если оптимизация отключена. Это тот тип вещей, для которого Оптимизация Peephole предназначена для.

РЕДАКТИРОВАТЬ: Сдвиг снова, поэтому добавьте некоторую сборку (LLVM 2.7 IR)

int regular(int c) {
  if (c == 0) { return 0; }
  return 1;
}

int yoda(int c) {
  if (0 == c) { return 0; }
  return 1;
}

define i32 @regular(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

define i32 @yoda(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

Даже если кто-то не знает, как читать ИК, я думаю, что он сам объясняет.

Ответ 2

В ваших версиях не будет никакой разницы.

Я предполагаю, что флаг type не является определяемым пользователем типом, а скорее некоторым встроенным типом. Enum - исключение!. Вы можете обращаться с перечислением, как будто он встроен. Фактически, это значения являются одним из встроенных типов!

В случае, если это пользовательский тип (кроме enum), тогда ответ полностью зависит от того, как вы перегрузили оператор ==. Обратите внимание, что вы должны перегрузить ==, задав две функции: по одной для каждой из ваших версий!

Ответ 3

Тот же код для amd64 с GCC 4.1.2:

        .loc 1 4 0  # int f = argc;
        movl    -20(%rbp), %eax
        movl    %eax, -4(%rbp)
        .loc 1 6 0 # if( f == 0 ) {
        cmpl    $0, -4(%rbp)
        jne     .L2
        .loc 1 7 0 # return 0;
        movl    $0, -36(%rbp)
        jmp     .L4
        .loc 1 8 0 # }
 .L2:
        .loc 1 10 0 # if( 0 == f ) {
        cmpl    $0, -4(%rbp)
        jne     .L5
        .loc 1 11 0 # return 1;
        movl    $1, -36(%rbp)
        jmp     .L4
        .loc 1 12 0 # }
 .L5:
        .loc 1 14 0 # return 2;
        movl    $2, -36(%rbp)
 .L4:
        movl    -36(%rbp), %eax
        .loc 1 15 0 # }
        leave
        ret

Ответ 4

Нет никакой разницы.

Вы можете получить очки в ответе на этот вопрос интервью, обратившись к устранению опечаток задания/сравнения, хотя:

if (flag = 0)  // typo here
   {
   // code never executes
   }

if (0 = flag) // typo and syntactic error -> compiler complains
   {
   // ...
   }

Хотя это правда, что, например, C-компилятор предупреждает в случае первого (flag = 0), таких предупреждений нет в PHP, Perl или Javascript или <insert language here>.

Ответ 5

Не будет никакой разницы по скорости. Почему это должно быть?

Ответ 6

Ну, есть разница, когда флаг является определяемым пользователем типом

struct sInt
{
    sInt( int i ) : wrappedInt(i)
    {
        std::cout << "ctor called" << std::endl;
    }

    operator int()
    {
        std::cout << "operator int()" << std::endl;
        return wrappedInt;
    }

    bool operator==(int nComp)
    {
        std::cout << "bool operator==(int nComp)" << std::endl;
        return (nComp == wrappedInt);
    }

    int wrappedInt;
};

int 
_tmain(int argc, _TCHAR* argv[])
{
    sInt s(0);

    //in this case this will probably be faster
    if ( 0 == s )
    {
        std::cout << "equal" << std::endl;
    }

    if ( s == 0 )
    {
        std::cout << "equal" << std::endl;
    }
}

В первом случае (0 == s) вызывается оператор преобразования, а затем возвращаемый результат сравнивается с 0. Во втором случае вызывается оператор ==.

Ответ 7

Когда сомневаешься, сравните его и узнайте правду.

Ответ 8

Они должны быть точно такими же с точки зрения скорости.

Обратите внимание, что некоторые люди используют, чтобы поместить константу слева в сопоставлениях равенства (так называемые "условия Yoda" ), чтобы избежать всех ошибок, которые могут возникнуть, если вы пишете = (оператор присваивания) вместо == (оператор сравнения равенства); поскольку присвоение литералу вызывает ошибку компиляции, такой ошибки избегают.

if(flag=0) // <--- typo: = instead of ==; flag is now set to 0
{
    // this is never executed
}

if(0=flag) // <--- compiler error, cannot assign value to literal
{

}

С другой стороны, большинство людей находят "условные условия Yoda" странными и раздражающими, особенно потому, что класс ошибок, которые они предотвращают, можно обнаружить также с помощью адекватных предупреждений компилятора.

if(flag=0) // <--- warning: assignment in conditional expression
{

}

Ответ 9

Как говорили другие, нет разницы.

0 должен быть оценен. flag необходимо оценить. Этот процесс занимает одно и то же время, независимо от того, на какой стороне они расположены.

Правильный ответ: они имеют одинаковую скорость.

Даже выражения if(flag==0) и if(0==flag) имеют одинаковое количество символов! Если один из них был написан как if(flag== 0), тогда у компилятора будет одно дополнительное пространство для синтаксического анализа, поэтому у вас будет законная причина указывать время компиляции.

Но так как такого не существует, нет абсолютно никакой причины, почему нужно быть быстрее других. Если есть причина, то компилятор делает очень, очень странные вещи для сгенерированного кода...

Ответ 10

Хорошо, я полностью согласен со всеми, сказанными в комментариях к OP, ради удовольствия:

Если компилятор недостаточно умен (действительно, вы не должны его использовать) или оптимизация отключена, x == 0 может скомпилировать инструкцию native jump if zero, тогда как 0 == x может быть более общим (и дорогостоящим ) сравнение числовых значений.

Тем не менее, я бы не хотел работать на босса, который думает в этих терминах...

Ответ 11

Какая скорость зависит от того, какую версию == вы используете. Здесь фрагмент, который использует 2 возможных реализации ==, и в зависимости от того, хотите ли вы вызывать x == 0 или 0 == x, выбран один из 2.

Если вы просто используете POD, это действительно не имеет значения, когда дело доходит до скорости.

#include <iostream>
using namespace std;

class x { 
  public:
  bool operator==(int x) { cout << "hello\n"; return 0; }
  friend bool operator==(int x, const x& a) { cout << "world\n"; return 0; } 
};

int main()
{ 
   x x1;
   //int m = 0;
   int k = (x1 == 0);
   int j = (0 == x1);
}

Ответ 12

Разумеется, нет никакой разницы в скорости выполнения. Условие должно быть оценено в обоих случаях одинаковым образом.

Ответ 13

Создайте две простые программы, используя предлагаемые способы.

Соберите коды. Посмотрите на сборку, и вы можете судить, но я сомневаюсь, что есть разница!

Интервью становятся все ниже, чем когда-либо.

Ответ 14

Я думаю, лучший ответ: "На каком языке этот пример?"

В вопросе не указан язык, и он помечен как "C", так и "С++". Точный ответ требует дополнительной информации.

Это пугающий вопрос программирования, но это может быть хорошим в коварном "пусть дать интервьюеру достаточно веревки, чтобы либо повеситься, либо построить дерево". Проблема с такими вопросами заключается в том, что они обычно записываются и передаются от интервьюера до интервьюера, пока не дойдут до людей, которые на самом деле не понимают его со всех сторон.

Ответ 15

Так же, как и в стороне (я действительно думаю, что любой достойный компилятор сделает этот вопрос спорным, так как он его оптимизирует), используя флаг 0 == над флагом == 0, предотвращает опечатку, где вы забыли одно из = (т.е. if вы случайно наберите флаг = 0, он будет компилироваться, но 0 = флаг не будет), который, я думаю, является ошибкой, которую каждый сделал в какой-то момент...

Ответ 16

Если вообще была разница, что заставляет компилятор быстрее выбирать один раз? Так логично, не может быть никакой разницы. Вероятно, это то, что ожидает интервьюер. Это действительно блестящий вопрос.