Саморазвертывающийся макрос-цикл в C / Stack Overflow

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

Поэтому я подумал вместо того, чтобы вручную раскручивать цикл с копией & Вставить Я мог использовать макросы, чтобы развернуть цикл во время компиляции, чтобы его можно было легко изменить позже.

То, что я представляю, выглядит примерно так:

#define LOOP_N_TIMES(N, CODE) <insert magic here>

Так что я могу заменить for (int i = 0; i < N, ++i) { do_stuff(); } с:

#define INNER_LOOP_COUNT 4
LOOP_N_TIMES(INNER_LOOP_COUNT, do_stuff();)

И он разворачивается, чтобы:

do_stuff(); do_stuff(); do_stuff(); do_stuff();

Поскольку препроцессор C все еще остается для меня загадкой, я понятия не имею, как это сделать, но я знаю, что это должно быть возможно, потому что Boost, кажется, имеет BOOST_PP_REPEAT макросы. К сожалению, я не могу использовать Boost для этого проекта.

11

Решение

Вы можете использовать шаблоны, чтобы развернуть.
Смотрите разборку для образца Живи на Годболте

введите описание изображения здесь

Но -funroll-loops имеет тот же эффект для этого образца.


Жить на Колиру

template <unsigned N> struct faux_unroll {
template <typename F> static void call(F const& f) {
f();
faux_unroll<N-1>::call(f);
}
};

template <> struct faux_unroll<0u> {
template <typename F> static void call(F const&) {}
};

#include <iostream>
#include <cstdlib>

int main() {
srand(time(0));

double r = 0;
faux_unroll<10>::call([&] { r += 1.0/rand(); });

std::cout << r;
}
21

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

Вы можете использовать препроцессор и поиграть в некоторые трюки с конкатенацией токенов и множественным расширением макросов, но вам придется жестко закодировать все возможности:

#define M_REPEAT_1(X) X
#define M_REPEAT_2(X) X X
#define M_REPEAT_3(X) X X X
#define M_REPEAT_4(X) X X X X
#define M_REPEAT_5(X) X M_REPEAT_4(X)
#define M_REPEAT_6(X) M_REPEAT_3(X) M_REPEAT_3(X)

#define M_EXPAND(...) __VA_ARGS__

#define M_REPEAT__(N, X) M_EXPAND(M_REPEAT_ ## N)(X)
#define M_REPEAT_(N, X) M_REPEAT__(N, X)
#define M_REPEAT(N, X) M_REPEAT_(M_EXPAND(N), X)

А затем разверните его так:

#define THREE 3

M_REPEAT(THREE, three();)
M_REPEAT(4, four();)
M_REPEAT(5, five();)
M_REPEAT(6, six();)

Этот метод требует буквальных чисел в качестве счета, вы не можете сделать что-то вроде этого:

#define COUNT (N + 1)

M_REPEAT(COUNT, stuff();)
10

Там нет стандартного способа сделать это.

Вот немного помешанный подход:

#define DO_THING printf("Shake it, Baby\n")
#define DO_THING_2 DO_THING; DO_THING
#define DO_THING_4 DO_THING_2; DO_THING_2
#define DO_THING_8 DO_THING_4; DO_THING_4
#define DO_THING_16 DO_THING_8; DO_THING_8
//And so on. Max loop size increases exponentially. But so does code size if you use them.

void do_thing_25_times(void){
//Binary for 25 is 11001
DO_THING_16;//ONE
DO_THING_8;//ONE
//ZERO
//ZERO
DO_THING;//ONE
}

Это не слишком много, чтобы попросить оптимизатора устранить мертвый код.
В таком случае:

#define DO_THING_N(N) if(((N)&1)!=0){DO_THING;}\
if(((N)&2)!=0){DO_THING_2;}\
if(((N)&4)!=0){DO_THING_4;}\
if(((N)&8)!=0){DO_THING_8;}\
if(((N)&16)!=0){DO_THING_16;}
5

Вы не можете использовать конструкцию #define для вычисления «unroll-count». Но с достаточным количеством макросов вы можете определить это:

#define LOOP1(a) a
#define LOOP2(a) a LOOP1(a)
#define LOOP3(a) a LOOP2(a)

#define LOOPN(n,a) LOOP##n(a)

int main(void)
{
LOOPN(3,printf("hello,world"););
}

Протестировано с VC2012

2

Ты не можешь писать реальный рекурсивные утверждения с макросами, и я уверен, что вы не можете иметь реальный итерации в макросах.

Однако вы можете взглянуть на порядок. Хотя он полностью построен на препроцессоре C, он «реализует» итеративные функции. На самом деле он может иметь до N итераций, где N — большое число. Я предполагаю, что это похоже на «рекурсивные» макросы. В любом случае, это такой пограничный случай, что немногие компиляторы поддерживают его (хотя GCC является одним из них).

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