用于使用开关展开循环的C ++模板?

时间:2013-06-07 10:48:31

标签: c++ templates c++11 switch-statement template-meta-programming

我的问题与Can one unroll a loop when working with an integer template parameter?类似,但我想混合编译时和运行时。具体来说,我在编译时知道一个常量NBLOCK,我想在变量start_block上写一个开关,它只在运行时知道NBLOCK是交换机中的条目数。这是我使用宏的原因:

#define CASE_UNROLL(i_loop)         \
  case i_loop : \
    dst.blocks[i_loop+1] -= (load_unaligned_epi8(srcblock) != zero) & block1; \
    srcblock += sizeof(*srcblock);

  switch(start_block)
    {
      CASE_UNROLL(0);
#if NBLOCKS > 2
      CASE_UNROLL(1);
#endif
#if NBLOCKS > 3
      CASE_UNROLL(2);
#endif
#if NBLOCKS > 4
      CASE_UNROLL(3);
#endif
...
...
#if NBLOCKS > 15
      CASE_UNROLL(14);
#endif
#if NBLOCKS > 16
#error "Too many blocks"
#endif
    }

我发现它非常难看。特别是如果我想将界限从16提高到32.

我想知道是否可以使用一些模板元编程来编写它。困难的部分是出于性能原因,使用跳转表编译开关而不是嵌套条件序列是至关重要的。

请注意,问题与C++/C++11 - Switch statement for variadic templates?非常相似,但据我所知,此处提出的解决方案是使用混合编译/调试时初始化函数指针数组来删除开关。我不能在这里向王子支付一个功能。

如果需要一些讨厌的扩展,我正在与GCC合作。

2 个答案:

答案 0 :(得分:2)

你可以简单地将Boost.Preprocessor与BOOST_PP_REPEAT(COUNT, MACRO, DATA)

一起使用
#define APPLY_FUNC(INDEX, FUNC) FUNC(INDEX);

// ...

switch(start_block)
{
    BOOST_PP_REPEAT(NBLOCK, APPLY_FUNC, CASE_UNROLL);
}

应扩展为:

switch(start_block)
{
    CASE_UNROLL(0);
    CASE_UNROLL(1);
    CASE_UNROLL(2);
    // ...
    CASE_UNROLL(NBLOCK-1);
}

答案 1 :(得分:1)

基于模板的展开:

template<int N>
struct loopUnroller
{
  template<typename Operation>
  inline void operator(Operation& op) { op(); loopUnroller<N-1>(op); }
};

template<>
struct loopUnroller<0>
{
  template<typename Operation>
  inline void operator(Operation& op) { op(); }
};

loopUnroller<6>(Foo)的调用可能会被内联,但也包含对内联loopUnroller<5>(Foo)等的调用。每个级别都会额外调用Foo()

如果你的编译器拒绝深入内联16级,那就有一个简单的修复:

template<>
struct loopUnroller<16>
{
  template<typename Operation>
  inline void operator(Operation& op) { 
        op(); op(); op(); op();
        op(); op(); op(); op();
        op(); op(); op(); op();
        op(); op(); op(); op();
  }
};

具有对数复杂度:

template<int N>
struct loopUnroller
{
  template<typename Operation>
  inline void operator(Operation& op) { 
       loopUnroller<N/2>(op);
       loopUnroller<N/2>(op);
       if (N%1) { op(); } // Will be optimized out if N is even.
  }
};

动态复杂:

template<int L>
struct loopUnroller
{
  template<typename Operation>
  inline void operator(Operation& op, int i) {
     if (i & (1<<L)) {
       for(int j = 0; j != 1<<L; ++j)
       {
         op();
       }
     }
     loopUnroller<L-1>(op, i);
  }
};

for循环现在具有固定的运行时长度,使其可能展开。 所以你有一个长度为32,16,8,4,2和1的展开循环(假设没有特化),在运行时你可以根据i的位选择循环。