Looping without clobbering CF
will be faster. См. Эту ссылку для некоторых лучей asm.
Не пытайтесь писать только adc
с встроенным asm внутри цикла C. Это невозможно для того, чтобы быть оптимальным, потому что вы не можете просить gcc не использовать флаги clobber. Попытка узнать asm с помощью встроенного asm GNU C сложнее, чем писать автономную функцию, особенно. в этом случае, когда вы пытаетесь сохранить флаг переноса.
Вы можете использовать setnc %[carry]
для сохранения и subb $1, %[carry]
для восстановления. (Или cmpb $1, %[carry]
Я думаю.) Или, как указывает Стивен, negb %[carry]
.
0 - 1
производит перенос, но 1 - 1
нет.
Использовать uint8_t
для переменной, чтобы удерживать перенос, поскольку вы никогда не добавите его непосредственно на %[anum]
. Это позволяет избежать случайностей partial-register slowdowns. например
uint8_t carry = 0;
int64_t numa, numb;
for (...) {
asm ("negb %[carry]\n\t"
"adc %[bnum], %[anum]\n\t"
"setc %[carry]\n\t"
: [carry] "+&r" (carry), [anum] "+r" (anum)
: [bnum] "rme" (bnum)
: // no clobbers
);
}
Вы также можете предоставить альтернативный шаблон ограничения для источника регистра, reg/mem dest. Я использовал an x86 "e"
constraint вместо "i"
, потому что 64-битный режим по-прежнему позволяет только 32-битные расширенные символы. gcc должен будет получить большие константы времени компиляции в регистр самостоятельно. Carry ранний сбитый, поэтому даже если он и bnum
были как 1
, так и gcc не могли использовать один и тот же регистр для обоих входов.
Это все еще ужасно и увеличивает длину цепи зависимостей, связанной с циклом, от 2c до 4c (Intel pre-Broadwell) или от 1c до 3c (Intel BDW/Skylake и AMD).
Таким образом, ваша петля работает со скоростью 1/3, потому что вы используете kludge вместо того, чтобы писать весь цикл в asm.
Предыдущая версия этого ответа предложил добавить перенос непосредственно, вместо того чтобы восстановить его в CF
. Этот подход имеет фатальный недостаток: он смешивает входящую перенос в эту итерацию с исходящим переносом, идущим на следующую итерацию.
Также sahf
- это набор AH из флагов. lahf
загружает AH во флаги (и он работает на всех низких 8 бит флагов.Сопоставьте эти инструкции; не используйте lahf
на 0 или 1, которые вы получили от setc
.
Прочтите инструкцию по установке insn для любых insns, которые, похоже, не делают то, что вы ожидаете. См https://stackoverflow.com/tags/x86/info
Вы делаете 32 бит на 64-битной машине? или 64 бит добавляется на 64-битную машину? –
Я использую 64-битную машину. Для записи 'anum' и' bnum' имеют длину 8 байтов. –
Является ли размер массива известным во время компиляции? –