我一直想知道 C++ 中是否有任何标准,允许您根据数组或其他一些预定义的数据集将静态表达式扩展为带有内部数据的 N 行代码。
示例: 如果需要对 5 个相邻的矩阵单元求和,我会编写这样的函数:
int sumPlus(const vector<vector<int>>& matrix, int atX, int atY) {
static const pair<int,int> positions[5] = {{0,0},{0,1},{0,1},{-1,0},{0,-1}};
int sum = 0;
for(int i = 0;i < 5;++i) {
sum += matrix[atY + positions[i].first][atX + positions[i].second];
}
return sum;
}
但据我所知,它将被转换为带有跳转的代码(jmp 或 jle 指令),这是缓慢的代码。所以问题本身:是否有任何标准可以提供语法,如下所示:
int sumPlus(const vector<vector<int>>& matrix, int atX, int atY) {
expression_array pair<int,int> positions[5] = {{0,0},{0,1},{0,1},{-1,0},{0,-1}};
int sum = 0;
expression<positions : position>(sum += matrix[atY + position.first][atX + position.second]);
return sum;
}
稍后将由编译器扩展为:
int sumPlus(const vector<vector<int>>& matrix, int atX, int atY) {
int sum = 0;
sum += matrix[atY + 0][atX + 0];
sum += matrix[atY + 1][atX + 0];
sum += matrix[atY + (-1)][atX + 0];
sum += matrix[atY + 0][atX + 1];
sum += matrix[atY + 0][atX + (-1)];
return sum;
}
两个版本产生相同的组件(当等效时),请参阅https://godbolt.org/z/7Kf59r6o8
您缺少的优化称为“循环展开”。编译器已经完全或部分展开循环,这意味着诸如
之类的代码for(int i = 0; i < 5; ++i) { /* do stuff */; }
...自动翻译成
/* do stuff where i = 0 */;
/* do stuff where i = 1 */;
/* do stuff where i = 2 */;
/* do stuff where i = 3 */;
/* do stuff where i = 4 */;
当然,只有编译器认为值得这样做时才会发生,这取决于启发式。 Clang 在循环展开方面比 GCC 更具攻击性。 只需让编译器弄清楚即可,或者,如果您已完成分析并知道编译器做出了错误的决定,您可以通过强制循环展开来改进它:
#pragma clang loop unroll_count(2)
就我个人而言,我会写
// I have faith that this loop gets unrolled.
for (int i = -1; i <= 1; ++i) {
// I have faith that this loop gets unrolled too.
for (int j = -1; j <= 1; ++j) {
// I have faith that this if statement is optimized out.
if (i == 0 || j == 0) {
sum += matrix[atY + i][atX + j];
}
}
}
expression<positions : position>(sum += matrix[atY + position.first][atX + > position.second]);
为了沿着这些思路得到一些东西,你可以创建一些由
std::index_sequence
和折叠表达式组成的怪物,但在这里不值得。
最后,它会看起来像
int sum(const vector<vector<int>>& matrix, int atX, int atY) {
static constexpr int positions[5][2] = {{0,0},{0,1},{1,0},{-1,0},{0,-1}};
return [atX, atY]<std::size_t... I>(std::index_sequence<I...>) {
return (matrix[atY + position[I][0]][atX + position[I][1]] + ... + 0);
}(std::make_index_sequence<I...>{});
}