C / C ++中的自展开宏循环

时间:2015-01-30 08:14:30

标签: c++ c boost macros loop-unrolling

我目前正在开展一个项目,每个周期都很重要。在分析我的应用程序时,我发现一些内部循环的开销非常高,因为它们只包含一些机器指令。另外,这些循环中的迭代次数在编译时是已知的。

所以我想而不是用copy&手动展开循环。 paste我可以使用宏在编译时展开循环,以便以后可以轻松修改。

我的形象是这样的:

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

这样我就可以用{/ p>替换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。

5 个答案:

答案 0 :(得分:23)

您可以使用模板展开。 请参阅示例 Live on Godbolt

的反汇编

enter image description here

-funroll-loops has the same effect for this sample


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

答案 1 :(得分:11)

您可以使用预处理器并使用令牌连接和多个宏扩展来玩一些技巧,但您必须对所有可能性进行硬编码:

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

答案 2 :(得分:7)

没有标准的方法可以做到这一点。

这是一种略微疯狂的做法:

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

答案 3 :(得分:3)

您不能使用#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测试

答案 4 :(得分:0)

您无法使用宏编写真实递归语句,而且我非常确定您无法在宏中进行真正的迭代

但是你可以看一下Order。虽然它完全是在C预处理器的顶层构建的,但它实现了&#34;类似迭代的功能。它实际上可以有多达N次迭代,其中N是一些大数。我猜测它类似于&#34;递归&#34;宏。无论如何,这是一个边缘情况,很少有编译器支持它(GCC就是其中之一)。