4

我的问题类似于Can one unroll a loop when using 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。

4

2 回答 2

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);
}
于 2013-06-13T11:29:28.600 回答
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 的位选择循环。

于 2013-08-22T09:04:49.600 回答