标签: micro-optimization

分支?:运营商?

对于现代硬件上的典型现代编译器,? :运算符是否会导致影响指令流水线的分支?

换句话说哪个更快,调用两个案例以避免可能的分支:

bool testVar = someValue(); // Used later.
purge(white);
purge(black);
Run Code Online (Sandbox Code Playgroud)

或选择实际需要清除的一个并且只与操作员一起做?::

bool testVar = someValue();
purge(testVar ? white : black);
Run Code Online (Sandbox Code Playgroud)

我知道你不知道purge()需要多长时间,但我只是在这里问一个关于我是否想要两次调用purge()来避免代码中可能的分支的一般性问题.

我意识到这是一个非常小的优化,并没有真正的区别,但仍然想知道.我希望?:不会导致分支,但希望确保我的理解是正确的.

c++ hardware compiler-construction micro-optimization

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

在计算中使用bool来避免分支

这是我想出的一点微优化好奇心:

struct Timer {
    bool running{false};
    int ticks{0};

    void step_versionOne(int mStepSize) {
        if(running) ticks += mStepSize;
    }

    void step_versionTwo(int mStepSize) {
        ticks += mStepSize * static_cast<int>(running);
    }
};
Run Code Online (Sandbox Code Playgroud)

这两种方法似乎实际上做了同样的事情.第二个版本是否避免了分支(因此,比第一个版本更快),或者是否有任何编译器能够进行这种优化-O3

c++ optimization boolean micro-optimization c++11

8
推荐指数
1
解决办法
893
查看次数

两个补码的长整数

我想用英特尔I64汇编程序做一些长整数数学运算(128位),需要创建一个2的补码.让我们说我的正面价值在于RDX:RAX.

2的补码是通过"翻转位并加1"来完成的.所以最天真的实现是(4条指令和14个字节的代码):

  NOT RAX
  NOT RDX
  ADD RAX,1   ; Can't use INC, it doesn't set Carry
  ADC RDX,0
Run Code Online (Sandbox Code Playgroud)

当我在RAX而不是NOT上使用NEG指令时,它对我来说是"+1"但是Carry是错误的,当RAX为零时NEG RAX清除了Carry,但是我需要携带JUST IN THIS CASE.所以下一个最好的方法可能是(4条指令和11个字节的代码):

  NOT RDX
  NEG RAX
  CMC
  ADC RDX,0                  ; fixed, thanks lurker
Run Code Online (Sandbox Code Playgroud)

还有4条说明.但是不是加+1,我可以减去-1,因为SBB将Carry-Bit加到减数上,当Carry清零时我会加+1.所以我的下一个最好的尝试是这个,有3个指令和10个字节的代码:

   NOT RDX
   NEG RAX
   SBB RDX,-1
Run Code Online (Sandbox Code Playgroud)

从我冗长的文字中可以看出,这一点并不明显.是否有一种更好,更易理解的方法来在汇编程序中进行级联2的补码?

assembly x86-64 micro-optimization twos-complement

8
推荐指数
1
解决办法
781
查看次数

为什么`-1*x`比`-x`快?为什么?

使用此代码:

include Benchmark
n = 10**8
r = []
Benchmark.benchmark(" "*7 + CAPTION, 7, FORMAT, ">avg:", ">total:") do |b|
  a = 1

  r << b.report("Benchmark -1:")   { (n).times do
    -1 * a
  end }

  r << b.report("Benchmark - :")   { (n).times do
    -a
  end }

  [(r.sum{|e| e }) / 2, r.sum{|e| e }]
end
Run Code Online (Sandbox Code Playgroud)

我得到了这个结果ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]:

                     user     system      total        real
Benchmark -1:  4.930000   0.000000   4.930000 (  4.938359)
Benchmark - :  5.650000   0.000000   5.650000 ( …
Run Code Online (Sandbox Code Playgroud)

ruby micro-optimization

8
推荐指数
1
解决办法
126
查看次数

在长模式下使用64/32位寄存器可能会有任何处罚吗?

可能这甚至都不是微观但纳米优化,但主题让我感兴趣,我想知道在长模式下使用非本机寄存器大小时是否存在任何惩罚?

我从各种来源了解到,部分寄存器更新(比如ax代替eax)会导致eflags停顿并降低性能.但我不确定长模式.对于此处理器操作模式,哪个寄存器大小被视为原生?x86-64仍然是x86架构的扩展,因此我相信32位仍然是原生的.还是我错了?

例如,像

sub eax, r14d
Run Code Online (Sandbox Code Playgroud)

要么

sub rax, r14
Run Code Online (Sandbox Code Playgroud)

具有相同的尺寸,但在使用其中任何一种时可能会有任何处罚吗?在如下连续指令中混合寄存器大小时可能会有任何处罚吗?(假设高dword在所有情况下均为零)

sub ecx, eax
sub r14, rax
Run Code Online (Sandbox Code Playgroud)

optimization x86 assembly micro-optimization

8
推荐指数
1
解决办法
186
查看次数

充分利用kaby湖上的管道

(此处跟进代码复习问题,包含此循环上下文的更多详细信息.)


环境:

  • Windows 7 x64
  • VS 2017社区
  • 在Intel i7700k(kaby lake)上定位x64代码

我没有写很多汇编程序代码,当我这么做时,它要么足够短,要么足够简单,以至于我不必担心压缩它的最大数量.我的更复杂的代码通常用C编写,我让编译器的优化器担心延迟,代码对齐等.

但是在我目前的项目中,MSVC的优化器在关键路径中的代码上做得非常糟糕.所以...

我还没有找到一个好的工具,可以对x64汇编代码进行静态或运行时分析,以便消除停顿,改善延迟等等.我所拥有的只是VS分析器,它告诉我(大致)哪些指令花了最多的时间.墙上的时钟告诉我最近的变化是否使事情变得更好或更糟.

作为替代方案,我一直在通过Agner的文档进行操作,希望能从我的代码中挤出一些更多的信息.问题是,在你理解了所有这些工作之前,很难理解他的任何工作.但它的一部分是有意义的,我正在尝试应用我学到的东西.

记住这一点,这里是我最内层循环的核心(不足为奇)是VS剖析器说我花费的时间:

nottop:

vpminub ymm2, ymm2, ymm3 ; reset out of range values
vpsubb  ymm2, ymm2, ymm0 ; take a step

top:
vptest  ymm2, ymm1       ; check for out of range values
jnz nottop

; Outer loop that does some math, does a "vpsubb ymm2, ymm2, ymm0",
; and eventually jumps back to top
Run Code Online (Sandbox Code Playgroud)

是的,这几乎是一个依赖链的教科书示例:这个紧密的小循环中的每个指令都取决于前一个操作的结果.这意味着没有并行性,这意味着我没有充分利用处理器.

受Agner的"优化汇编程序"文档的启发,我想出了一种方法(希望)允许我一次做2个操作,所以我可以有一个管道更新ymm2和另一个更新(比如说)ymm8.

虽然这是一个非平凡的变化,所以在我开始撕掉所有东西之前,我想知道它是否可能有所帮助.看看Agner的kaby lake(我的目标)的"指令表",我看到:

        uops
        each
        port    Latency
pminub …
Run Code Online (Sandbox Code Playgroud)

performance assembly x86-64 micro-optimization avx2

8
推荐指数
1
解决办法
275
查看次数

最快的轮询循环 - 如何修剪 1 个 CPU 周期?

在 ARM Cortex M3(类似于 STM32F101)上的实时应用程序¹中,我需要在尽可能紧密的循环中轮询一些内部外设寄存器直到它为零。我使用位带来访问适当的位。(工作)C 代码是

while (*(volatile uint32_t*)kMyBit != 0);
Run Code Online (Sandbox Code Playgroud)

该代码被复制到片上可执行 RAM 中。经过一些手动优化²,轮询循环下降到以下,我将³计时为 6 个周期:

0x00600200 681A      LDR      r2,[r3,#0x00]
0x00600202 2A00      CMP      r2,#0x00
0x00600204 D1FC      BNE      0x00600200
Run Code Online (Sandbox Code Playgroud)

如何降低投票的不确定性?5 个周期的循环符合我的目标:在它变为零后尽可能接近 15.5 个周期对同一位进行采样。

我的规范要求可靠地检测至少 6.5 个 CPU 时钟周期的低脉冲;如果持续时间少于 12.5 个周期,则可靠地将其归类为短;如果它持续超过 18.5 个周期,就可以可靠地对其进行分类。脉冲与 CPU 时钟没有确定的相位关系,这是我唯一准确的时序参考。这需要最多 5 个时钟的轮询循环。实际上,我正在模拟在几十年前的 8 位 CPU 上运行的代码,该 CPU 可以以 5 个时钟周期进行轮询,而这已成为规范。


我试图通过在循环之前插入 NOP 来抵消代码对齐,在我尝试过的许多变体中,但从未观察到任何变化。

我试图反转 CMP 和 LDR,但仍然得到 6 个周期:

0x00600200 681A      LDR      r2,[r3,#0x00]
; we loop here
0x00600202 2A00      CMP      r2,#0x00
0x00600204 681A      LDR      r2,[r3,#0x00]
0x00600206 …
Run Code Online (Sandbox Code Playgroud)

assembly cortex-m3 micro-optimization

8
推荐指数
1
解决办法
532
查看次数

什么是计算 floor(log(m / n)) 的有效方法,其中 m 和 n 是整数?

基本上,正如标题所说。我想知道一种计算方法floor(log2(x / y)),其中xy是非零无符号机器整数,在尽可能少的周期内(尽可能避免使用分支,内存带宽,除法等在微小部分中很昂贵的东西像这样的代码)。这里需要准确的(整数)答案。我在考虑如何通过有效地计算来优化Adaptive Shivers Sort的外循环,因为它需要计算floor(log2(r / c)),其中rc算法的运行长度和元参数;假设x <= y适用于此类离线版本的解决方案,其中c 被选择为等于输入的长度,但通用解决方案在其他设置中可能有用。

您可以假设使用PopCountand CountLeadingZeros/CountTrailingZeros、常见的 SSE 样式指令,甚至浮点计算——但它需要是处理器可以在短短几个周期内完成的事情。

c optimization assembly micro-optimization

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

在 x86-64 asm 中:如果源操作数是两个立即数,是否有一种方法可以优化两个相邻的 32 位存储/写入内存?

有没有优化此代码(x86-64)的好方法?

mov dword ptr[rsp], 0;
mov dword ptr[rsp+4], 0
Run Code Online (Sandbox Code Playgroud)

其中立即数可以是任何值,不一定是零,但在这种情况下总是立即数。

原来的那对店还慢吗?硬件中的写入组合和 ?ops 的并行操作可能会使一切变得非常快?我想知道有没有问题可以解决。

我正在考虑类似的事情(不知道以下说明是否存在)

mov  qword ptr[rsp], 0
Run Code Online (Sandbox Code Playgroud)

或者

mov  eax, 0;
mov  qword ptr[rsp], rax    ; assuming we can spare a register, a bad idea to corrupt one though
Run Code Online (Sandbox Code Playgroud)

optimization assembly x86-64 micro-optimization

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

使用内存映射文件在 C++ 中解析二进制文件太慢

我正在尝试按整数解析二进制文件,以检查整数值是否满足某个条件,但循环非常慢。

此外,我发现memory-mapped files 是将文件快速读入内存的最快方法,因此我使用了以下Boost基于代码的代码:

unsigned long long int get_file_size(const char *file_path) {
    const filesystem::path file{file_path};
    const auto generic_path = file.generic_path();
    return filesystem::file_size(generic_path);
}

boost::iostreams::mapped_file_source read_bytes(const char *file_path,
                                         const unsigned long long int offset,
                                         const unsigned long long int length) {
    boost::iostreams::mapped_file_params parameters;
    parameters.path = file_path;
    parameters.length = static_cast<size_t>(length);
    parameters.flags = boost::iostreams::mapped_file::mapmode::readonly;
    parameters.offset = static_cast<boost::iostreams::stream_offset>(offset);

    boost::iostreams::mapped_file_source file;

    file.open(parameters);
    return file;
}

boost::iostreams::mapped_file_source read_bytes(const char *file_path) {
    const auto file_size = get_file_size(file_path);
    const auto mapped_file_source = read_bytes(file_path, 0, …
Run Code Online (Sandbox Code Playgroud)

c++ optimization memory-mapped-files micro-optimization

8
推荐指数
1
解决办法
185
查看次数