c++ c boost macros loop-unrolling

Macrobucle de autodesenrollado en C/C++



boost macros (5)

No hay una forma estándar de hacer esto.

Aquí hay un enfoque un poco loco:

#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 }

No es demasiado pedirle a un optimizador que elimine el código muerto. En ese caso:

#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;}

Actualmente estoy trabajando en un proyecto, donde cada ciclo cuenta. Al perfilar mi aplicación descubrí que la sobrecarga de algunos circuitos internos es bastante alta, ya que consisten en solo unas pocas instrucciones de la máquina. Además, el número de iteraciones en estos bucles se conoce en tiempo de compilación.

Así que pensé que en lugar de desenrollar manualmente el ciclo con copiar y pegar, podría usar macros para desenrollar el ciclo en el momento de la compilación para que pueda ser modificado fácilmente más adelante.

Lo que imagino es algo como esto:

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

De modo que puedo reemplazar for (int i = 0; i < N, ++i) { do_stuff(); } for (int i = 0; i < N, ++i) { do_stuff(); } con:

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

Y se desenrolla a sí mismo a:

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

Como el preprocesador C sigue siendo un misterio para mí la mayor parte del tiempo, no tengo idea de cómo lograrlo, pero sé que debe ser posible porque Boost parece tener un macros BOOST_PP_REPEAT . Desafortunadamente no puedo usar Boost para este proyecto.


No puede escribir declaraciones recursivas reales con macros y estoy bastante seguro de que tampoco puede tener una iteración real en las macros.

Sin embargo, puedes echar un vistazo a Order . Aunque está completamente construido sobre el preprocesador C, "implementa" funcionalidades de tipo iteración. En realidad, puede tener iteraciones de hasta N, donde N es un gran número. Supongo que es similar para las macros "recursivas". De todos modos, es un caso límite que pocos compiladores lo soportan (aunque GCC es uno de ellos).


No puede usar una construcción #define para calcular el "conteo de desenrollado". Pero con suficientes macros puedes definir esto:

#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");); }

Probado con VC2012


Puedes usar el pre-procesador y jugar algunos trucos con la concatenación de tokens y la expansión de macros múltiples, pero tienes que codificar todas las posibilidades:

#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)

Y luego expandirlo así:

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

Este método requiere números literales como conteos, no puedes hacer algo como esto:

#define COUNT (N + 1) M_REPEAT(COUNT, stuff();)


Puedes usar plantillas para desenrollar. Ver el desmontaje de la muestra Live on Godbolt

Pero -funroll-loops tiene el mismo efecto para esta muestra .

Live On Coliru

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; }