Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Наши голосования > == 0 ИЛИ !


Автор: B.V. 23.02.18, 14:10
В продолжение темы сложного выбора. Обращаю внимание, что речь снова о C++. Примеры, надеюсь, не потребуются

Автор: negram 23.02.18, 14:14
Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле.

Автор: amk 23.02.18, 15:04
Зависит от ситуации. Что понятнее, то и использую. Это не Python, издержек на сравнение с 0 нет

Автор: B.V. 23.02.18, 15:17
Цитата negram @
Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле.

Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения

Автор: KILLER 23.02.18, 15:17
Когда работаешь с числами - удобнее наверное на 0 проверять. Например делитель числа.

Автор: B.V. 23.02.18, 15:19
Цитата KILLER @
удобнее наверное на 0 проверять

Так наверное или удобнее? :) Сам-то что используешь и в каких ситуациях?

Автор: KILLER 23.02.18, 15:20
Цитата B.V. @
Так наверное или удобнее? :) Сам-то что используешь и в каких ситуациях?

Я голоснул за восклицательный знак. Но вообще делитель я бы проверял с помощью if a == 0 togda error, с указателями и прочим юзал бы if !pointer togda error

Автор: B.V. 23.02.18, 15:24
Цитата KILLER @
Но вообще делитель я бы проверял с помощью if a == 0 togda error

Что-то вспомнилась картинка
user posted image

Автор: KILLER 23.02.18, 15:25
Цитата B.V. @
Что-то вспомнилась картинка

Последний вариант прокатит разве что в скриптовых языках или в каком нибудь C# с Java, а в С++ выхватишь Access Violation.

Автор: B.V. 23.02.18, 15:35
Цитата KILLER @
а в С++ выхватишь Access Violation

Да нет, выхватишь UB.

Автор: KILLER 23.02.18, 15:40
Цитата B.V. @
Да нет, выхватишь UB.

Незнаю что там на счет UB. Может в новом стандарте UB будет, помню у Страуструпа вроде было написано что деление на 0 приведет к аппаратной ошибке процессора, и соответственно обрабатывать такую ошибку нужно с помощью какого нибудь SEH исключения. Стандартные исключения ловят только программные ошибки, и надеяться на то что catch отловит деление на 0 глупая затея.

Добавлено
Хотя помню компилятор майкрософт в дебаге ловит деление на ноль. В релизе хз.

Автор: Славян 23.02.18, 16:09
Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее!

Автор: KILLER 23.02.18, 16:24
Цитата Славян @
Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее!

Не представляю даже, как бы ты на явошарпах с питонами писал бы. Повесился наверное раньше :D

Автор: negram 23.02.18, 16:34
Цитата B.V. @
Цитата negram @
Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле.

Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения

Не было никогда такого выбора в плюсах. Выбор мог быть только у тех, кто воспринимает плюсы как "си с классами". благо, такие уже лет 10-15 как вымерли. :-?

Автор: amk 23.02.18, 21:28
Цитата Славян @
Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее!
Для сравнения с нулём CMP никогда не использовался. Даже в простейших компиляторах Small-C и Rat-C сравнение с нулём обрабатывалось отдельно от общего случая.
Так что тебе лапшу на уши вешали.

Автор: Славян 23.02.18, 21:43
Увы, но у меня и сейчас MSVS 2017 так жедает (CMP) без настроек ускорения/оптимизации. :yes-sad:

Автор: amk 23.02.18, 23:39
Ну, видимо так M$ понимает понятие оптимизации. Раз нет оптимизации, значит нельзя даже мелочи оптимизировать.

Автор: OpenGL 24.02.18, 08:58
(x == 0).toString().length() == 4 :crazy:

Добавлено
А вообще фигня холивар. Даёшь табы vs пробелы :yes:

Добавлено
Цитата Славян @
Увы, но у меня и сейчас MSVS 2017 так жедает (CMP) без настроек ускорения/оптимизации.

У тебя странная студия. У меня она даже в дебаге test-ом сравнивает. В релизе с оптимизациями тем более.

Автор: Da$aD 24.02.18, 10:16
Цитата OpenGL @
Даёшь табы vs пробелы

Так уже давно холивар закрыт.

http://intellindent.info/seriously/

Проблема только в том, что инструментарий (IDE, редакторы) не умеют в грамотный индент табами с выравниванием пробелами, поэтому приходится деградировать до all-spaces.

Автор: OpenGL 24.02.18, 11:38
Так нечестно. Я это должен был выложить это в качестве аргумента того, что сторонники пробелов не умеют пользоваться табами :(
Цитата Da$aD @
Проблема только в том, что инструментарий (IDE, редакторы) не умеют в грамотный индент табами с выравниванием пробелами, поэтому приходится деградировать до all-spaces.

Я для этого пользуюсь astyl-ом, автоматически запускаемым при сохранении файла. Он в абсолютном большинстве случаев отличает, когда надо использовать alignment, а когда - indentation - в целом меня вполне устраивает. Пример из статьи отформатировал он правильно.

Автор: B.V. 25.02.18, 19:10
Цитата OpenGL @
Даёшь табы vs пробелы

Слабо. Даже
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    () {
    }

vs
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ()
    {
    }

как-то, ИМХО, интереснее :)

Добавлено
Между тем, мнения в голосовалке разделились почти поровну. Не ожидал..

Автор: Qraizer 25.02.18, 22:52
Цитата B.V. @
мнения в голосовалке разделились почти поровну
Всё просто: каждой ситуации своё предпочтение. Если переменная хранит признак, то ! в самый раз, а если число, то == 0. Просто это не каждый сформулировать догадывается.

Автор: Bas 26.02.18, 11:42
Цитата Qraizer @
Если переменная хранит признак, то ! в самый раз, а если число, то == 0.

Если переменная объявлена но не определена?
Или в условии задачи она точно определена?

Добавлено
Цитата B.V. @
это выбор между двумя семантиками проверки нулевого значения

Все таки проверка числа.

Автор: leo 27.02.18, 06:18
Цитата Славян @
Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее!

Все эти "крайне" и "существенно" сильно преувеличены. Регистровые операции cmp r,i и test r,r исполняются одинаково быстро. Разница между ними в размере команды, что косвенно\теоретически может сказаться на задержке их декодирования, да и то только при при определенных "неблагоприятных обстоятельствах". Причем в х86 есть укороченный вариант команды сравнения cmp r/m,i8 с однобайтовой константой, автоматически расширяемой знаком до размера операнда. Соотв-но нормальный (не тупой) компилятор для сравнения с нулем должен использовать именно этот вариант (а не общий, когда размер константы совпадает с размером операнда). Зато cmp на современных x86 имеет преимущество перед test r,r при сравнении операнда в памяти (когда окружающий код не требует его загрузки в регистр), т.к. одна команда cmp m,i8 и декодируется, и все in-order стадии конвеера проходит быстрее, чем пара команд mov r,m + test r,r (хотя на практике разницы может и не быть, т.к. все зависит от окружающего кода, "ветра", "фаз луны" и т.д.).

Автор: korvin 02.03.18, 20:39
Цитата B.V. @
Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения

А почему не между двумя семантиками проверки булевого значения?

Автор: B.V. 03.03.18, 17:27
Цитата korvin @
А почему не между двумя семантиками проверки булевого значения?

Потому что думаю, что выбор был бы слишком предсказуем: подавляющее большинство выбрало бы вариант !. А вот с численными значениями интереснее

Автор: amk 04.03.18, 11:20
Цитата korvin @
А почему не между двумя семантиками проверки булевого значения?
Булевы значения вообще не принято сравнивать. Ещё в C, где вместо них использовались целые, сравнение признака с нулём считалось плохим стилем, если не ошибкой. А сравнение с TRUE (макрос, равный единице) было именно ошибкой.

Автор: Polinom2686 05.03.18, 05:08
Цитата B.V. @
Слабо. Даже

() {
}

vs

()
{
}

как-то, ИМХО, интереснее

Я выбираю первый вариант :)

Автор: JoeUser 05.03.18, 08:46
Для сравнения числовых значений - использую "==", для булевских просто проверка или проверка с отрицанием.
Для форматирования C/C++ кода все ж использую JavaStyle. Для меня он кажется более компактным и лучше читаемым.

Автор: Славян 05.03.18, 14:57
Цитата Polinom2686 @
Я выбираю первый вариант
А я - только второй; по-моему даже в 100% случаев. :yes:

Автор: B.V. 05.03.18, 15:31
Цитата Polinom2686 @
Я выбираю первый

Цитата Славян @
А я - только второй

Воу-воу, погодите до отдельной голосовалки :D

Автор: Славян 05.03.18, 16:10
Здесь пока - фифти-фифти; в той отдельной думаю, что Полином2pentPro будет с перевесом (по количеству виденых мною исходников...). :yes-sad:

Автор: Qraizer 05.03.18, 17:24
Вот и правильно, Славян. А то для }, вишьли, отдельная строка, потому что заканчивает блок, а { почему-то скромненько спаривается с заголовком, что к последующему блоку не имеет никакого отношения, кроме обоснования его наличия.

Автор: Славян 05.03.18, 17:56
Да, а я как бы согласен с вами, Qraizer, но всё равно мне чертовски жаль, что на фигурки уходит две строчки. Ничего тут не могу поделать! Горе, непреодолимое, и всё тут. :'( :'( :'(

Автор: JoeUser 05.03.18, 19:07
Цитата Славян @
Да, а я как бы согласен с вами, Qraizer, но всё равно мне чертовски жаль, что на фигурки уходит две строчки. Ничего тут не могу поделать! Горе, непреодолимое, и всё тут.

Не слушай Qraizer'а - переходи на светлую сторону Силы! :lol: Одну строчку можно сэкономить - используй Java Style.

Автор: Славян 05.03.18, 19:22
Сокрытие строки не есть светлая сторона, а токмо тёмная! :angry:

Автор: Da$aD 05.03.18, 21:14
Цитата Polinom2686 @
Я выбираю первый вариант


<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    func :: ArgType -> ReturnType
    func arg = statement


Problems? :crazy:

Автор: JoeUser 05.03.18, 23:09
Цитата Славян @
Сокрытие строки

не сокрытие - а экономия! не халявщик - а партнер! :lol:

Автор: Славян 06.03.18, 01:10
Единственным выходом из непреодолимой ситуации вижу только такой: если весь блок вычислений находится правее if'а (for'а, while'а, ...), то компилер бы его сам заключал в фигурные скобки! И экономия и красота, но... мечты?..

Автор: Polinom2686 06.03.18, 04:20
Цитата JoeUser @
И экономия и красота, но... мечты?..

Переходи на питон :)

Автор: wind 19.03.18, 18:03
терпеть не могу ! в начале
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    if (a ...)
    if (b ...)
    if (!c ...)

неровненько... <_<

Автор: Qraizer 19.03.18, 18:49
А я вот так пишу:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    if ( a ...)
    if ( b ...)
    if (!c ...)
А ещё
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    a = ...
    b =(...)
    c+= ...
итп.

Автор: Славян 19.03.18, 19:01
Так и не должно быть ровненько! Строки вида "if( a...)" несут суть опосля переменной (вида "if( a!=...)"), а строки же с отрицанием сразу утверждают нечто "if( !c )". Разная идеология! В первых вариантах "a" сравнится с чем-то/с какой-то субстанцией, а в отрицании ужо сказано, что всё тут двоичное грядёт. Так что - норм!! :yes: :yes:

Автор: applegame 26.03.18, 14:21
Цитата Славян @
Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее!

ЛОЛШТО?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    bool foo1(int a) {
        return a == 0;
    }
     
    bool foo2(int a) {
        return !a;
    }

компилируется в
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    foo1(int):
            test    edi, edi
            sete    al
            ret
    foo2(int):
            test    edi, edi
            sete    al
            ret

Пруф - https://godbolt.org/g/oKkN1v

Автор: Славян 26.03.18, 15:09
Да, я тоже только что проверил на типичном примере, и вижу, что оба случая (при настройках оптимизации в максимум) реализуются через test. Но это лишь говорит о том, что оптимизатор понял, что test (делает AND по сути ;) ) быстрее довольно туповатого и прямого CMP! Так что всё норм! :yes: :yes: :yes:

Добавлено
Более того, посмотрел на Release и Debug варианты и что видим:
Release: test reg32, reg32
Debug: cmp dword ptr [], 0
Итог: ! быстрее ==0 при их прямом переводе на маш. код! ч.т.д. :tong:

Автор: Jin X 27.03.18, 19:59
applegame, а стоило ли рассчитывать, что эти варианты будут различаться? :)

Вот так надо:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    for(;x;) return false; return true;
:D

Добавлено
Цитата Славян @
Но это лишь говорит о том, что оптимизатор понял, что test (делает AND по сути ) быстрее довольно туповатого и прямого CMP!
Это лишь говорит о том, что test короче, чем cmp. Скорость работы та же :)
Потому что будет cmp edi,0 (3 байта), а не cmp edi,edi (2 байта, как и test edi,edi).

Добавлено
При этом test edi,edi можно с тем же успехом заменить на and edi,edi или на or edi,edi.
Размер тот же, скорость та же.

Добавлено
У меня сейчас комп не пашет (загрузился с флешки). Кто может скомпилить и прислать дизасм такого кода (для 32-х бит)?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    return (!x || x==0x80000000);
Любопытно, как это оптимизируется.
Я поаплодирую, если там будет написано так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    add edi,edi
    sete al
(ну там вместо edi может быть что-то другое, разумеется)

Добавлено
Можно ещё так попробовать:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    return !(x&0x7FFFFFFF);

Автор: Qraizer 27.03.18, 23:52
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int foo(int x)
    {
      return (!x || x==0x80000000);
    }
     
    int bar(int x)
    {
      return !(x&0x7FFFFFFF);
    }
По скорости. VC2015:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _TEXT   SEGMENT
    _x$ = 8                                                 ; size = 4
    _bar    PROC                                            ; COMDAT
    ; File c:\disk_f\work\100olymp\q.c
    ; Line 8
            mov     eax, DWORD PTR _x$[esp-4]
            and     eax, 2147483647                         ; 7fffffffH
            neg     eax
            sbb     eax, eax
            inc     eax
    ; Line 9
            ret     0
    _bar    ENDP
    _TEXT   ENDS
    ; Function compile flags: /Ogtpy
    ;       COMDAT _foo
    _TEXT   SEGMENT
    _x$ = 8                                                 ; size = 4
    _foo    PROC                                            ; COMDAT
    ; File c:\disk_f\work\100olymp\q.c
    ; Line 3
            mov     eax, DWORD PTR _x$[esp-4]
            test    eax, eax
            je      SHORT $LN3@foo
            cmp     eax, -2147483648                        ; 80000000H
            je      SHORT $LN3@foo
            xor     eax, eax
    ; Line 4
            ret     0
    $LN3@foo:
    ; Line 3
            mov     eax, 1
    ; Line 4
            ret     0
    _foo    ENDP
    _TEXT   ENDS
ICL2016:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _TEXT   SEGMENT  PARA PUBLIC FLAT  'CODE'
           ALIGN     16
            PUBLIC _foo
    ; --- foo(int)
    _foo    PROC NEAR
    ; parameter 1: 4 + esp
    .B1.1:                          ; Preds .B1.0
    L1::
                                                               ;2.1
            mov       eax, DWORD PTR [4+esp]                        ;1.5
            test      eax, eax                                      ;3.12
            je        .B1.3         ; Prob 50%                      ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.2:                          ; Preds .B1.1
            cmp       eax, -2147483648                              ;3.20
            jne       .B1.4         ; Prob 50%                      ;3.20
                                    ; LOE ebx ebp esi edi
    .B1.3:                          ; Preds .B1.2 .B1.1
            mov       eax, 1                                        ;3.12
            ret                                                     ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.4:                          ; Preds .B1.2
            xor       eax, eax                                      ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.5:                          ; Preds .B1.4
            ret                                                     ;3.12
            ALIGN     16
                                    ; LOE
    ; mark_end;
    _foo ENDP
           ALIGN     16
            PUBLIC _bar
    ; --- bar(int)
    _bar    PROC NEAR
    ; parameter 1: 4 + esp
    .B2.1:                          ; Preds .B2.0
    L2::
                                                               ;7.1
            xor       eax, eax                                      ;8.14
            mov       edx, 1                                        ;8.14
            test      DWORD PTR [4+esp], 2147483647                 ;8.14
            cmove     eax, edx                                      ;8.14
            ret                                                     ;8.14
            ALIGN     16
                                    ; LOE
    ; mark_end;
    _bar ENDP
    ;_bar   ENDS
    _TEXT   ENDS
По размеру. VC2015:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    PUBLIC  _foo
    PUBLIC  _bar
    ; Function compile flags: /Ogspy
    ;       COMDAT _bar
    _TEXT   SEGMENT
    _x$ = 8                                                 ; size = 4
    _bar    PROC                                            ; COMDAT
    ; File c:\disk_f\work\100olymp\q.c
    ; Line 8
            mov     eax, DWORD PTR _x$[esp-4]
            and     eax, 2147483647                         ; 7fffffffH
            neg     eax
            sbb     eax, eax
            inc     eax
    ; Line 9
            ret     0
    _bar    ENDP
    _TEXT   ENDS
    ; Function compile flags: /Ogspy
    ;       COMDAT _foo
    _TEXT   SEGMENT
    _x$ = 8                                                 ; size = 4
    _foo    PROC                                            ; COMDAT
    ; File c:\disk_f\work\100olymp\q.c
    ; Line 3
            cmp     DWORD PTR _x$[esp-4], 0
            je      SHORT $LN3@foo
            cmp     DWORD PTR _x$[esp-4], -2147483648       ; 80000000H
            je      SHORT $LN3@foo
            xor     eax, eax
    ; Line 4
            ret     0
    $LN3@foo:
    ; Line 3
            xor     eax, eax
            inc     eax
    ; Line 4
            ret     0
    _foo    ENDP
    _TEXT   ENDS
ICL2016:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _TEXT   SEGMENT  DWORD PUBLIC FLAT  'CODE'
            PUBLIC _foo
    ; --- foo(int)
    _foo    PROC NEAR
    ; parameter 1: 4 + esp
    .B1.1:                          ; Preds .B1.0
    L1::
                                                               ;2.1
            mov       eax, DWORD PTR [4+esp]                        ;1.5
            test      eax, eax                                      ;3.12
            je        .B1.3         ; Prob 50%                      ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.2:                          ; Preds .B1.1
            cmp       eax, -2147483648                              ;3.20
            jne       .B1.4         ; Prob 50%                      ;3.20
                                    ; LOE ebx ebp esi edi
    .B1.3:                          ; Preds .B1.2 .B1.1
            push      1                                             ;3.12
            pop       eax                                           ;3.12
            ret                                                     ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.4:                          ; Preds .B1.2
            xor       eax, eax                                      ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.5:                          ; Preds .B1.4
            ret                                                     ;3.12
                                    ; LOE
    ; mark_end;
    _foo ENDP
            PUBLIC _bar
    ; --- bar(int)
    _bar    PROC NEAR
    ; parameter 1: 4 + esp
    .B2.1:                          ; Preds .B2.0
    L2::
                                                               ;7.1
            xor       eax, eax                                      ;8.14
            test      DWORD PTR [4+esp], 2147483647                 ;8.14
            sete      al                                            ;8.14
            ret                                                     ;8.14
                                    ; LOE
    ; mark_end;
    _bar ENDP
    ;_bar   ENDS
    _TEXT   ENDS
64 бита приводить?

Автор: Jin X 28.03.18, 10:54
Цитата Qraizer @
64 бита приводить?
Не надо, спасибо.

Цитата Qraizer @
        test      DWORD PTR [4+esp], 2147483647                 ;8.14
        sete      al  
Вот это уже нормально :good:

Но Intel что-то гонит. Оптимизация по размеру (приведённый чуть выше код) должна быть быстрее, ибо cmovcc медленнее, чем setcc.

Интересен вариант с icl в случае, если x будет не в стеке, а в регистре...

Автор: Qraizer 28.03.18, 12:35
Медленнее, на такт. Но она может быть спараллелена посредством задействования обоих значений результата, тогда как setcc не может.

Добавлено
Цитата Jin X @
Интересен вариант с icl в случае, если x будет не в стеке, а в регистре...
Ок, fastcall. По скорости:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _TEXT   SEGMENT  PARA PUBLIC FLAT  'CODE'
           ALIGN     16
            PUBLIC @foo@4
    ; --- foo(int)
    @foo@4  PROC NEAR
    ; parameter 1: ecx
    .B1.1:                          ; Preds .B1.0
    L1::
                                                               ;2.1
            test      ecx, ecx                                      ;3.12
            je        .B1.3         ; Prob 50%                      ;3.12
                                    ; LOE ecx ebx ebp esi edi
    .B1.2:                          ; Preds .B1.1
            cmp       ecx, -2147483648                              ;3.20
            jne       .B1.4         ; Prob 50%                      ;3.20
                                    ; LOE ebx ebp esi edi
    .B1.3:                          ; Preds .B1.2 .B1.1
            mov       eax, 1                                        ;3.12
            ret                                                     ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.4:                          ; Preds .B1.2
            xor       eax, eax                                      ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.5:                          ; Preds .B1.4
            ret                                                     ;3.12
            ALIGN     16
                                    ; LOE
    ; mark_end;
    @foo@4 ENDP
           ALIGN     16
            PUBLIC @bar@4
    ; --- bar(int)
    @bar@4  PROC NEAR
    ; parameter 1: ecx
    .B2.1:                          ; Preds .B2.0
    L2::
                                                               ;7.1
            xor       eax, eax                                      ;8.14
            mov       edx, 1                                        ;8.14
            test      ecx, 2147483647                               ;8.14
            cmove     eax, edx                                      ;8.14
            ret                                                     ;8.14
            ALIGN     16
                                    ; LOE
    ; mark_end;
    @bar@4 ENDP
    ;@bar@4 ENDS
    _TEXT   ENDS
По размеру:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _TEXT   SEGMENT  DWORD PUBLIC FLAT  'CODE'
            PUBLIC @foo@4
    ; --- foo(int)
    @foo@4  PROC NEAR
    ; parameter 1: ecx
    .B1.1:                          ; Preds .B1.0
    L1::
                                                               ;2.1
            test      ecx, ecx                                      ;3.12
            je        .B1.3         ; Prob 50%                      ;3.12
                                    ; LOE ecx ebx ebp esi edi
    .B1.2:                          ; Preds .B1.1
            cmp       ecx, -2147483648                              ;3.20
            jne       .B1.4         ; Prob 50%                      ;3.20
                                    ; LOE ebx ebp esi edi
    .B1.3:                          ; Preds .B1.2 .B1.1
            push      1                                             ;3.12
            pop       eax                                           ;3.12
            ret                                                     ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.4:                          ; Preds .B1.2
            xor       eax, eax                                      ;3.12
                                    ; LOE eax ebx ebp esi edi
    .B1.5:                          ; Preds .B1.4
            ret                                                     ;3.12
                                    ; LOE
    ; mark_end;
    @foo@4 ENDP
            PUBLIC @bar@4
    ; --- bar(int)
    @bar@4  PROC NEAR
    ; parameter 1: ecx
    .B2.1:                          ; Preds .B2.0
    L2::
                                                               ;7.1
            test      ecx, 2147483647                               ;8.14
            push      0                                             ;8.14
            pop       eax                                           ;8.14
            sete      al                                            ;8.14
            ret                                                     ;8.14
                                    ; LOE
    ; mark_end;
    @bar@4 ENDP
    ;@bar@4 ENDS
    _TEXT   ENDS

Автор: Jin X 28.03.18, 12:47
Qraizer, с чем она может быть спараллелена, когда её работа зависит от результата предыдущей операции (всех 3-х предыдущих инструкций, я даже скажу)?

gcc, кстати, срабатывает как xor+test+sete (xor+and+sete для регистра) при оптимизации по скорости для обоих вариантов кода.
А вот просто !x или x==0 он компилит аж в:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    mov edx,[esp+4]
    xor eax,eax
    test edx,edx
    sete al


Добавлено
Цитата Qraizer @
        push      1
        pop       eax  
Жесть какая!
Тогда бы уж сделали
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    xor eax,eax
    inc eax
Размер тот же, но хоть быстрее должно быть...

Добавлено
Цитата Jin X @
xor+test+sete
Зачем тут xor eax,eax, когда я объявляю тип функции bool? Он же байтовый!

Автор: Qraizer 28.03.18, 12:55
Цитата Jin X @
с чем она может быть спараллелена, когда её работа зависит от результата предыдущей операции
С последующими действиями. Может использоваться либо eax, либо edx, но какой именно, станет известно лишь позднее. Временно можно использовать два потока исполнения, один из которых будет впоследствии отброшен, а второй, использующий "правильный" risc-регистр, переотображён на eax. То же можно сделать с setcc, также задействуя два risc-регистра, однако тут больше сложностей с подготовкой значения для второго. В отличие от cmovcc, где оба значения уже подготовлены и лежат в регистрах, для setcc значения 0 и 1, причём чётко байтовые, нужно подготавливать явно. Вероятно, по этой причине микрокод для них может отличаться.

Добавлено
Цитата Jin X @
Жесть какая!
Соседние push/pop не имеют штрафов по зависимостям. Они спецом для этого оптимизированы. Пара xor/inс зависит от ALU

Добавлено
Цитата Jin X @
Зачем тут xor eax,eax, когда я объявляю тип функции bool? Он же байтовый!
Ну я же привёл прототипы. Там int. Если хочешь C++, где есть честный bool, то это будут другие прототипы.

Добавлено
Проверил на intel, никакой разницы. Он всегда предпочитает работать с полноразмерными регистрами, ибо так куда эффективнее работает микрокод. (Вообще, даже самые древние мануалы советуют никогда не смешивать разноразмерные данные в зависимых инструкциях.) А вот visual немного удивил:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    _TEXT   SEGMENT
    ?bar@@YI_NH@Z PROC                                      ; bar, COMDAT
    ; _x$ = ecx
    ; File c:\users\shmatko\documents\intel\документация\qq.cpp
    ; Line 8
            test    ecx, 2147483647                         ; 7fffffffH
            sete    al
    ; Line 9
            ret     0
    ?bar@@YI_NH@Z ENDP                                      ; bar
    _TEXT   ENDS
    ; Function compile flags: /Ogtpy
    ;       COMDAT ?foo@@YI_NH@Z
    _TEXT   SEGMENT
    ?foo@@YI_NH@Z PROC                                      ; foo, COMDAT
    ; _x$ = ecx
    ; File c:\users\shmatko\documents\intel\документация\qq.cpp
    ; Line 3
            test    ecx, ecx
            je      SHORT $LN3@foo
            cmp     ecx, -2147483648                        ; 80000000H
            je      SHORT $LN3@foo
            xor     al, al
    ; Line 4
            ret     0
    $LN3@foo:
    ; Line 3
            mov     al, 1
    ; Line 4
            ret     0
    ?foo@@YI_NH@Z ENDP                                      ; foo
    _TEXT   ENDS
Это по скорости.

Добавлено
Тока эта... сегодня VC2017.

Добавлено
P.S.
Цитата Qraizer @
Временно можно использовать два потока исполнения...
Забыл добавить, что это лишь мои рассуждения, почему такое может иметь место. Как оно на самом деле, без понятия.

Автор: Jin X 28.03.18, 21:52
Цитата Qraizer @
Соседние push/pop не имеют штрафов по зависимостям.
Так, дело не в зависимости, а в использовании стека (то бишь памяти, а это время).

Цитата Qraizer @
Ну я же привёл прототипы. Там int. Если хочешь C++, где есть честный bool, то это будут другие прототипы.
Я заменил int на bool и откомпилил gcc, но результат всё равно почему-то пишется в eax.

Цитата Qraizer @
Вообще, даже самые древние мануалы советуют никогда не смешивать разноразмерные данные в зависимых инструкциях
Не спорю (кстати, это может быть причиной использования cmov), просто здесь всё равно используется sete, поэтому xor eax,eax ничего ускорить не должен, по идее...
Возможно, просто компилер устроен так, что оперирует только полными регистрами. Вот, кстати, надо посмотреть как он будет char возвращать - как байт или дворд. Intel и gcc.

Автор: Qraizer 29.03.18, 02:46
Цитата Jin X @
Так, дело не в зависимости, а в использовании стека (то бишь памяти, а это время).
Ничего страшного. Ляжет в L1 кэша, ибо вершина стека да в кэш.промахе... когда такое бывало-то, оттуда же прочтётся, а в память запишется в фоне при отбросе кеш.строк, да и то не факт, что не перезапишется к тому времени мульён раз другими данными.

Автор: Jin X 29.03.18, 14:05
Цитата Qraizer @
Ничего страшного. Ляжет в L1 кэша
Ляжет, но обращение к кэшу тоже требует времени. Кэш (даже L1) - это же не регистры.

Автор: Qraizer 29.03.18, 14:14
Так это не отнимет тактов. Ещё в i486 была очередь отложенных записей, которые не тормозили ALU, в отличие от чтений, а, если не ошибаюсь, в P6 появилась фича вытаскивать эти данные прям из очереди, т.е. даже без обращения к L1. Поспрашивай leo, он больше об этом всём знает.

Автор: Jin X 29.03.18, 15:35
По поводу cmov'ов Агнер пишет так:
Цитата
The advantage of a conditional move is that it avoids branch mispredictions. But it has the disadvantage that it increases the length of a dependency chain, while a predicted branch breaks the dependency chain. If the code in example 9.9c is part of a dependency chain then the cmov instruction adds to the length of the chain. The latency of cmov is two clock cycles on Intel processors, and one clock cycle on AMD processors.
9.9c - это
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
     ; Example 9.9c. Branch implemented with conditional move
     mov eax, [b]
     cmp eax, [c]
     mov eax, [d]
     cmovng eax, [e]
p.s. Про цепочку зависимостей, в общем-то, оно и так понятно.

И вот ещё:
Цитата
If a conditional jump is used for setting a Boolean variable to 0 or 1 then it is often more efficient to use the conditional set instruction.
Здесь он, конечно, сравнивает setcc с jcc, но всё же не про movcc не пишет, а именно про setcc.

Цитата Qraizer @
в P6 появилась фича вытаскивать эти данные прям из очереди, т.е. даже без обращения к L1
Может быть, конечно.
Но я могу лишь судить по таблице скоростей и задержек: http://agner.org/optimize/instruction_tables.pdf
Ну и мои тесты показывают, что push 1+pop eax работает медленнее, чем xor eax,eax+inc eax, хотя я понимаю, что при других сочетаниях результаты могут быть другими.
Но в любом случае вряд ли push/pop будет когда-либо быстрее, чем xor/inc.

Добавлено
Собственно, хочется понять: это продуманный умысел Intel (и тогда в чём он?) или недочёт (что было бы странно для производителя процессоров)?

Добавлено
Цитата Qraizer @
Поспрашивай leo, он больше об этом всём знает.
Я его, пожалуй, приглашу в эту тему :)

Автор: Qraizer 29.03.18, 17:17
Цитата Jin X @
Собственно, хочется понять: это продуманный умысел Intel (и тогда в чём он?) или недочёт (что было бы странно для производителя процессоров)?
Думаю, никаких теорий заговоров. У него просто мало информации. Если использовать эти функции в контексте, да ещё и инлайнить, код может отличаться от голых __fastcall функций разительно.

Автор: amk 30.03.18, 21:26
Цитата Jin X @
Ну и мои тесты показывают, что push 1+pop eax работает медленнее, чем xor eax,eax+inc eax, хотя я понимаю, что при других сочетаниях результаты могут быть другими.
Но в любом случае вряд ли push/pop будет когда-либо быстрее, чем xor/inc.
На AMD, судя по таймингам, одинаково, только комбинация push+pop немного длиннее. на intel push+pop выполняется дольше.
А непосредственная засылка единицы в регистр на обоих процессорах будет работать ещё быстрее.

Автор: Славян 31.03.18, 04:38
Я вполне допускаю, что засылка значения быстрее и всё такое, но всё же идеологически вижу это медленнее/неправильнее по таким причинам: засылка значения - это как привоз некими 32-мя фурами издалека значения 0 или 1 (распараллелили), а XOR - это работат тут, на месте, недалёкая=простая в геометрико-транзисторном виде, тоже параллельная 32 раза, - должна быть существенно быстрее! Будет ещё, конечно, INC, но и он ничего извне не просит, а может и тут, на месте, молотить. Так что... :yes-sad:

Добавлено
Цитата Jin X @
При этом test edi,edi можно с тем же успехом заменить на and edi,edi или на or edi,edi.
Размер тот же, скорость та же.
Это они практически такие же, но идеологически test не меняет регистр, а лишь "проверяет" (and "в уме") биты, посему ДОЛЖЕН быть быстрее. :yes-sad:

Автор: JoeUser 31.03.18, 04:51
Цитата amk @
посему ДОЛЖЕН быть быстрее.

Лично я, чтобы так плотно, на асме не прогал лет 20 точно. Но одно - помню, нет такого понятия "ДОЛЖЕН". Есть документация.

Автор: Славян 31.03.18, 05:26
Я имел ввиду схемотехническую реализацию, аппаратную. И цитата та не от amk. ;)

Автор: amk 31.03.18, 06:12
Цитата Славян @
засылка значения - это как привоз некими 32-мя фурами издалека значения 0 или 1 (распараллелили)
Это не завоз издалека. Это когда ты приходишь куда-то, а нужное значение у тебя тут-же с собой, в кармане, и не надо никого за ним куда-то посылать, ни из подручных средств на месте готовить.

Автор: Славян 31.03.18, 06:35
Э, погодьте-ка! Регистр всё же кусок памяти процессора, такие-то ячейки на подложке, элементы микросхемы. Поэтому ничего в кармане у них вечно нет, - всё откуда-то завозится. И непосредственное значение, даже бит его, всё равно придёт=принесётся! А вот нанофабрика по созданию операции XOR вполне может быть тут же, в шаге; так что мне мыслится это быстрее. Хоть и соглашусь, что практическая реализация может статься и наоборот сделана. :yes-sad:

Автор: amk 31.03.18, 06:52
Непосредственные данные (в диапазоне от -128 до 127 это всего 1 байт) поступают на исполнение одновременно с командой, так что никаких дополнительных задержек они в принципе создать не могут. А вот для команды XOR приходится задействовать ALU - пересылать данные туда-сюда. Хорошо, что требуется на это всего один такт. Но потом-то приходится ещё и инкремент делать. А это отдельная команда, вдобавок завязанная на результат предыдущей, запараллелить её не получится.

Автор: Славян 31.03.18, 07:55
Ну я вижу работу декодера как-то так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    if( XOR )
    {
      switch( операнды )
      {
        case reg32s:
            if( reg32a vs reg32a ) обнулить!( reg32a );
            else ФабрикаXOR(...);
            ...
        case память:
            ВытащитьДанные();
            ФабрикаXOR(...);
            ...
        case ...
      }
    }
А команды mov reg32, data8 всё равно ж нет, так что приходится зачитывать аж 32 бита, что долго и много.

Автор: Jin X 31.03.18, 17:47
Цитата amk @
На AMD, судя по таймингам, одинаково, только комбинация push+pop немного длиннее
Почему длиннее? push 1 = 2 байта, pop eax = 1 байт, итого 3.
xor eax,eax = 2 байта, inc eax = 1 байт (на 32-х битах, не 64), итого тоже 3.

Цитата amk @
А непосредственная засылка единицы в регистр на обоих процессорах будет работать ещё быстрее.
Это понятно, но тут оптимизации по размеру.

Цитата Славян @
Это они практически такие же, но идеологически test не меняет регистр, а лишь "проверяет" (and "в уме") биты, посему ДОЛЖЕН быть быстрее.
OMG, в каком "уме"? "В уме" может так же означать (в теории), что его нужно куда-то скопировать и там сделать and. Это как вариант. Как там в микрокоде конкретно реализовано - не знаю. Но это всё демагогия. А надо смотреть на таблицы, которые говорят, что на Nehalem, к примеру, mov, xor, test, or, and, add, sub, inc и dec (для операция reg,reg или reg,imm) имеют одни и те же характеристики: используемые порты, кол-во микроопераций и latency. К тому же, xor с inc не могут параллелиться даже чисто теоретически, т.к. inc зависим от результата предыдущей операции - xor.
Ещё в качестве примера: inc eax по твоей логике должен быть быстрее, чем add eax,1, однако на Pentium 4 он работает медленнее, т.к. не меняет значения флага CF, что требует дополнительной микрооперации.
Ещё "в теории" loop должен работать быстрее, чем dec eax + jnz, но вот на практике всё иначе (ещё со времён царя Гороха)...

Цитата Славян @
Ну я вижу работу декодера как-то так
Видь. А Intel видим её по-своему :tong:

Автор: Славян 31.03.18, 18:24
Цитата Jin X @
Ещё в качестве примера: inc eax по твоей логике должен быть быстрее, чем add eax,1, однако на Pentium 4 он работает медленнее, т.к. не меняет значения флага CF, что требует дополнительной микрооперации.
Ну во многих древних книжках по ASM'у так и писалось, что INC - короткая и быстрая, а не то что ADD. Что там поменяли существенно с годами - неясно, но всё же думается, что первые инженеры процессоров не зря сделали такую команду отдельно, - нутром чуяли её специфику, а потому и ускорение. :yes: :yes:

Автор: Jin X 31.03.18, 18:34
Славян, напомню, что раньше память ОЗУ была в дифиците и измерялась килобайтами, а не гигабайтами, поэтому вполне логично было иметь 1-байтовые инструкции как альтернативу 3-байтовым - это раз.
Ну и да, во времена 8086 процессора add (reg,imm) выполнялась за 4 такта, а inc - за 3. Но с тех пор воды утекло целое озеро Байкал...

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)