Я пишу этот код, чтобы сделать график свечи, и я хочу красную рамку, если цена открытия за день больше, чем цена закрытия. Я также хочу, чтобы поле было зеленым, если цена закрытия выше цены открытия.
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);
Спасибо!
Как я отмечал в своем комментарии, компилятор сделает эти оптимизации за вас. Вот минимальный компилируемый пример:
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
что я написал (по крайней мере, в большинстве реализаций с целым числом без знака, подробности см. в комментариях).
Других решений пока нет …