Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.189.14.219] |
|
Сообщ.
#1
,
|
|
|
В продолжение темы сложного выбора. Обращаю внимание, что речь снова о C++. Примеры, надеюсь, не потребуются
|
Сообщ.
#2
,
|
|
|
Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле.
|
Сообщ.
#3
,
|
|
|
Зависит от ситуации. Что понятнее, то и использую. Это не Python, издержек на сравнение с 0 нет
|
Сообщ.
#4
,
|
|
|
Цитата negram @ Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле. Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения |
Сообщ.
#5
,
|
|
|
Когда работаешь с числами - удобнее наверное на 0 проверять. Например делитель числа.
|
Сообщ.
#6
,
|
|
|
Цитата KILLER @ удобнее наверное на 0 проверять Так наверное или удобнее? Сам-то что используешь и в каких ситуациях? |
Сообщ.
#7
,
|
|
|
Цитата B.V. @ Так наверное или удобнее? Сам-то что используешь и в каких ситуациях? Я голоснул за восклицательный знак. Но вообще делитель я бы проверял с помощью if a == 0 togda error, с указателями и прочим юзал бы if !pointer togda error |
Сообщ.
#8
,
|
|
|
Цитата KILLER @ Но вообще делитель я бы проверял с помощью if a == 0 togda error Что-то вспомнилась картинка |
Сообщ.
#9
,
|
|
|
Цитата B.V. @ Что-то вспомнилась картинка Последний вариант прокатит разве что в скриптовых языках или в каком нибудь C# с Java, а в С++ выхватишь Access Violation. |
Сообщ.
#11
,
|
|
|
Цитата B.V. @ Да нет, выхватишь UB. Незнаю что там на счет UB. Может в новом стандарте UB будет, помню у Страуструпа вроде было написано что деление на 0 приведет к аппаратной ошибке процессора, и соответственно обрабатывать такую ошибку нужно с помощью какого нибудь SEH исключения. Стандартные исключения ловят только программные ошибки, и надеяться на то что catch отловит деление на 0 глупая затея. Добавлено Хотя помню компилятор майкрософт в дебаге ловит деление на ноль. В релизе хз. |
Сообщ.
#12
,
|
|
|
Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее!
|
Сообщ.
#13
,
|
|
|
Цитата Славян @ Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее! Не представляю даже, как бы ты на явошарпах с питонами писал бы. Повесился наверное раньше |
Сообщ.
#14
,
|
|
|
Цитата B.V. @ Цитата negram @ Потребуются, ибо выбора между сравнением с нулём и логическим "не" никогда не было. В плюсах, в смысле. Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения Не было никогда такого выбора в плюсах. Выбор мог быть только у тех, кто воспринимает плюсы как "си с классами". благо, такие уже лет 10-15 как вымерли. |
Сообщ.
#15
,
|
|
|
Цитата Славян @ Для сравнения с нулём CMP никогда не использовался. Даже в простейших компиляторах Small-C и Rat-C сравнение с нулём обрабатывалось отдельно от общего случая.Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее! Так что тебе лапшу на уши вешали. |
Сообщ.
#16
,
|
|
|
Увы, но у меня и сейчас MSVS 2017 так жедает (CMP) без настроек ускорения/оптимизации.
|
Сообщ.
#17
,
|
|
|
Ну, видимо так M$ понимает понятие оптимизации. Раз нет оптимизации, значит нельзя даже мелочи оптимизировать.
|
Сообщ.
#18
,
|
|
|
(x == 0).toString().length() == 4
Добавлено А вообще фигня холивар. Даёшь табы vs пробелы Добавлено Цитата Славян @ Увы, но у меня и сейчас MSVS 2017 так жедает (CMP) без настроек ускорения/оптимизации. У тебя странная студия. У меня она даже в дебаге test-ом сравнивает. В релизе с оптимизациями тем более. |
Сообщ.
#19
,
|
|
|
Цитата OpenGL @ Даёшь табы vs пробелы Так уже давно холивар закрыт. http://intellindent.info/seriously/ Проблема только в том, что инструментарий (IDE, редакторы) не умеют в грамотный индент табами с выравниванием пробелами, поэтому приходится деградировать до all-spaces. |
Сообщ.
#20
,
|
|
|
Так нечестно. Я это должен был выложить это в качестве аргумента того, что сторонники пробелов не умеют пользоваться табами
Цитата Da$aD @ Проблема только в том, что инструментарий (IDE, редакторы) не умеют в грамотный индент табами с выравниванием пробелами, поэтому приходится деградировать до all-spaces. Я для этого пользуюсь astyl-ом, автоматически запускаемым при сохранении файла. Он в абсолютном большинстве случаев отличает, когда надо использовать alignment, а когда - indentation - в целом меня вполне устраивает. Пример из статьи отформатировал он правильно. |
Сообщ.
#21
,
|
|
|
Цитата OpenGL @ Даёшь табы vs пробелы Слабо. Даже () { } vs () { } как-то, ИМХО, интереснее Добавлено Между тем, мнения в голосовалке разделились почти поровну. Не ожидал.. |
Сообщ.
#22
,
|
|
|
Цитата B.V. @ Всё просто: каждой ситуации своё предпочтение. Если переменная хранит признак, то ! в самый раз, а если число, то == 0. Просто это не каждый сформулировать догадывается. мнения в голосовалке разделились почти поровну |
Сообщ.
#23
,
|
|
|
Цитата Qraizer @ Если переменная хранит признак, то ! в самый раз, а если число, то == 0. Если переменная объявлена но не определена? Или в условии задачи она точно определена? Добавлено Цитата B.V. @ это выбор между двумя семантиками проверки нулевого значения Все таки проверка числа. |
Сообщ.
#24
,
|
|
|
Цитата Славян @ Ибо вещали (сильно давно), что операция 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 (хотя на практике разницы может и не быть, т.к. все зависит от окружающего кода, "ветра", "фаз луны" и т.д.). |
Сообщ.
#25
,
|
|
|
Цитата B.V. @ Это не выбор между нулём и НЕ, это выбор между двумя семантиками проверки нулевого значения А почему не между двумя семантиками проверки булевого значения? |
Сообщ.
#26
,
|
|
|
Цитата korvin @ А почему не между двумя семантиками проверки булевого значения? Потому что думаю, что выбор был бы слишком предсказуем: подавляющее большинство выбрало бы вариант !. А вот с численными значениями интереснее |
Сообщ.
#27
,
|
|
|
Цитата korvin @ Булевы значения вообще не принято сравнивать. Ещё в C, где вместо них использовались целые, сравнение признака с нулём считалось плохим стилем, если не ошибкой. А сравнение с TRUE (макрос, равный единице) было именно ошибкой. А почему не между двумя семантиками проверки булевого значения? |
Сообщ.
#28
,
|
|
|
Цитата B.V. @ Слабо. Даже () { } vs () { } как-то, ИМХО, интереснее Я выбираю первый вариант |
Сообщ.
#29
,
|
|
|
Для сравнения числовых значений - использую "==", для булевских просто проверка или проверка с отрицанием.
Для форматирования C/C++ кода все ж использую JavaStyle. Для меня он кажется более компактным и лучше читаемым. |
Сообщ.
#30
,
|
|
|
Цитата Polinom2686 @ А я - только второй; по-моему даже в 100% случаев. Я выбираю первый вариант |
Сообщ.
#31
,
|
|
|
Цитата Polinom2686 @ Я выбираю первый Цитата Славян @ А я - только второй Воу-воу, погодите до отдельной голосовалки |
Сообщ.
#32
,
|
|
|
Здесь пока - фифти-фифти; в той отдельной думаю, что Полином2pentPro будет с перевесом (по количеству виденых мною исходников...).
|
Сообщ.
#33
,
|
|
|
Вот и правильно, Славян. А то для }, вишьли, отдельная строка, потому что заканчивает блок, а { почему-то скромненько спаривается с заголовком, что к последующему блоку не имеет никакого отношения, кроме обоснования его наличия.
|
Сообщ.
#34
,
|
|
|
Да, а я как бы согласен с вами, Qraizer, но всё равно мне чертовски жаль, что на фигурки уходит две строчки. Ничего тут не могу поделать! Горе, непреодолимое, и всё тут.
|
Сообщ.
#35
,
|
|
|
Цитата Славян @ Да, а я как бы согласен с вами, Qraizer, но всё равно мне чертовски жаль, что на фигурки уходит две строчки. Ничего тут не могу поделать! Горе, непреодолимое, и всё тут. Не слушай Qraizer'а - переходи на светлую сторону Силы! Одну строчку можно сэкономить - используй Java Style. |
Сообщ.
#36
,
|
|
|
Сокрытие строки не есть светлая сторона, а токмо тёмная!
|
Сообщ.
#37
,
|
|
|
Цитата Polinom2686 @ Я выбираю первый вариант func :: ArgType -> ReturnType func arg = statement Problems? |
Сообщ.
#38
,
|
|
|
Цитата Славян @ Сокрытие строки не сокрытие - а экономия! |
Сообщ.
#39
,
|
|
|
Единственным выходом из непреодолимой ситуации вижу только такой: если весь блок вычислений находится правее if'а (for'а, while'а, ...), то компилер бы его сам заключал в фигурные скобки! И экономия и красота, но... мечты?..
|
Сообщ.
#40
,
|
|
|
Цитата JoeUser @ И экономия и красота, но... мечты?.. Переходи на питон |
Сообщ.
#41
,
|
|
|
терпеть не могу ! в начале
if (a ...) if (b ...) if (!c ...) неровненько... |
Сообщ.
#42
,
|
|
|
А я вот так пишу:
if ( a ...) if ( b ...) if (!c ...) a = ... b =(...) c+= ... |
Сообщ.
#43
,
|
|
|
Так и не должно быть ровненько! Строки вида "if( a...)" несут суть опосля переменной (вида "if( a!=...)"), а строки же с отрицанием сразу утверждают нечто "if( !c )". Разная идеология! В первых вариантах "a" сравнится с чем-то/с какой-то субстанцией, а в отрицании ужо сказано, что всё тут двоичное грядёт. Так что - норм!!
|
Сообщ.
#44
,
|
|
|
Цитата Славян @ Только "!" использую. Ибо вещали (сильно давно), что операция CMP (асм-реализация "==0") крайне тормознутая, а "test" (в случае ! используется, бывает) существенно быстрее! ЛОЛШТО? bool foo1(int a) { return a == 0; } bool foo2(int a) { return !a; } компилируется в foo1(int): test edi, edi sete al ret foo2(int): test edi, edi sete al ret Пруф - https://godbolt.org/g/oKkN1v |
Сообщ.
#45
,
|
|
|
Да, я тоже только что проверил на типичном примере, и вижу, что оба случая (при настройках оптимизации в максимум) реализуются через test. Но это лишь говорит о том, что оптимизатор понял, что test (делает AND по сути ) быстрее довольно туповатого и прямого CMP! Так что всё норм!
Добавлено Более того, посмотрел на Release и Debug варианты и что видим: Release: test reg32, reg32 Debug: cmp dword ptr [], 0 Итог: ! быстрее ==0 при их прямом переводе на маш. код! ч.т.д. |
Сообщ.
#46
,
|
|
|
applegame, а стоило ли рассчитывать, что эти варианты будут различаться?
Вот так надо: 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-х бит)? return (!x || x==0x80000000); Я поаплодирую, если там будет написано так: add edi,edi sete al Добавлено Можно ещё так попробовать: return !(x&0x7FFFFFFF); |
Сообщ.
#47
,
|
|
|
int foo(int x) { return (!x || x==0x80000000); } int bar(int x) { return !(x&0x7FFFFFFF); } _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 _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 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 _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 |
Сообщ.
#48
,
|
|
|
Цитата Qraizer @ Не надо, спасибо.64 бита приводить? Цитата Qraizer @ Вот это уже нормально test DWORD PTR [4+esp], 2147483647 ;8.14 sete al Но Intel что-то гонит. Оптимизация по размеру (приведённый чуть выше код) должна быть быстрее, ибо cmovcc медленнее, чем setcc. Интересен вариант с icl в случае, если x будет не в стеке, а в регистре... |
Сообщ.
#49
,
|
|
|
Медленнее, на такт. Но она может быть спараллелена посредством задействования обоих значений результата, тогда как setcc не может.
Добавлено Цитата Jin X @ Ок, fastcall. По скорости:Интересен вариант с icl в случае, если x будет не в стеке, а в регистре... _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 _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 |
Сообщ.
#50
,
|
|
|
Qraizer, с чем она может быть спараллелена, когда её работа зависит от результата предыдущей операции (всех 3-х предыдущих инструкций, я даже скажу)?
gcc, кстати, срабатывает как xor+test+sete (xor+and+sete для регистра) при оптимизации по скорости для обоих вариантов кода. А вот просто !x или x==0 он компилит аж в: mov edx,[esp+4] xor eax,eax test edx,edx sete al Добавлено Цитата Qraizer @ Жесть какая! push 1 pop eax Тогда бы уж сделали xor eax,eax inc eax Добавлено Цитата Jin X @ Зачем тут xor eax,eax, когда я объявляю тип функции bool? Он же байтовый! xor+test+sete |
Сообщ.
#51
,
|
|
|
Цитата Jin X @ С последующими действиями. Может использоваться либо eax, либо edx, но какой именно, станет известно лишь позднее. Временно можно использовать два потока исполнения, один из которых будет впоследствии отброшен, а второй, использующий "правильный" risc-регистр, переотображён на eax. То же можно сделать с setcc, также задействуя два risc-регистра, однако тут больше сложностей с подготовкой значения для второго. В отличие от cmovcc, где оба значения уже подготовлены и лежат в регистрах, для setcc значения 0 и 1, причём чётко байтовые, нужно подготавливать явно. Вероятно, по этой причине микрокод для них может отличаться. с чем она может быть спараллелена, когда её работа зависит от результата предыдущей операции Добавлено Цитата Jin X @ Соседние push/pop не имеют штрафов по зависимостям. Они спецом для этого оптимизированы. Пара xor/inс зависит от ALU Жесть какая! Добавлено Цитата Jin X @ Ну я же привёл прототипы. Там int. Если хочешь C++, где есть честный bool, то это будут другие прототипы. Зачем тут xor eax,eax, когда я объявляю тип функции bool? Он же байтовый! Добавлено Проверил на intel, никакой разницы. Он всегда предпочитает работать с полноразмерными регистрами, ибо так куда эффективнее работает микрокод. (Вообще, даже самые древние мануалы советуют никогда не смешивать разноразмерные данные в зависимых инструкциях.) А вот visual немного удивил: _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 @ Забыл добавить, что это лишь мои рассуждения, почему такое может иметь место. Как оно на самом деле, без понятия. Временно можно использовать два потока исполнения... |
Сообщ.
#52
,
|
|
|
Цитата Qraizer @ Так, дело не в зависимости, а в использовании стека (то бишь памяти, а это время).Соседние push/pop не имеют штрафов по зависимостям. Цитата Qraizer @ Я заменил int на bool и откомпилил gcc, но результат всё равно почему-то пишется в eax.Ну я же привёл прототипы. Там int. Если хочешь C++, где есть честный bool, то это будут другие прототипы. Цитата Qraizer @ Не спорю (кстати, это может быть причиной использования cmov), просто здесь всё равно используется sete, поэтому xor eax,eax ничего ускорить не должен, по идее...Вообще, даже самые древние мануалы советуют никогда не смешивать разноразмерные данные в зависимых инструкциях Возможно, просто компилер устроен так, что оперирует только полными регистрами. Вот, кстати, надо посмотреть как он будет char возвращать - как байт или дворд. Intel и gcc. |
Сообщ.
#53
,
|
|
|
Цитата Jin X @ Ничего страшного. Ляжет в L1 кэша, ибо вершина стека да в кэш.промахе... когда такое бывало-то, оттуда же прочтётся, а в память запишется в фоне при отбросе кеш.строк, да и то не факт, что не перезапишется к тому времени мульён раз другими данными. Так, дело не в зависимости, а в использовании стека (то бишь памяти, а это время). |
Сообщ.
#54
,
|
|
|
Цитата Qraizer @ Ляжет, но обращение к кэшу тоже требует времени. Кэш (даже L1) - это же не регистры. Ничего страшного. Ляжет в L1 кэша |
Сообщ.
#55
,
|
|
|
Так это не отнимет тактов. Ещё в i486 была очередь отложенных записей, которые не тормозили ALU, в отличие от чтений, а, если не ошибаюсь, в P6 появилась фича вытаскивать эти данные прям из очереди, т.е. даже без обращения к L1. Поспрашивай leo, он больше об этом всём знает.
|
Сообщ.
#56
,
|
|
|
По поводу 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. ; 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 @ Я его, пожалуй, приглашу в эту тему Поспрашивай leo, он больше об этом всём знает. |
Сообщ.
#57
,
|
|
|
Цитата Jin X @ Думаю, никаких теорий заговоров. У него просто мало информации. Если использовать эти функции в контексте, да ещё и инлайнить, код может отличаться от голых __fastcall функций разительно. Собственно, хочется понять: это продуманный умысел Intel (и тогда в чём он?) или недочёт (что было бы странно для производителя процессоров)? |
Сообщ.
#58
,
|
|
|
Цитата Jin X @ На AMD, судя по таймингам, одинаково, только комбинация push+pop немного длиннее. на intel push+pop выполняется дольше.Ну и мои тесты показывают, что push 1+pop eax работает медленнее, чем xor eax,eax+inc eax, хотя я понимаю, что при других сочетаниях результаты могут быть другими. Но в любом случае вряд ли push/pop будет когда-либо быстрее, чем xor/inc. А непосредственная засылка единицы в регистр на обоих процессорах будет работать ещё быстрее. |
Сообщ.
#59
,
|
|
|
Я вполне допускаю, что засылка значения быстрее и всё такое, но всё же идеологически вижу это медленнее/неправильнее по таким причинам: засылка значения - это как привоз некими 32-мя фурами издалека значения 0 или 1 (распараллелили), а XOR - это работат тут, на месте, недалёкая=простая в геометрико-транзисторном виде, тоже параллельная 32 раза, - должна быть существенно быстрее! Будет ещё, конечно, INC, но и он ничего извне не просит, а может и тут, на месте, молотить. Так что...
Добавлено Цитата Jin X @ Это они практически такие же, но идеологически test не меняет регистр, а лишь "проверяет" (and "в уме") биты, посему ДОЛЖЕН быть быстрее. При этом test edi,edi можно с тем же успехом заменить на and edi,edi или на or edi,edi. Размер тот же, скорость та же. |
Сообщ.
#60
,
|
|
|
Цитата amk @ посему ДОЛЖЕН быть быстрее. Лично я, чтобы так плотно, на асме не прогал лет 20 точно. Но одно - помню, нет такого понятия "ДОЛЖЕН". Есть документация. |
Сообщ.
#61
,
|
|
|
Я имел ввиду схемотехническую реализацию, аппаратную. И цитата та не от amk.
|
Сообщ.
#62
,
|
|
|
Цитата Славян @ Это не завоз издалека. Это когда ты приходишь куда-то, а нужное значение у тебя тут-же с собой, в кармане, и не надо никого за ним куда-то посылать, ни из подручных средств на месте готовить. засылка значения - это как привоз некими 32-мя фурами издалека значения 0 или 1 (распараллелили) |
Сообщ.
#63
,
|
|
|
Э, погодьте-ка! Регистр всё же кусок памяти процессора, такие-то ячейки на подложке, элементы микросхемы. Поэтому ничего в кармане у них вечно нет, - всё откуда-то завозится. И непосредственное значение, даже бит его, всё равно придёт=принесётся! А вот нанофабрика по созданию операции XOR вполне может быть тут же, в шаге; так что мне мыслится это быстрее. Хоть и соглашусь, что практическая реализация может статься и наоборот сделана.
|
Сообщ.
#64
,
|
|
|
Непосредственные данные (в диапазоне от -128 до 127 это всего 1 байт) поступают на исполнение одновременно с командой, так что никаких дополнительных задержек они в принципе создать не могут. А вот для команды XOR приходится задействовать ALU - пересылать данные туда-сюда. Хорошо, что требуется на это всего один такт. Но потом-то приходится ещё и инкремент делать. А это отдельная команда, вдобавок завязанная на результат предыдущей, запараллелить её не получится.
|
Сообщ.
#65
,
|
|
|
Ну я вижу работу декодера как-то так:
if( XOR ) { switch( операнды ) { case reg32s: if( reg32a vs reg32a ) обнулить!( reg32a ); else ФабрикаXOR(...); ... case память: ВытащитьДанные(); ФабрикаXOR(...); ... case ... } } |
Сообщ.
#66
,
|
|
|
Цитата amk @ Почему длиннее? push 1 = 2 байта, pop eax = 1 байт, итого 3.На AMD, судя по таймингам, одинаково, только комбинация push+pop немного длиннее 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 видим её по-своему Ну я вижу работу декодера как-то так |
Сообщ.
#67
,
|
|
|
Цитата Jin X @ Ну во многих древних книжках по ASM'у так и писалось, что INC - короткая и быстрая, а не то что ADD. Что там поменяли существенно с годами - неясно, но всё же думается, что первые инженеры процессоров не зря сделали такую команду отдельно, - нутром чуяли её специфику, а потому и ускорение. Ещё в качестве примера: inc eax по твоей логике должен быть быстрее, чем add eax,1, однако на Pentium 4 он работает медленнее, т.к. не меняет значения флага CF, что требует дополнительной микрооперации. |
Сообщ.
#68
,
|
|
|
Славян, напомню, что раньше память ОЗУ была в дифиците и измерялась килобайтами, а не гигабайтами, поэтому вполне логично было иметь 1-байтовые инструкции как альтернативу 3-байтовым - это раз.
Ну и да, во времена 8086 процессора add (reg,imm) выполнялась за 4 такта, а inc - за 3. Но с тех пор воды утекло целое озеро Байкал... |