производительность — в C ++ самый быстрый способ преобразовать положительное число в 1 и отрицательное в 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)) );
}

Вот как я пытался это сравнить. Каждый прогон показывает только 2 нс. Мой главный вопрос к сообществу:

Могу ли я на самом деле сделать это быстрее, используя правильный сдвиг и избегая условных переходов?

#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

Решение

Как я отмечал в своем комментарии, компилятор сделает эти оптимизации за вас. Вот минимальный компилируемый пример:

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

volatile это просто для того, чтобы оптимизация не «знала» значение a и вместо этого это заставляет это быть прочитанным.

Это было скомпилировано с командой g++ -O3 -S test.cpp и он создает файл с именем test.s

Внутри test.s находится сборка, сгенерированная компилятором (простите AT&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 что я написал (по крайней мере, в большинстве реализаций с целым числом без знака, подробности см. в комментариях).

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]