编译器是否检测到多次计算的变量?
Does compiler detect variables that are calculated more than one time?
我的问题涉及以下构造:
uint16_t flag = 0x1f;
while (curCase) {
if ((curCase->i == i) && (curCase->j == j)) flag ^= 0x10;
if ((curCase->i == i-1) && (curCase->j == j)) flag ^= 0x01;
if ((curCase->i == i) && (curCase->j == j+1)) flag ^= 0x02;
if ((curCase->i == i+1) && (curCase->j == j)) flag ^= 0x04;
if ((curCase->i == i) && (curCase->j == j-1)) flag ^= 0x08;
if (!(flag ^ 0xf0)) return; // if none of the neighbors are candidates (ignore first bits)
curCase = curCase->suiv;
}
curCase只是链表的一个元素,有2个int属性i和j,下一个元素suiv;
知道我的程序在单线程上运行,因此指向的值在 while 的迭代期间不能改变;
我想知道编译器是否足够聪明(我想答案是肯定的,但我想确定)来检测代码是否需要计算指针 curCase->i 和 curCase-> j 5 次,我本可以在循环开始时声明 2 个临时指针并分配 curCase->i 和 curCase->j;
更普遍的是,如果它检测到多次计算的变量并因此进行优化。
我已经用不同的列表大小对这两个选项进行了测试,但我没有发现一个比另一个快得多。
对于这种大小的代码,很难找到基于编译器优化的性能差异(只是评论)。我 认为 这完全取决于编译器实现,它可以完成,但这取决于对范围的分析。
为了检验假设,我对您的代码进行了一些调整以使用 Compile Explorer,如下所示:
struct cur { int i; int j; void * suiv;} typedef cur;
int i;
int j;
void function(cur *curCase) {
int flag = 0x1f;
while (curCase) {
if ((curCase->i == i) && (curCase->j == j)) flag ^= 0x10;
if ((curCase->i == i-1) && (curCase->j == j)) flag ^= 0x01;
if ((curCase->i == i) && (curCase->j == j+1)) flag ^= 0x02;
if ((curCase->i == i+1) && (curCase->j == j)) flag ^= 0x04;
if ((curCase->i == i) && (curCase->j == j-1)) flag ^= 0x08;
if (!(flag ^ 0xf0)) return; // if none of the neighbors are candidates (ignore first bits)
curCase = curCase->suiv;
}
}
而且 gcc 和 CLang 的汇编代码似乎没有隔离您提到的指令。
你可以在这里找到代码分析:https://godbolt.org/z/afQxxd
正如我在评论中所述,您必须告诉编译器进行优化,否则它不会做太多事情。此外,标志未使用,因此我将其修改为 returned,以便编译器会关心它。
这里是对 return 和 flag
稍作修改的同一示例,并进行了优化,以便您可以看到编译器真正可以做什么。
我的问题涉及以下构造:
uint16_t flag = 0x1f;
while (curCase) {
if ((curCase->i == i) && (curCase->j == j)) flag ^= 0x10;
if ((curCase->i == i-1) && (curCase->j == j)) flag ^= 0x01;
if ((curCase->i == i) && (curCase->j == j+1)) flag ^= 0x02;
if ((curCase->i == i+1) && (curCase->j == j)) flag ^= 0x04;
if ((curCase->i == i) && (curCase->j == j-1)) flag ^= 0x08;
if (!(flag ^ 0xf0)) return; // if none of the neighbors are candidates (ignore first bits)
curCase = curCase->suiv;
}
curCase只是链表的一个元素,有2个int属性i和j,下一个元素suiv;
知道我的程序在单线程上运行,因此指向的值在 while 的迭代期间不能改变;
我想知道编译器是否足够聪明(我想答案是肯定的,但我想确定)来检测代码是否需要计算指针 curCase->i 和 curCase-> j 5 次,我本可以在循环开始时声明 2 个临时指针并分配 curCase->i 和 curCase->j;
更普遍的是,如果它检测到多次计算的变量并因此进行优化。
我已经用不同的列表大小对这两个选项进行了测试,但我没有发现一个比另一个快得多。
对于这种大小的代码,很难找到基于编译器优化的性能差异(只是评论)。我 认为 这完全取决于编译器实现,它可以完成,但这取决于对范围的分析。
为了检验假设,我对您的代码进行了一些调整以使用 Compile Explorer,如下所示:
struct cur { int i; int j; void * suiv;} typedef cur;
int i;
int j;
void function(cur *curCase) {
int flag = 0x1f;
while (curCase) {
if ((curCase->i == i) && (curCase->j == j)) flag ^= 0x10;
if ((curCase->i == i-1) && (curCase->j == j)) flag ^= 0x01;
if ((curCase->i == i) && (curCase->j == j+1)) flag ^= 0x02;
if ((curCase->i == i+1) && (curCase->j == j)) flag ^= 0x04;
if ((curCase->i == i) && (curCase->j == j-1)) flag ^= 0x08;
if (!(flag ^ 0xf0)) return; // if none of the neighbors are candidates (ignore first bits)
curCase = curCase->suiv;
}
}
而且 gcc 和 CLang 的汇编代码似乎没有隔离您提到的指令。
你可以在这里找到代码分析:https://godbolt.org/z/afQxxd
正如我在评论中所述,您必须告诉编译器进行优化,否则它不会做太多事情。此外,标志未使用,因此我将其修改为 returned,以便编译器会关心它。
这里是对 return 和 flag
稍作修改的同一示例,并进行了优化,以便您可以看到编译器真正可以做什么。