19

私は現在、すべてのサイクルが重要なプロジェクトに取り組んでいます。アプリケーションのプロファイリング中に、一部の内部ループのオーバーヘッドが非常に高いことがわかりました。これは、それらがほんの数個のマシン命令で構成されているためです。さらに、これらのループの反復回数はコンパイル時にわかります。

そこで、コピー & ペーストで手動でループを展開する代わりに、後で簡単に変更できるように、マクロを使用してコンパイル時にループを展開できると考えました。

私がイメージするのは次のようなものです:

#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 を使用できません。

4

5 に答える 5

29

テンプレートを使用して展開できます。サンプルLive on Godboltの分解を参照してください

ここに画像の説明を入力

しかし-funroll-loops、このサンプルでは同じ効果があります


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;
}
于 2015-01-30T08:58:04.357 に答える
14

プリプロセッサを使用して、トークンの連結と複数のマクロ展開でいくつかのトリックを実行できますが、すべての可能性をハードコーディングする必要があります。

#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();)
于 2015-01-30T08:40:43.937 に答える
11

これを行う標準的な方法はありません。

これは少しおかしなアプローチです:

#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;}
于 2015-01-30T08:34:31.900 に答える
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でテスト済み

于 2015-01-30T08:42:33.580 に答える