【发布时间】:2019-03-03 15:38:42
【问题描述】:
我正在计算 3 分的平均值:
g0 dw 70
g1 dw 100
g2 dw 65
与
xor rax, rax
xor rcx, rcx
mov ax, [g0]
inc rcx
add ax, [g1]
inc rcx
add ax, [g2]
inc rcx
xor rdx, rdx
idiv rcx
成绩不需要是单词,因为对于平均而言,字节就足够了,但总和必须是一个单词以避免溢出(即使用此算法)。
如何将成绩转换为字节?使用db 是不够的,因为那样我必须将ax 更改为al,但这会导致最后溢出。
我不能指示mov/add 只从[g*] 中获取一个字节,因为这会导致操作数大小不匹配。
我正在使用 yasm。
【问题讨论】:
-
如果你知道总和只是字,你可以做
dx:ax / cx除以idiv cx...在x86-64的大多数情况下,使用字寄存器不会带来性能优势,甚至可能由于现代 CPU 的更复杂的寄存器管理而招致额外的惩罚,但在 DIV/IDIV 指令的情况下,64 位变体可能仍然比 32/16/8 位变体慢得多。所以这是一件需要考虑的事情(因为您的代码只能写为 16b,尽管在 x86-64 模式下,出于性能原因(更短的指令操作码)我可能会直接使用 32b)。 -
如果您坚持使用
idiv(签名除法),您还应该在签名变体中包含其他部分(然后使用movsx而不是建议的movzx,并使用cqo签名-将rax扩展到rdx:rax而不是xor rdx,rdx.. 或使用div与您的zx485 代码进行无符号除法。 -
@Ped7g 我问这个是因为我认为我可以从使用更少的字节中受益。只有在内存不足的情况下,我才应该记住这一点?
-
特殊情况可能需要特殊的解决方案,所以继续练习你能想到的任何东西。 x86-64 的性能问题从来都不是简单的,机器非常复杂。您将标记更改为 8 位类型的方式将节省内存,因此如果您在输入端有数十亿个标记,这绝对是有道理的(但总和需要超过 64 位,两个 64b 寄存器对将提供总计 128b 的总和,这在这种数十亿马克的特殊情况下就足够了)。更短的代码确实使 L1 指令缓存等更容易... ...
-
然后对一些不明显的事情有更复杂(轻微!)的惩罚,比如以不可预测的方式使用寄存器的一部分,迫使 CPU 将两个独立代码行的结果合并回一个某些指令的价值,限制乱序执行以不同的顺序做更多的事情。无论如何,首先要专注于编写易于维护的 100% 正确代码(即,与细微优化相比,有符号 div 与无符号值是主要问题)。一旦你有了工作代码,你就可以测量它的性能,看看瓶颈在哪里,然后解决那个部分。
标签: assembly x86 64-bit x86-64 yasm