可以在编译时将“for循环”作为编译器的优化来完成吗?

时间:2016-05-24 16:06:48

标签: c++ for-loop compiler-optimization template-meta-programming

前言
最近我发现了模板元编程有用的真实案例(可以在我的项目中用于提高效率) 例如:1。将给定大小的位的字包装成一个字节数组,2.字节转换为bitset转换)。
所有这些用例都有一些共同之处:它们在编译时展开for循环,这就是性能改进可能来自的地方。

示例:打包uint8_t数组,其中每个元素需要6位并使用8位:

inline void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut, size_t nOfBytes) {
  const int T = 3;
  const int N = nOfBytes / 8 * 6;
  for(int i = 0; i < N; ++i) {
    // case 0
    if(i % T == 0) pOut[i] = (pIn[i/3*4] << 2) + (pIn[i/3*4+1] >> 4);
    // case 1
    else if(i % T == 1) pOut[i] = (pIn[(i-1)/3*4+1] << 4) + (pIn[(i-1)/3*4+2] >> 2);
    // case 2
    else if(i % T == 2) pOut[i] = (pIn[(i-2)/3*4+2] << 6) + pIn[(i-2)/3*4+3];
    }
  }

我们可以发现,因为我们知道编译时结果位的数量, 我们可以用这样的方式用手动源代码替换循环:

pOut[0] = pIn[0] << 2 + pIn[1] >> 4;
pOut[1] = pIn[1] << 4 + pIn[2] >> 2;
pOut[2] = pIn[2] << 6 + pIn[1];
pOut[3] = pIn[4] << 2 + pIn[5] >> 4;
///....
pOut[23] = pIn[30] << 6 + pIn[31];

由于手动输入很无聊,我们可以将Template Meta编程用于此目的:

template <size_t nOfBytesIn>
void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) {
  static const size_t nOfBytesOut = nOfBytesIn / 8 * 6;
  LoopPack<nOfBytesOut-1>::pack_8bits_to6bits(pIn, pOut);
}
/* #### Switch statement */
template <size_t remainder, int I> struct SwitchStatementPack {
  static inline void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) { }
};

template <int I> struct SwitchStatementPack<0, I> {
  static inline void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) {
    pOut[I] = (pIn[I/3*4] << 2) + (pIn[I/3*4+1] >> 4);
  }
};
template <int I> struct SwitchStatementPack<1, I> {
  static inline void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) {
    pOut[I] = (pIn[(I-1)/3*4+1] << 4) + (pIn[(I-1)/3*4+2] >> 2);
  }
};
template <int I> struct SwitchStatementPack<2, I> {
  static inline void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) {
    pOut[I] = (pIn[(I-2)/3*4+2] << 6) + pIn[(I-2)/3*4+3];
  }
};
/*#### Loop*/
template <int I> struct LoopPack {
  static const size_t T = 3; // period
  static inline void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) {
    // recursive step
    SwitchStatementPack<I % T, I>::pack_8bits_to6bits(pIn, pOut);
    LoopPack<I-1>::pack_8bits_to6bits(pIn, pOut);
  }
};
// base case
template <> struct LoopPack<-1> {
  static void pack_8bits_to6bits(uint8_t* pIn, uint8_t* pOut) { }
};

问题:
如果我们可以指定使用TMP让编译器展开该循环,那么当前的编译器是否足够智能,可以自行完成这样的优化?

更新: 我更新了问题以使其更清晰,表明我感兴趣的是编译器是否可以提供这样的优化,而不是我们是否可以在编译时展开循环,问题应该是清楚的,因为我提供了解开循环的TMP解决方案 - 我知道如何展开它。

0 个答案:

没有答案