我的问题与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合作。
答案 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的位选择循环。