在c ++中,将正数转换为1并将负数转换为0的最快方法

时间:2017-07-20 02:02:24

标签: c++ performance microbenchmark

我正在编写这个代码来制作烛台图表,如果当天的开盘价大于收盘价,我想要一个红色的盒子。如果收盘价高于开盘价,我还希望盒子为绿色。

if(open > close) {
    boxColor = red;
} else {
    boxColor = green;
}

为此,伪代码比英文句子更容易。

所以我先编写了这段代码,然后尝试对其进行基准测试,但我不知道如何获得有意义的结果。

for(int i = 0; i < history.get().close.size(); i++) {
    auto open = history->open[i];
    auto close = history->close[i];
    int red = ((int)close - (int)open) >> ((int)sizeof(close) * 8);
    int green = ((int)open - (int)close) >> ((int)sizeof(close) * 8);
    gl::color(red,green,0);
    gl::drawSolidRect( Rectf(vec2(i - 1, open), vec2(i + 1, close)) );
}

这就是我尝试对它进行基准测试的方法。每次运行只显示2ns。我对社区的主要问题是:

通过使用右移并避免条件分支,我能真正加快速度吗?

#include <benchmark/reporter.h>

static void BM_red_noWork(benchmark::State& state) {
    double open = (double)rand() / RAND_MAX;
    double close = (double)rand() / RAND_MAX;
    while (state.KeepRunning()) {
    }
}
BENCHMARK(BM_red_noWork);

static void BM_red_fast_work(benchmark::State& state) {
    double open = (double)rand() / RAND_MAX;
    double close = (double)rand() / RAND_MAX;
    while (state.KeepRunning()) {
        int red = ((int)open - (int)close) >> sizeof(int) - 1;
    }
}
BENCHMARK(BM_red_fast_work);

static void BM_red_slow_work(benchmark::State& state) {
    double open = (double)rand() / RAND_MAX;
    double close = (double)rand() / RAND_MAX;
    while (state.KeepRunning()) {
        int red = open > close ? 0 : 1;
    }
}
BENCHMARK(BM_red_slow_work);

谢谢!

1 个答案:

答案 0 :(得分:4)

正如我在评论中所述,编译器将为您进行这些优化。这是一个最小的可编译示例:

int main() {
  volatile int a = 42;
  if (a <= 0) {
    return 0;
  } else {
    return 1;
  }
}

volatile只是为了防止&#34;了解&#34; a的值,而是强制它被读取。

这是使用命令g++ -O3 -S test.cpp编译的,它生成一个名为test.s

的文件

Inside test.s是编译器生成的程序集(原谅AT&amp; T语法):

movl    $42, -4(%rsp)
movl    -4(%rsp), %eax
testl   %eax, %eax
setg    %al
movzbl  %al, %eax
ret

如您所见,它是无分支的。如果数字为testl,它使用<= 0设置标志,然后使用setg读取该值,将其移回正确的寄存器,最后返回。

应该注意,这是根据您的代码改编的。写这个的更好方法就是:

int main() {
  volatile int a = 42;
  return a > 0;
}

它也会生成相同的程序集。

这可能比您在C ++中直接编写的任何可读内容都要好。例如你的代码(希望纠正位算术错误):

int main() {
  volatile int a = 42;
  return ~(a >> (sizeof(int) * CHAR_BIT - 1)) & 1;
}

编译为:

movl    $42, -4(%rsp)
movl    -4(%rsp), %eax
notl    %eax
shrl    $31, %eax
ret

这确实是非常小一些。但它并没有明显加快。特别是当你旁边有一个GL电话时。我宁愿花1-3个额外的周期来获得可读的代码,而不是不知不觉地想知道我的同事(或者6个月前的我,这基本上是同一件事)是做什么的。

编辑:我应该注意到编译器还优化了我写的位运算,因为我写的不如我所能写的那么好。程序集实际上是:(~a) >> 31,它等同于我编写的~(a >> 31) & 1(至少在大多数实现中使用无符号整数,请参阅注释以获取详细信息)。