在 C++ 中,将正数转换为 1 并将负数转换为 0 的最快方法
in c++, the fastest way to convert a positive number to 1 and negative to 0
我正在编写此代码来制作烛台图表,如果当天的开盘价高于收盘价,我想要一个红色框。如果收盘价高于开盘价,我还希望方框为绿色。
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);
谢谢!
正如我在评论中所述,编译器将为您进行这些优化。这是一个最小的可编译示例:
int main() {
volatile int a = 42;
if (a <= 0) {
return 0;
} else {
return 1;
}
}
volatile
只是为了防止对 "knowing" a
的值进行优化,而是强制读取它。
这是用命令 g++ -O3 -S test.cpp
编译的,它生成一个名为 test.s
的文件
里面test.s是编译器生成的程序集(请原谅AT&T语法):
movl , -4(%rsp)
movl -4(%rsp), %eax
testl %eax, %eax
setg %al
movzbl %al, %eax
ret
如您所见,它是无分支的。如果数字是 <= 0
,它使用 testl
设置一个标志,然后使用 setg
读取该值,将其移回正确的寄存器,最后是 returns。
请注意,这是根据您的代码改编的。一个更好的写法是:
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 , -4(%rsp)
movl -4(%rsp), %eax
notl %eax
shrl , %eax
ret
确实很小,非常小。但它并没有明显更快。尤其是当您旁边有一个 GL 调用时。我宁愿多花 1-3 个周期来获得可读代码,也不愿绞尽脑汁想知道我的同事(或 6 个月前的我,这基本上是同一件事)做了什么。
编辑:我应该指出,编译器还优化了我编写的位算法,因为我写得不如我能写的好。程序集实际上是: (~a) >> 31
相当于我写的 ~(a >> 31) & 1
(至少在大多数使用无符号整数的实现中,详情请参阅评论)。
我正在编写此代码来制作烛台图表,如果当天的开盘价高于收盘价,我想要一个红色框。如果收盘价高于开盘价,我还希望方框为绿色。
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);
谢谢!
正如我在评论中所述,编译器将为您进行这些优化。这是一个最小的可编译示例:
int main() {
volatile int a = 42;
if (a <= 0) {
return 0;
} else {
return 1;
}
}
volatile
只是为了防止对 "knowing" a
的值进行优化,而是强制读取它。
这是用命令 g++ -O3 -S test.cpp
编译的,它生成一个名为 test.s
里面test.s是编译器生成的程序集(请原谅AT&T语法):
movl , -4(%rsp)
movl -4(%rsp), %eax
testl %eax, %eax
setg %al
movzbl %al, %eax
ret
如您所见,它是无分支的。如果数字是 <= 0
,它使用 testl
设置一个标志,然后使用 setg
读取该值,将其移回正确的寄存器,最后是 returns。
请注意,这是根据您的代码改编的。一个更好的写法是:
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 , -4(%rsp)
movl -4(%rsp), %eax
notl %eax
shrl , %eax
ret
确实很小,非常小。但它并没有明显更快。尤其是当您旁边有一个 GL 调用时。我宁愿多花 1-3 个周期来获得可读代码,也不愿绞尽脑汁想知道我的同事(或 6 个月前的我,这基本上是同一件事)做了什么。
编辑:我应该指出,编译器还优化了我编写的位算法,因为我写得不如我能写的好。程序集实际上是: (~a) >> 31
相当于我写的 ~(a >> 31) & 1
(至少在大多数使用无符号整数的实现中,详情请参阅评论)。