Как BTS-адресат в памяти может быть значительно медленнее, чем загрузка / BTS reg,reg / store?
В общем случае, как может инструкция, которая может занимать память или регистрировать операнды, когда-либо быть медленнее с операндами памяти, чем mov + mov -> инструкция -> mov + mov
Основываясь на пропускной способности и задержке, найденных в таблицах инструкций Агнера Фога (в моем случае на Skylake, p238), я вижу, что следующие числа дляbtr/bts
инструкции:
instruction, operands, uops fused domain, uops unfused domain, latency, throughput
mov r,r 1 1 0-1 .25
mov m,r 1 2 2 1
mov r,m 1 1 2 .5
...
bts/btr r,r 1 1 N/A .5
bts/btr m,r 10 10 N/A 5
Я не понимаю, как эти числа могут быть правильными. Даже в худшем случае, когда нет резервных регистров, и вы сохранили один во временной области памяти, было бы быстрее:
## hypothetical worst-case microcode that saves/restores a scratch register
mov m,r // + 1 throughput , save a register
mov r,m // + .5 throughput , load BTS destination operand
bts r,r // + 1 throughput , do bts (or btr)
mov m,r // + 1 throughput , store result
mov r,m // + .5 throughput , restore register
В худшем случае у этого есть лучшая пропускная способность, чем просто bts m,r
(4 < 5). (Примечание редактора: суммирование пропускной способности не работает, когда у них разные узкие места. Вам нужно учитывать операторы и порты; эта последовательность должна быть пропускной способностью 2c, узким местом при пропускной способности хранилища 1/ такт.)
Инструкции микрокода имеют собственный набор регистров, поэтому маловероятно, что это действительно понадобится. Кто-нибудь может объяснить почемуbts
(или вообще любая инструкция) может иметь более высокую пропускную способность с памятью, регистровыми операндами, чем при использовании политики перемещения наихудшего случая.
(Примечание редактора: да, есть несколько скрытых временных регистров, которые может использовать микрокод. Что-то вроде add [mem], reg
по крайней мере логически просто загружается в один из них, а затем сохраняет результат.)
1 ответ
Вам не хватает того, что BT, BTC, BTS и BTR не работают, как вы описали, когда используется операнд памяти. Вы предполагаете, что версии памяти работают так же, как версии регистров, но это не совсем так. В регистровой версии значение второго используемого операнда берется по модулю 64 (или 16, или 32). В версии для памяти значение второго операнда используется как есть. Это означает, что фактическая ячейка памяти, к которой обращается инструкция, может быть не адресом, заданным операндом памяти, а адресом где-то за ним.
Например, игнорируя необходимость сохранять регистры и атомарность, чтобы получить ту же операцию BTS [rsi + rdi], rax
используя регистровую версию BTS, вам нужно будет сделать что-то вроде этого:
LEA rbx, [rsi + rdi]
MOV rcx, rax
SHR rcx, 8
MOV rdx, [rbx + rcx]
BTS rdx, rax
MOV [rbx + rcx], rdx
Вы можете упростить это, если знаете, что значение RAX меньше 64, или если это более простой операнд памяти. В самом деле, как вы заметили, в подобных случаях может быть преимуществом использование более быстрой версии с регистром по сравнению с версией с более медленной памятью, даже если это означает еще несколько инструкций.