相关疑难解决方法(0)

找到位数组中设置的最高有效位(最左侧)

我有一个位数组实现,其中第0个索引是数组中第一个字节的MSB,第8个索引是第二个字节的MSB,等等...

找到这个位数组中设置的第一个位的快速方法是什么?我查找的所有相关解决方案都找到了第一个最重要的位,但我需要第一个最重要的解决方案.所以,给定0x00A1,我想要8(因为它是左起第9位).

c 32-bit bit-manipulation

38
推荐指数
5
解决办法
7万
查看次数

为什么破坏LZCNT的"输出依赖性"很重要?

在测量某些东西的同时,我测量的吞吐量比我计算的要低得多,我将其缩小到LZCNT指令(它也发生在TZCNT中),如以下基准所示:

  xor ecx, ecx
_benchloop:
  lzcnt eax, edx
  add ecx, 1
  jnz _benchloop
Run Code Online (Sandbox Code Playgroud)

和:

  xor ecx, ecx
_benchloop:
  xor eax, eax  ; this shouldn't help, but it does
  lzcnt eax, edx
  add ecx, 1
  jnz _benchloop
Run Code Online (Sandbox Code Playgroud)

第二个版本要快得多.它不应该.LZCNT没有理由对其输出有输入依赖性.与BSR/BSF不同,xZCNT指令总是覆盖其输出.

我在4770K上运行它,所以LZCNT和TZCNT没有被执行为BSR/BSF.

这里发生了什么?

x86 assembly

22
推荐指数
1
解决办法
1339
查看次数

x86 bsr/bsf如何具有固定的延迟,而不是数据依赖?它不像伪代码那样循环比特吗?

我正在试图分析一些x86二进制代码的"时序通道".我发布了一个问题来理解bsf/bsr操作码.

如此高级,这两个操作码可以被建模为"循环",它计算给定操作数的前导零和尾随零.该x86手册对这些操作码具有良好的形式化,如下所示:

IF SRC = 0
  THEN
    ZF ? 1;
    DEST is undefined;
  ELSE
    ZF ? 0;
    temp ? OperandSize – 1;
    WHILE Bit(SRC, temp) = 0
    DO
      temp ? temp - 1;
    OD;
    DEST ? temp;
FI;
Run Code Online (Sandbox Code Playgroud)

但令我惊讶的是,bsf/bsr指令似乎有固定的cpu周期.根据我在这里找到的一些文档:https://gmplib.org/~tege/x86-timing.pdf,似乎它们总是需要8个CPU周期来完成.

所以这是我的问题:

  1. 我确认这些指令有固定的cpu周期.换句话说,无论给出什么操作数,它们总是花费相同的时间来处理,并且没有"时序通道".我在英特尔的官方文档中找不到相应的规格.

  2. 那么为什么有可能呢?显然这是一个"循环"或某种程度,至少是高级别的.背后的设计决策是什么?CPU流水线更容易?

performance x86 assembly intel cpu-architecture

9
推荐指数
1
解决办法
412
查看次数

为什么整数除以-1(负一)导致FPE?

我的任务是表达一些看似奇怪的C代码行为(在x86上运行).我可以很容易地完成其他所有事情,但这个让我很困惑.

代码段1输出 -2147483648

int a = 0x80000000;
int b = a / -1;
printf("%d\n", b);
Run Code Online (Sandbox Code Playgroud)

代码片段2没有输出任何内容,并给出了一个 Floating point exception

int a = 0x80000000;
int b = -1;
int c = a / b;
printf("%d\n", c);
Run Code Online (Sandbox Code Playgroud)

我很清楚Code Snippet 1(1 + ~INT_MIN == INT_MIN)的结果的原因,但是我不太明白整数除以-1如何生成FPE,也不能在我的Android手机(AArch64,GCC 7.2.0)上重现它.代码2只输出与代码1相同,没有任何例外.它是x86处理器的隐藏bug功能吗?

该任务没有告诉任何其他内容(包括CPU架构),但由于整个课程基于桌面Linux发行版,您可以放心地认为它是一个现代的x86.


编辑:我联系了我的朋友,他在Ubuntu 16.04(Intel Kaby Lake,GCC 6.3.0)上测试了代码.结果与所指定的任何内容一致(代码1输出所述内容,代码2与FPE崩溃).

c x86 gcc x86-64 floating-point-exceptions

8
推荐指数
2
解决办法
1188
查看次数

VS:_BitScanReverse64内在的意外优化行为

以下代码在调试模式下工作正常,因为如果没有设置Bit,_BitScanReverse64被定义为返回0.引用MSDN :(返回值为)"如果设置了索引则为非零,如果未找到设置位,则为0."

如果我在发布模式下编译此代码它仍然有效,但如果我启用编译器优化,例如\ O1或\ O2,则索引不为零且assert()失败.

#include <iostream>
#include <cassert>

using namespace std;

int main()
{
  unsigned long index = 0;
  _BitScanReverse64(&index, 0x0ull);

  cout << index << endl;

  assert(index == 0);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是预期的行为吗?我正在使用Visual Studio Community 2015,版本14.0.25431.01更新3.(我离开了cout,因此在优化期间不会删除变量索引).还有一个有效的解决方法或我不应该直接使用此编译器内在?

c++ optimization x86-64 intrinsics visual-studio

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

为什么clang用-O0生成效率低的asm(对于这个简单的浮点和)?

我在llvm clang Apple LLVM 8.0.0版(clang-800.0.42.1)上反汇编代码:

int main() {
    float a=0.151234;
    float b=0.2;
    float c=a+b;
    printf("%f", c);
}
Run Code Online (Sandbox Code Playgroud)

我编译时没有-O规范,但我也试过-O0(给出相同)和-O2(实际上计算值并存储它预先计算)

产生的反汇编如下(我删除了不相关的部分)

->  0x100000f30 <+0>:  pushq  %rbp
    0x100000f31 <+1>:  movq   %rsp, %rbp
    0x100000f34 <+4>:  subq   $0x10, %rsp
    0x100000f38 <+8>:  leaq   0x6d(%rip), %rdi       
    0x100000f3f <+15>: movss  0x5d(%rip), %xmm0           
    0x100000f47 <+23>: movss  0x59(%rip), %xmm1        
    0x100000f4f <+31>: movss  %xmm1, -0x4(%rbp)  
    0x100000f54 <+36>: movss  %xmm0, -0x8(%rbp)
    0x100000f59 <+41>: movss  -0x4(%rbp), %xmm0         
    0x100000f5e <+46>: addss  -0x8(%rbp), %xmm0
    0x100000f63 <+51>: movss  %xmm0, -0xc(%rbp)
    ...
Run Code Online (Sandbox Code Playgroud)

显然它正在做以下事情:

  1. 将两个浮点数加载到寄存器xmm0和xmm1上
  2. 把它们放在堆栈中
  3. 从堆栈加载一个值(不是之前的xmm0)到xmm0
  4. 执行添加. …

c assembly x86-64 compiler-optimization llvm-codegen

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