Оптимизация циклов и если

У меня процедура выглядит так:

void Process1(unsigned char* data)
{

}
void Process2(unsigned char* data)
{

}
void Process3(unsigned char* data)
{

}

#define FLAG1 (1 << 1)
#define FLAG2 (1 << 2)
#define FLAG3 (1 << 3)

void ProcessData(unsigned char* data, unsigned int bytes, unsigned int flags)
{
bool b1 = !!(flags & FLAG1);
bool b2 = !!(flags & FLAG2);
bool b3 = !!(flags & FLAG3);
for (unsigned int i = 0; i < bytes; i ++)
{
if (b1) Process1(data + i);
if (b2) Process2(data + i);
if (b3) Process3(data + i);
}
}

Как это выглядит, flags & FLAG1 A.K.A b1 не будет меняться во всех циклах. Но мы все равно должны делать ветку в каждом цикле. Мне просто интересно, есть ли способ избежать этой ненужной ветви динамически.

Вот демонстрация решения Лундина.

#include <windows.h>
#include <stdio.h>
#include <time.h>
LARGE_INTEGER ls, le, ll;
#define START_CLOCK() QueryPerformanceCounter(&ls)
#define END_CLOCK() printf ("%.0lf ns\n", (QueryPerformanceCounter(&le), ((double)le.QuadPart - ls.QuadPart) / ll.QuadPart * 1000000));void Process1(unsigned char* data)
{
(*data)++;
}
void Process2(unsigned char* data)
{
(*data)--;
}
void Process3(unsigned char* data)
{
(*data) *= (*data);
}

#define FLAG1 (1 << 1)
#define FLAG2 (1 << 2)
#define FLAG3 (1 << 3)

void ProcessData(unsigned char* data, unsigned int bytes, unsigned int flags)
{
bool b1 = !!(flags & FLAG1);
bool b2 = !!(flags & FLAG2);
bool b3 = !!(flags & FLAG3);
for (unsigned int i = 0; i < bytes; i ++)
{
if (b1) Process1(data + i);
if (b2) Process2(data + i);
if (b3) Process3(data + i);
}
}typedef void (*proc_t)(unsigned char*);

inline static void do_nothing (unsigned char* ptr)
{
(void)ptr;
}

void ProcessData_x(unsigned char* data, unsigned int bytes, unsigned int flags)
{
bool b1 = (flags & FLAG1) != 0;  // de-obfuscate the boolean logic
bool b2 = (flags & FLAG2) != 0;
bool b3 = (flags & FLAG3) != 0;

proc_t p1 = b1 ? Process1 : do_nothing;
proc_t p2 = b2 ? Process2 : do_nothing;
proc_t p3 = b3 ? Process3 : do_nothing;

for (unsigned int i = 0; i<bytes; i++)
{
p1(data + i);
p2(data + i);
p3(data + i);
}
}

int main()
{
if (!QueryPerformanceFrequency(&ll)) return 1;

const unsigned int bytes = 0xffff;
srand((unsigned int)time(NULL));
unsigned int flags = rand() & 0x7;
unsigned char* data = new unsigned char[bytes];
for (unsigned int i = 0; i < bytes; i++)
{
data[i] = (unsigned char)(rand() & 0xff);
}

START_CLOCK();

ProcessData(data, bytes, flags);

END_CLOCK();

START_CLOCK();

ProcessData_x(data, bytes, flags);

END_CLOCK();
}

вот вывод:

134 ns
272 ns

Я запускал его несколько раз, но неожиданно он стоит еще больше времени 🙁 .. он также скомпилирован ‘vs2010 Release x86’

2

Решение

Прежде всего, не имеет смысла говорить об оптимизации без особой системы …

При этом я бы оптимизировал ветки следующим образом:

typedef void (*proc_t)(unsigned char*);

inline static void do_nothing (unsigned char* ptr)
{
(void)ptr;
}

...

void ProcessData(unsigned char* data, unsigned int bytes, unsigned int flags)
{
bool b1 = (flags & FLAG1) != 0;  // de-obfuscate the boolean logic
bool b2 = (flags & FLAG2) != 0;
bool b3 = (flags & FLAG3) != 0;

proc_t p1 = b1 ? Process1 : do_nothing;
proc_t p2 = b2 ? Process2 : do_nothing;
proc_t p3 = b3 ? Process3 : do_nothing;

for (unsigned int i = 0; i<bytes; i++)
{
p1(data + i);
p2(data + i);
p3(data + i);
}
}
4

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

Решение C ++. Аналогично ответу Лундина, но без вызова пустой функции. Я не уверен, что это влияет на производительность, главное преимущество в том, что вам не нужно вручную перечислять все вызовы процессов в цикле. Если вы хотите микрооптимизировать или хотите использовать c, вы можете использовать массив в стеке, но вам придется самостоятельно управлять некоторыми счетчиками.

typedef void (*proc_t)(unsigned char*);
std::vector<proc_t> processes;
if (b1) processes.push_back(Process1);
if (b2) processes.push_back(Process2);
if (b3) processes.push_back(Process3);

for(auto p : processes)
for (unsigned int i = 0; i<bytes; i++)
p(data + i);
3

    bool b1 = !!(flags & FLAG1);
bool b2 = !!(flags & FLAG2);
bool b3 = !!(flags & FLAG3);int caseNow=SelectCaseAtOnce(b1,b2,b3);

if(caseNow==0)
for (unsigned int i = 0; i < bytes; i ++)
{
Process1(data + i);

}
else if(caseNow==1)
for (unsigned int i = 0; i < bytes; i ++)
{

Process2(data + i);

}
else if(caseNow==2)
for (unsigned int i = 0; i < bytes; i ++)
{

Process3(data + i);
}
else if(caseNow==3)
for (unsigned int i = 0; i < bytes; i ++)
{
Process1(data + i);
Process2(data + i);

}
if(caseNow==4)
for (unsigned int i = 0; i < bytes; i ++)
{
Process1(data + i);

Process3(data + i);
}
else if(caseNow==5)
for (unsigned int i = 0; i < bytes; i ++)
{

Process2(data + i);
Process3(data + i);
}
else if(caseNow==6)
for (unsigned int i = 0; i < bytes; i ++)
{
Process1(data + i);
Process2(data + i);
Process3(data + i);
}
else {}
0

Вот еще одно решение с использованием шаблонов — таким образом вы получите оптимизированную версию внутреннего цикла для каждого варианта. Если функции ProcessN достаточно короткие / простые, чтобы их можно было встроить, это может быть полезной оптимизацией.

#include <tuple>
#include <map>
#include <utility>

using namespace std;

inline void Process1(unsigned char* data) {}
inline void Process2(unsigned char* data) {}
inline void Process3(unsigned char* data) {}

#define FLAG1 (1 << 1)
#define FLAG2 (1 << 2)
#define FLAG3 (1 << 3)

template <bool b1, bool b2, bool b3>
void ProcessData(unsigned char* data, unsigned int bytes) {
for (unsigned int i = 0; i < bytes; i++) {
if (b1) Process1(data + i);
if (b2) Process2(data + i);
if (b3) Process3(data + i);
}
}

void ProcessData(unsigned char* data, unsigned int bytes, unsigned int flags) {
typedef void (*ProcessFunc)(unsigned char*, unsigned int bytes);
static map<tuple<bool, bool, bool>, ProcessFunc> funcs{
{make_tuple(false, false, false), ProcessData<false, false, false>},
{make_tuple(false, false, true), ProcessData<false, false, true>},
{make_tuple(false, true, false), ProcessData<false, true, false>},
{make_tuple(false, true, true), ProcessData<false, true, true>},
{make_tuple(true, false, false), ProcessData<true, false, false>},
{make_tuple(true, false, true), ProcessData<true, false, true>},
{make_tuple(true, true, false), ProcessData<true, true, false>},
{make_tuple(true, true, true), ProcessData<true, true, true>}};

bool b1 = !!(flags & FLAG1);
bool b2 = !!(flags & FLAG2);
bool b3 = !!(flags & FLAG3);
funcs[make_tuple(b1, b2, b3)](data, bytes);
}
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector