Спросив этот ТАК вопрос, Я получил очень интересный комментарий от @ AndonM.Coleman, который мне пришлось проверить.
Поскольку ваш дизассемблированный код написан для x86, стоит указать, что XOR установит / сбросит нулевой флаг, а NOT — нет (иногда полезно, если вы хотите выполнить побитовую операцию, не затрагивая условия перехода, которые зависят от флагов от предыдущих операций) , Теперь, учитывая, что вы не пишете ассемблер напрямую, у вас действительно нет значимого доступа к этому флагу, поэтому я сомневаюсь, что это причина для предпочтения одного над другим.
Его комментарий меня заинтересовал, если следующий код будет производить те же инструкции по сборке
#include <iostream>
int main()
{
unsigned int val = 0;
std::cout << "Enter a numeric value: ";
std::cin >> val;
if ( (val ^ ~0U) == 0)
{
std::cout << "Value inverted is zero" << std::endl;
} else
{
std::cout << "Value inverted is not zero" << std::endl;
}
if ( (~val) == 0)
{
std::cout << "Value inverted is zero" << std::endl;
} else
{
std::cout << "Value inverted is not zero" << std::endl;
}
return 0;
}
Для следующих двух операций
if ( (val ^ ~0U) == 0 )
а также
if ( (~val) == 0 )
не оптимизирован Сборка в Visual Studio 2010 дает следующую разборку:
if ( (val ^ ~0U) == 0)
00AD1501 mov eax,dword ptr [val]
00AD1504 xor eax,0FFFFFFFFh
00AD1507 jne main+86h (0AD1536h)if ( (~val) == 0)
00AD1561 mov eax,dword ptr [val]
00AD1564 not eax
00AD1566 test eax,eax
00AD1568 jne main+0E7h (0AD1597h)
Мой вопрос касается оптимизации. Лучше написать
if ( (val ^ ~0U) == 0)
или же
if ( (~val) == 0)
Это зависит от многих вещей, но в основном от того, что (если вообще что-то) вы говорите компилятору для оптимизации.
Если компилятор настроен на оптимизацию по размеру (наименьший байт-код), то иногда он будет использовать XOR
в, казалось бы, странных местах. Например, схема кодирования переменной длины, которую использует X86, может установить регистр в 0 от XOR
в меньшем количестве байт кода, чем требуется при использовании MOV
инструкция.
XOR
:if ( (val ^ ~0U) == 0 ) /* 3-bytes to negate and test (x86) */
XOR eax,0FFFFFFFFh
требуется 3 байта А ТАКЖЕ устанавливает / очищает нулевой флаг (ZF)
NOT
:if ( (~val) == 0) /* 4-bytes to negate and test (x86) */
NOT eax
закодирован в 2-байтовую инструкцию, но не влияет на флаги процессора.
TEST eax,eax
добавляет дополнительные 2 байта, и необходимо установить / очистить Zero Flag (ZF)
NOT
это также простая инструкция, но, поскольку она не влияет на какие-либо флаги процессора, вы должны выполнить TEST
Инструкция впоследствии использовать его для ветвления, как видно из вашего кода. Это на самом деле дает больший байт-код, поэтому умный компилятор, настроенный на оптимизацию по размеру, наверное стараться избегать использования NOT
, Сколько циклов выполняются обеими этими инструкциями вместе, зависит от генерации процессора, и умный компилятор также учитывает это при принятии решения, когда ему говорят, что нужно оптимизировать скорость.
Если вы не пишете сборку с ручной настройкой, лучше всего использовать то, что является наиболее понятным для человека, и надеяться, что компилятор достаточно умен, чтобы выбирать различные инструкции / планирование / и т.д. оптимизировать для размера / скорости в соответствии с запросом во время компиляции. Компиляторы имеют умный набор эвристик, которые они используют для выбора и планирования команд, они знают больше об архитектуре целевого процессора, чем средний кодер.
Если позже вы обнаружите, что эта ветвь действительно является узким местом, и нет более высокоуровневого пути решения проблемы, тогда вы могли бы выполнить некоторую низкоуровневую настройку. Тем не менее, это такая тривиальная вещь, на которой стоит сосредоточиться в наши дни, если только вы не нацелены на что-то вроде встроенного процессора с низким энергопотреблением или устройства с ограниченной памятью. Единственные места, где я когда-либо выживал достаточную производительность путем ручной настройки, чтобы сделать ее достойной, были в алгоритмах, которые выиграли от параллелизма данных, и где компилятор не был достаточно умен, чтобы эффективно использовать специализированные наборы команд, такие как MMX / SSE.
Других решений пока нет …