Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.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 23.02.18, 15:20 |
Я голоснул за восклицательный знак. Но вообще делитель я бы проверял с помощью if a == 0 togda error, с указателями и прочим юзал бы if !pointer togda error |
Автор: B.V. 23.02.18, 15:24 |
Что-то вспомнилась картинка |
Автор: KILLER 23.02.18, 15:25 |
Последний вариант прокатит разве что в скриптовых языках или в каком нибудь C# с Java, а в С++ выхватишь Access Violation. |
Автор: B.V. 23.02.18, 15:35 |
Да нет, выхватишь UB. |
Автор: KILLER 23.02.18, 15:40 |
Незнаю что там на счет UB. Может в новом стандарте UB будет, помню у Страуструпа вроде было написано что деление на 0 приведет к аппаратной ошибке процессора, и соответственно обрабатывать такую ошибку нужно с помощью какого нибудь SEH исключения. Стандартные исключения ловят только программные ошибки, и надеяться на то что catch отловит деление на 0 глупая затея. Добавлено Хотя помню компилятор майкрософт в дебаге ловит деление на ноль. В релизе хз. |
Автор: Славян 23.02.18, 16:09 |
Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее! |
Автор: KILLER 23.02.18, 16:24 |
Цитата Славян @ Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее! Не представляю даже, как бы ты на явошарпах с питонами писал бы. Повесился наверное раньше |
Автор: negram 23.02.18, 16:34 |
Цитата B.V. @ Цитата negram @ Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле. Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения Не было никогда такого выбора в плюсах. Выбор мог быть только у тех, кто воспринимает плюсы как "си с классами". благо, такие уже лет 10-15 как вымерли. |
Автор: amk 23.02.18, 21:28 |
Цитата Славян @ Для сравнения с нулём CMP никогда не использовался. Даже в простейших компиляторах Small-C и Rat-C сравнение с нулём обрабатывалось отдельно от общего случая.Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее! Так что тебе лапшу на уши вешали. |
Автор: Славян 23.02.18, 21:43 |
Увы, но у меня и сейчас MSVS 2017 так жедает (CMP) без настроек ускорения/оптимизации. |
Автор: amk 23.02.18, 23:39 |
Ну, видимо так M$ понимает понятие оптимизации. Раз нет оптимизации, значит нельзя даже мелочи оптимизировать. |
Автор: OpenGL 24.02.18, 08:58 |
(x == 0).toString().length() == 4 Добавлено А вообще фигня холивар. Даёшь табы vs пробелы Добавлено Цитата Славян @ Увы, но у меня и сейчас MSVS 2017 так жедает (CMP) без настроек ускорения/оптимизации. У тебя странная студия. У меня она даже в дебаге test-ом сравнивает. В релизе с оптимизациями тем более. |
Автор: Da$aD 24.02.18, 10:16 |
Так уже давно холивар закрыт. 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 |
Слабо. Даже <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> () { } vs <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> () { } как-то, ИМХО, интереснее Добавлено Между тем, мнения в голосовалке разделились почти поровну. Не ожидал.. |
Автор: Qraizer 25.02.18, 22:52 |
Всё просто: каждой ситуации своё предпочтение. Если переменная хранит признак, то ! в самый раз, а если число, то == 0. Просто это не каждый сформулировать догадывается. |
Автор: Bas 26.02.18, 11:42 |
Если переменная объявлена но не определена? Или в условии задачи она точно определена? Добавлено Все таки проверка числа. |
Автор: 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 |
Потому что думаю, что выбор был бы слишком предсказуем: подавляющее большинство выбрало бы вариант !. А вот с численными значениями интереснее |
Автор: amk 04.03.18, 11:20 |
Булевы значения вообще не принято сравнивать. Ещё в C, где вместо них использовались целые, сравнение признака с нулём считалось плохим стилем, если не ошибкой. А сравнение с TRUE (макрос, равный единице) было именно ошибкой. |
Автор: Polinom2686 05.03.18, 05:08 |
Я выбираю первый вариант |
Автор: JoeUser 05.03.18, 08:46 |
Для сравнения числовых значений - использую "==", для булевских просто проверка или проверка с отрицанием. Для форматирования C/C++ кода все ж использую JavaStyle. Для меня он кажется более компактным и лучше читаемым. |
Автор: Славян 05.03.18, 14:57 |
А я - только второй; по-моему даже в 100% случаев. |
Автор: B.V. 05.03.18, 15:31 |
Воу-воу, погодите до отдельной голосовалки |
Автор: Славян 05.03.18, 16:10 |
Здесь пока - фифти-фифти; в той отдельной думаю, что Полином2pentPro будет с перевесом (по количеству виденых мною исходников...). |
Автор: Qraizer 05.03.18, 17:24 |
Вот и правильно, Славян. А то для }, вишьли, отдельная строка, потому что заканчивает блок, а { почему-то скромненько спаривается с заголовком, что к последующему блоку не имеет никакого отношения, кроме обоснования его наличия. |
Автор: Славян 05.03.18, 17:56 |
Да, а я как бы согласен с вами, Qraizer, но всё равно мне чертовски жаль, что на фигурки уходит две строчки. Ничего тут не могу поделать! Горе, непреодолимое, и всё тут. |
Автор: JoeUser 05.03.18, 19:07 |
Цитата Славян @ Да, а я как бы согласен с вами, Qraizer, но всё равно мне чертовски жаль, что на фигурки уходит две строчки. Ничего тут не могу поделать! Горе, непреодолимое, и всё тут. Не слушай Qraizer'а - переходи на светлую сторону Силы! Одну строчку можно сэкономить - используй Java Style. |
Автор: Славян 05.03.18, 19:22 |
Сокрытие строки не есть светлая сторона, а токмо тёмная! |
Автор: Da$aD 05.03.18, 21:14 |
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> func :: ArgType -> ReturnType func arg = statement Problems? |
Автор: JoeUser 05.03.18, 23:09 |
не сокрытие - а экономия! |
Автор: Славян 06.03.18, 01:10 |
Единственным выходом из непреодолимой ситуации вижу только такой: если весь блок вычислений находится правее if'а (for'а, while'а, ...), то компилер бы его сам заключал в фигурные скобки! И экономия и красота, но... мечты?.. |
Автор: Polinom2686 06.03.18, 04:20 |
Переходи на питон |
Автор: 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" сравнится с чем-то/с какой-то субстанцией, а в отрицании ужо сказано, что всё тут двоичное грядёт. Так что - норм!! |
Автор: 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! Так что всё норм! Добавлено Более того, посмотрел на Release и Debug варианты и что видим: Release: test reg32, reg32 Debug: cmp dword ptr [], 0 Итог: ! быстрее ==0 при их прямом переводе на маш. код! ч.т.д. |
Автор: Jin X 27.03.18, 19:59 |
applegame, а стоило ли рассчитывать, что эти варианты будут различаться? Вот так надо: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> for(;x;) return false; return true; Добавлено Цитата Славян @ Это лишь говорит о том, что test короче, чем cmp. Скорость работы та же Но это лишь говорит о том, что оптимизатор понял, что test (делает AND по сути ) быстрее довольно туповатого и прямого 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}> (ну там вместо edi может быть что-то другое, разумеется) add edi,edi sete al Добавлено Можно ещё так попробовать: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> return !(x&0x7FFFFFFF); |
Автор: Qraizer 27.03.18, 23:52 |
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> По скорости. VC2015:int foo(int x) { return (!x || x==0x80000000); } int bar(int x) { return !(x&0x7FFFFFFF); } <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> ICL2016:_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 <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> По размеру. VC2015:_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 <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> ICL2016: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 <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> 64 бита приводить? _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 |
Автор: Jin X 28.03.18, 10:54 |
Не надо, спасибо. Вот это уже нормально Но Intel что-то гонит. Оптимизация по размеру (приведённый чуть выше код) должна быть быстрее, ибо cmovcc медленнее, чем setcc. Интересен вариант с icl в случае, если x будет не в стеке, а в регистре... |
Автор: Qraizer 28.03.18, 12:35 |
Медленнее, на такт. Но она может быть спараллелена посредством задействования обоих значений результата, тогда как setcc не может. Добавлено Ок, 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 Добавлено Жесть какая! Тогда бы уж сделали <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Размер тот же, но хоть быстрее должно быть... xor eax,eax inc eax Добавлено Зачем тут xor eax,eax, когда я объявляю тип функции bool? Он же байтовый! |
Автор: Qraizer 28.03.18, 12:55 |
Цитата Jin X @ С последующими действиями. Может использоваться либо eax, либо edx, но какой именно, станет известно лишь позднее. Временно можно использовать два потока исполнения, один из которых будет впоследствии отброшен, а второй, использующий "правильный" risc-регистр, переотображён на eax. То же можно сделать с setcc, также задействуя два risc-регистра, однако тут больше сложностей с подготовкой значения для второго. В отличие от cmovcc, где оба значения уже подготовлены и лежат в регистрах, для setcc значения 0 и 1, причём чётко байтовые, нужно подготавливать явно. Вероятно, по этой причине микрокод для них может отличаться. с чем она может быть спараллелена, когда её работа зависит от результата предыдущей операции Добавлено Соседние push/pop не имеют штрафов по зависимостям. Они спецом для этого оптимизированы. Пара xor/inс зависит от ALU Добавлено Ну я же привёл прототипы. Там 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.Забыл добавить, что это лишь мои рассуждения, почему такое может иметь место. Как оно на самом деле, без понятия. |
Автор: Jin X 28.03.18, 21:52 |
Так, дело не в зависимости, а в использовании стека (то бишь памяти, а это время). Цитата Qraizer @ Я заменил int на bool и откомпилил gcc, но результат всё равно почему-то пишется в eax.Ну я же привёл прототипы. Там int. Если хочешь C++, где есть честный bool, то это будут другие прототипы. Цитата Qraizer @ Не спорю (кстати, это может быть причиной использования cmov), просто здесь всё равно используется sete, поэтому xor eax,eax ничего ускорить не должен, по идее...Вообще, даже самые древние мануалы советуют никогда не смешивать разноразмерные данные в зависимых инструкциях Возможно, просто компилер устроен так, что оперирует только полными регистрами. Вот, кстати, надо посмотреть как он будет char возвращать - как байт или дворд. Intel и gcc. |
Автор: Qraizer 29.03.18, 02:46 |
Ничего страшного. Ляжет в L1 кэша, ибо вершина стека да в кэш.промахе... когда такое бывало-то, оттуда же прочтётся, а в память запишется в фоне при отбросе кеш.строк, да и то не факт, что не перезапишется к тому времени мульён раз другими данными. |
Автор: Jin X 29.03.18, 14:05 |
Ляжет, но обращение к кэшу тоже требует времени. Кэш (даже L1) - это же не регистры. |
Автор: Qraizer 29.03.18, 14:14 |
Так это не отнимет тактов. Ещё в i486 была очередь отложенных записей, которые не тормозили ALU, в отличие от чтений, а, если не ошибаюсь, в P6 появилась фича вытаскивать эти данные прям из очереди, т.е. даже без обращения к L1. Поспрашивай leo, он больше об этом всём знает. |
Автор: Jin X 29.03.18, 15:35 |
По поводу cmov'ов Агнер пишет так: Цитата 9.9c - это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. <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> p.s. Про цепочку зависимостей, в общем-то, оно и так понятно. ; Example 9.9c. Branch implemented with conditional move mov eax, [b] cmp eax, [c] mov eax, [d] cmovng eax, [e] И вот ещё: Цитата Здесь он, конечно, сравнивает setcc с jcc, но всё же не про movcc не пишет, а именно про setcc.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. Цитата 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 29.03.18, 17:17 |
Цитата Jin X @ Думаю, никаких теорий заговоров. У него просто мало информации. Если использовать эти функции в контексте, да ещё и инлайнить, код может отличаться от голых __fastcall функций разительно. Собственно, хочется понять: это продуманный умысел Intel (и тогда в чём он?) или недочёт (что было бы странно для производителя процессоров)? |
Автор: amk 30.03.18, 21:26 |
Цитата Jin X @ На AMD, судя по таймингам, одинаково, только комбинация push+pop немного длиннее. на intel push+pop выполняется дольше.Ну и мои тесты показывают, что push 1+pop eax работает медленнее, чем xor eax,eax+inc eax, хотя я понимаю, что при других сочетаниях результаты могут быть другими. Но в любом случае вряд ли push/pop будет когда-либо быстрее, чем xor/inc. А непосредственная засылка единицы в регистр на обоих процессорах будет работать ещё быстрее. |
Автор: Славян 31.03.18, 04:38 |
Я вполне допускаю, что засылка значения быстрее и всё такое, но всё же идеологически вижу это медленнее/неправильнее по таким причинам: засылка значения - это как привоз некими 32-мя фурами издалека значения 0 или 1 (распараллелили), а XOR - это работат тут, на месте, недалёкая=простая в геометрико-транзисторном виде, тоже параллельная 32 раза, - должна быть существенно быстрее! Будет ещё, конечно, INC, но и он ничего извне не просит, а может и тут, на месте, молотить. Так что... Добавлено Цитата Jin X @ Это они практически такие же, но идеологически test не меняет регистр, а лишь "проверяет" (and "в уме") биты, посему ДОЛЖЕН быть быстрее. При этом test edi,edi можно с тем же успехом заменить на and edi,edi или на or edi,edi. Размер тот же, скорость та же. |
Автор: JoeUser 31.03.18, 04:51 |
Лично я, чтобы так плотно, на асме не прогал лет 20 точно. Но одно - помню, нет такого понятия "ДОЛЖЕН". Есть документация. |
Автор: Славян 31.03.18, 05:26 |
Я имел ввиду схемотехническую реализацию, аппаратную. И цитата та не от amk. |
Автор: amk 31.03.18, 06:12 |
Цитата Славян @ Это не завоз издалека. Это когда ты приходишь куда-то, а нужное значение у тебя тут-же с собой, в кармане, и не надо никого за ним куда-то посылать, ни из подручных средств на месте готовить. засылка значения - это как привоз некими 32-мя фурами издалека значения 0 или 1 (распараллелили) |
Автор: Славян 31.03.18, 06:35 |
Э, погодьте-ка! Регистр всё же кусок памяти процессора, такие-то ячейки на подложке, элементы микросхемы. Поэтому ничего в кармане у них вечно нет, - всё откуда-то завозится. И непосредственное значение, даже бит его, всё равно придёт=принесётся! А вот нанофабрика по созданию операции XOR вполне может быть тут же, в шаге; так что мне мыслится это быстрее. Хоть и соглашусь, что практическая реализация может статься и наоборот сделана. |
Автор: amk 31.03.18, 06:52 |
Непосредственные данные (в диапазоне от -128 до 127 это всего 1 байт) поступают на исполнение одновременно с командой, так что никаких дополнительных задержек они в принципе создать не могут. А вот для команды XOR приходится задействовать ALU - пересылать данные туда-сюда. Хорошо, что требуется на это всего один такт. Но потом-то приходится ещё и инкремент делать. А это отдельная команда, вдобавок завязанная на результат предыдущей, запараллелить её не получится. |
Автор: Славян 31.03.18, 07:55 |
Ну я вижу работу декодера как-то так: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> А команды mov reg32, data8 всё равно ж нет, так что приходится зачитывать аж 32 бита, что долго и много. if( XOR ) { switch( операнды ) { case reg32s: if( reg32a vs reg32a ) обнулить!( reg32a ); else ФабрикаXOR(...); ... case память: ВытащитьДанные(); ФабрикаXOR(...); ... case ... } } |
Автор: Jin X 31.03.18, 17:47 |
Почему длиннее? push 1 = 2 байта, pop eax = 1 байт, итого 3. xor eax,eax = 2 байта, inc eax = 1 байт (на 32-х битах, не 64), итого тоже 3. Цитата amk @ Это понятно, но тут оптимизации по размеру.А непосредственная засылка единицы в регистр на обоих процессорах будет работать ещё быстрее. Цитата Славян @ OMG, в каком "уме"? "В уме" может так же означать (в теории), что его нужно куда-то скопировать и там сделать and. Это как вариант. Как там в микрокоде конкретно реализовано - не знаю. Но это всё демагогия. А надо смотреть на таблицы, которые говорят, что на Nehalem, к примеру, mov, xor, test, or, and, add, sub, inc и dec (для операция reg,reg или reg,imm) имеют одни и те же характеристики: используемые порты, кол-во микроопераций и latency. К тому же, xor с inc не могут параллелиться даже чисто теоретически, т.к. inc зависим от результата предыдущей операции - xor.Это они практически такие же, но идеологически test не меняет регистр, а лишь "проверяет" (and "в уме") биты, посему ДОЛЖЕН быть быстрее. Ещё в качестве примера: inc eax по твоей логике должен быть быстрее, чем add eax,1, однако на Pentium 4 он работает медленнее, т.к. не меняет значения флага CF, что требует дополнительной микрооперации. Ещё "в теории" loop должен работать быстрее, чем dec eax + jnz, но вот на практике всё иначе (ещё со времён царя Гороха)... Видь. А Intel видим её по-своему |
Автор: Славян 31.03.18, 18:24 |
Цитата Jin X @ Ну во многих древних книжках по ASM'у так и писалось, что INC - короткая и быстрая, а не то что ADD. Что там поменяли существенно с годами - неясно, но всё же думается, что первые инженеры процессоров не зря сделали такую команду отдельно, - нутром чуяли её специфику, а потому и ускорение. Ещё в качестве примера: inc eax по твоей логике должен быть быстрее, чем add eax,1, однако на Pentium 4 он работает медленнее, т.к. не меняет значения флага CF, что требует дополнительной микрооперации. |
Автор: Jin X 31.03.18, 18:34 |
Славян, напомню, что раньше память ОЗУ была в дифиците и измерялась килобайтами, а не гигабайтами, поэтому вполне логично было иметь 1-байтовые инструкции как альтернативу 3-байтовым - это раз. Ну и да, во времена 8086 процессора add (reg,imm) выполнялась за 4 такта, а inc - за 3. Но с тех пор воды утекло целое озеро Байкал... |