相关疑难解决方法(0)

为什么MSVC在执行此位测试之前会发出无用的MOVSX?

在MSVC 2013中编译以下代码,64位版本构建,/O2优化:

while (*s == ' ' || *s == ',' || *s == '\r' || *s == '\n') {
    ++s;
}
Run Code Online (Sandbox Code Playgroud)

我得到了以下代码 - 使用64位寄存器作为带有bt(位测试)指令的查找表,它具有非常酷的优化.

    mov     rcx, 17596481020928             ; 0000100100002400H
    npad    5
$LL82@myFunc:
    movzx   eax, BYTE PTR [rsi]
    cmp     al, 44                          ; 0000002cH
    ja      SHORT $LN81@myFunc
    movsx   rax, al
    bt      rcx, rax
    jae     SHORT $LN81@myFunc
    inc     rsi
    jmp     SHORT $LL82@myFunc
$LN81@myFunc:
    ; code after loop...
Run Code Online (Sandbox Code Playgroud)

但我的问题是:movsx rax, al第一个分支后的目的是什么?

首先,我们从字符串中加载一个字节rax并对其进行零扩展:

movzx eax, BYTE …
Run Code Online (Sandbox Code Playgroud)

c++ x86 assembly x86-64 visual-studio

15
推荐指数
2
解决办法
918
查看次数

在某些CPU的紧密循环中出现ADC/SBB和INC/DEC问题

我在Delphi中编写一个简单的BigInteger类型.它主要由TLimb的动态数组组成,其中TLimb是32位无符号整数,32位大小字段,它还保存BigInteger的符号位.

要添加两个BigIntegers,我创建一个适当大小的新BigInteger然后,在一些簿记之后,调用以下过程,将三个指针传递给左右操作数和结果的数组的相应开始,以及左右肢的数量分别为.

普通代码:

class procedure BigInteger.PlainAdd(Left, Right, Result: PLimb; LSize, RSize: Integer); 
asm
// EAX = Left, EDX = Right, ECX = Result
        PUSH    ESI
        PUSH    EDI
        PUSH    EBX
        MOV     ESI,EAX                 // Left
        MOV     EDI,EDX                 // Right
        MOV     EBX,ECX                 // Result
        MOV     ECX,RSize               // Number of limbs at Left
        MOV     EDX,LSize               // Number of limbs at Right
        CMP     EDX,ECX
        JAE     @SkipSwap
        XCHG    ECX,EDX                 // Left and LSize should be largest
        XCHG    ESI,EDI                 // so swap
@SkipSwap:
        SUB     EDX,ECX                 // …
Run Code Online (Sandbox Code Playgroud)

delphi x86 assembly

15
推荐指数
2
解决办法
919
查看次数

使用CMP reg测试寄存器是否为零,0与OR reg,reg?

使用以下代码是否存在任何执行速度差异:

cmp al, 0
je done
Run Code Online (Sandbox Code Playgroud)

以下内容:

or al, al
jz done
Run Code Online (Sandbox Code Playgroud)

我知道JE和JZ指令是相同的,并且使用OR可以提供一个字节的大小改进.但是,我也关心代码速度.逻辑运算符似乎比SUB或CMP更快,但我只是想确定.这可能是规模和速度之间的权衡,或双赢(当然代码将更加不透明).

optimization x86 assembly micro-optimization

13
推荐指数
2
解决办法
3755
查看次数

Branch Target Buffer检测到哪些分支错误预测?

我目前正在查看CPU管道的各个部分,它们可以检测分支错误预测.我发现这些是:

  1. 分支目标缓冲区(BPU CLEAR)
  2. 分支地址计算器(BA CLEAR)
  3. 跳转执行单元(这里不确定信号名称?)

我知道2和3检测到了什么,但我不明白在BTB中检测到了什么错误预测.BAC检测BTB错误地预测非分支指令的分支的位置,其中BTB未能检测到分支,或者BTB错误预测了x86 RET指令的目标地址.执行单元评估分支并确定它是否正确.

在分支目标缓冲区中检测到什么类型的错误预测?究竟在这里发现了什么错误预测?

我能找到的唯一线索是英特尔开发者手册第3卷(底部的两个BPU CLEAR事件计数器):

在此输入图像描述

BPU在错误地认为未采取分支后预测了一个分支.

这似乎暗示预测并非"同步",而是"异步",因此"在错误地假设"之后?

更新:

Ross,这是CPU分支电路,来自最初的英特尔专利(如何用于"阅读"?):

在此输入图像描述

我在任何地方都看不到"分支预测单位"?读过这篇论文的人会认为"BPU"是将BTB电路,BTB缓存,BAC和RSB分组在一起的懒惰方式吗?

所以我的问题仍然存在,哪个组件会引发BPU CLEAR信号?

optimization intel cpu-architecture computer-architecture branch-prediction

10
推荐指数
2
解决办法
1080
查看次数

为什么EAX中的高16位不能按名称访问(如AX,AH和AL)?

为什么没有特定的寄存器来访问寄存器的其他部分(16-32)?

al一样访问ax寄存器的8位部分.

在此输入图像描述

x86 assembly

6
推荐指数
2
解决办法
717
查看次数

优化第 7 代英特尔酷睿视频 RAM 中递增的 ASCII 十进制计数器

我正在尝试针对特定的 Kaby Lake CPU (i5-7300HQ) 优化以下子例程,理想情况下,与原始形式相比,代码速度至少快 10 倍。该代码在 16 位实模式下作为软盘式引导加载程序运行。它在屏幕上显示一个十位数的十进制计数器,从 0 - 9999999999 计数然后停止。

我查看了 Agner 的微体系结构汇编优化指南、 指令性能表和英特尔的优化参考手册

到目前为止,我能够做的唯一明智的优化是将loop指令交换为dec + jnz在此处进行解释。

另一种可能的优化可能是交换lodsbfor mov + dec,但我发现的关于它的信息一直存在冲突,有些人说它有一点帮助,而另一些人则认为它实际上可能会损害现代 CPU 的性能。

我还尝试切换到 32 位模式并将整个计数器保留在一个未使用的寄存器对中以消除任何内存访问,但在读入一点后我意识到这十位将立即被缓存,并且 L1 缓存之间的延迟差异和寄存器只有大约三倍,所以绝对不值得以这种格式使用计数器的额外开销。

(编者注:add reg延迟为 1 个周期,add [mem]延迟约为 6 个周期,包括 5 个周期的存储转发延迟。如果[mem]像视频 RAM 那样不可缓存,则更糟。)

org 7c00h

pos equ 2*(2*80-2)  ;address on screen

;init
cli
mov ax,3
int 10h
mov …
Run Code Online (Sandbox Code Playgroud)

optimization x86 assembly intel bootloader

6
推荐指数
1
解决办法
258
查看次数

如何在 x86 程序集中设置或清除溢出标志?

我想编写一个简单的代码(或算法)来设置/清除溢出标志。对于设置 OF,我知道我可以使用有符号值。但我怎么能清除呢?

x86 assembly eflags

5
推荐指数
1
解决办法
6457
查看次数

最近英特尔微架构中的简单解码器能否处理所有 1-µop 指令?

最近的 Intel CPU 的前端包含一个复杂的解码器和许多简单的解码器。复杂解码器可以处理解码为多个微操作的指令,而简单解码器仅支持解码为单个(融合域)微操作的指令。

是否可以通过简单解码器来解码所有 1-μop 指令,或者是否存在只能由复杂解码器处理的 1-μop 指令?

cpu x86 x86-64 intel cpu-architecture

5
推荐指数
1
解决办法
327
查看次数

现代 CPU 是否需要花费大量资源来保持标志更新?

据我了解,在现代无序 CPU 上,最昂贵的事情之一是状态,因为必须在多个版本中跟踪该状态,并在许多指令中保持最新状态等。

一些像 x86 和 ARM 的指令集大量使用了标志,当成本模型不是今天的样子时引入了标志,而标志只需要几个逻辑门。诸如每个算术指令设置标志以检测零、进位和溢出之类的事情。

这些在现代乱序实现上保持更新是否特别昂贵?例如,一条 ADD 指令更新进位标志,这必须被跟踪,因为虽然它可能永远不会被使用,但有可能其他指令可以在 N 条指令后使用它,对 N 没有固定的上限?

在没有这些标志的指令集架构(如 MIPS)上,诸如加法和减法之类的整数运算是否更便宜?

performance assembly cpu-architecture micro-optimization eflags

5
推荐指数
1
解决办法
120
查看次数

如何让BSR指令在64位上工作?

我正在尝试找到无符号 64 位 int 的前导位。我正在使用 BSR,因为我的处理器没有 LZCNT 指令。我的问题是,一旦输入正好是 2^32,它就会返回 2^64 作为前导位值,然后循环返回输出,直到 2^64。

这是我的代码:

unsigned long int LeadingBit(unsigned long int a) {
  if(a==0)
    return 0;
  unsigned long int nlb;
  asm (
       "BSR %1, %0   \n"
       : "=r" (nlb)
       : "mr" (a)
       : "cc"
       );
  return 1<<nlb;

}
Run Code Online (Sandbox Code Playgroud)

这段代码的目的是能够输入一个64位整数,并让它返回前导1的位置的值。例如:a = 65(1000001)返回1000000。

assembly g++ x86-64 inline-assembly

4
推荐指数
1
解决办法
3237
查看次数

如何在汇编中以字节数的2位进行交换

*我正在使用程序集8086(x86-32)

交易非常简单,我在寄存器AL(8位)中有一个字节大小的数字,现在,我需要在寄存器中的第1位(右起第二位)和第4位(右起第五位)之间进行交换AL.

例如:如果Al有这个数字:00010000B现在它将有00000010B.

谢谢!

x86 assembly bit-manipulation

3
推荐指数
2
解决办法
1531
查看次数

*移动消除*插槽在 Intel CPU 中如何工作?

Andreas Abel 和 Jan Reineke在他们描述 uiCA 的论文中讨论了移动消除

4.1.4移动消除。[...] 然而,这种消除动作并不总是成功的。[...]我们开发了微基准,使用这些计数器来分析移动消除何时成功。[...]
以下模型与我们的观察一致。处理器跟踪由多个架构寄存器使用的物理寄存器。我们说每个这样的物理寄存器占用一个消除槽。当相应的寄存器被覆盖后,消除槽被再次释放。* 一个周期内可以消除的移动指令数量既取决于可用消除槽的数量,也取决于前一个周期中成功消除的数量。

我对我不明白的部分进行了强调。

我认为给定的物理寄存器只能由单个体系结构寄存器从重命名到退出使用。我认为文本的含义暗示了其他情况,所以我正在努力理解移动消除槽是如何工作的(此时甚至寄存器重命名实际上是如何工作的)。

x86 intel cpu-architecture

3
推荐指数
1
解决办法
516
查看次数