Clang sizeof(“literal”)优化

时间:2015-11-06 08:24:03

标签: c++ gcc clang compiler-optimization

使用C ++体验,我尝试了解字符串文字的strlen#include <iostream> #include <cstring> #define LOOP_COUNT 1000000000 unsigned long long rdtscl(void) { unsigned int lo, hi; __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); } int main() { unsigned long long before = rdtscl(); size_t ret; for (int i = 0; i < LOOP_COUNT; i++) ret = strlen("abcd"); unsigned long long after = rdtscl(); std::cout << "Strlen " << (after - before) << " ret=" << ret << std::endl; before = rdtscl(); for (int i = 0; i < LOOP_COUNT; i++) ret = sizeof("abcd"); after = rdtscl(); std::cout << "Sizeof " << (after - before) << " ret=" << ret << std::endl; } 之间的性能差异。

这是我的小基准代码:

clang++

使用clang++ -O3 -Wall -o sizeof_vs_strlen sizeof_vs_strlen.cpp ./sizeof_vs_strlen Strlen 36 ret=4 Sizeof 62092396 ret=5 进行编译,得到以下结果:

g++

使用g++ -O3 -Wall -o sizeof_vs_strlen sizeof_vs_strlen.cpp ./sizeof_vs_strlen Strlen 30 ret=4 Sizeof 30 ret=5

g++

我强烈怀疑sizeof确实优化了clang++clang++的循环。 这个结果是一个已知的问题吗?

编辑:

sizeofrdtsc mov %edx,%r14d shl $0x20,%r14 mov $0x3b9aca01,%ecx xchg %ax,%ax add $0xffffffed,%ecx // 0x400ad0 jne 0x400ad0 <main+192> mov %eax,%eax or %rax,%r14 rdtsc

的循环生成的程序集
g++

rdtsc mov %edx,%esi mov %eax,%ecx rdtsc 的那个:

clang++

我不明白为什么add执行{jneg++ (GCC) 5.1.0 clang version 3.6.2 (tags/RELEASE_362/final) }循环,它似乎毫无用处。这是一个错误吗?

有关信息:

clang

EDIT2: 它可能是Mat mat_img(image); int stepSize = 65; int width = mat_img.size().width; int height = mat_img.size().height; for (int i = 0; i<height; i += stepSize) cv::line(mat_img, Point(0, i), Point(width, i), cv::Scalar(0, 255, 255)); for (int i = 0; i<width; i += stepsSize) cv::line(mat_img, Point(i, 0), Point(i, height), cv::Scalar(255, 0, 255)); 中的一个错误。 我开了一个bug report

2 个答案:

答案 0 :(得分:2)

我称之为clang中的错误。

它实际上正在优化sizeof本身,而不是循环。

为了使代码更清晰,我将std::cout更改为printf,然后为main获取以下LLVM-IR代码:

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
entry:
  %0 = tail call { i32, i32 } asm sideeffect "rdtsc", "={ax},={dx},~{dirflag},~{fpsr},~{flags}"() #2, !srcloc !1
  %asmresult1.i = extractvalue { i32, i32 } %0, 1
  %conv2.i = zext i32 %asmresult1.i to i64
  %shl.i = shl nuw i64 %conv2.i, 32
  %asmresult.i = extractvalue { i32, i32 } %0, 0
  %conv.i = zext i32 %asmresult.i to i64
  %or.i = or i64 %shl.i, %conv.i
  %1 = tail call { i32, i32 } asm sideeffect "rdtsc", "={ax},={dx},~{dirflag},~{fpsr},~{flags}"() #2, !srcloc !1
  %asmresult.i.25 = extractvalue { i32, i32 } %1, 0
  %asmresult1.i.26 = extractvalue { i32, i32 } %1, 1
  %conv.i.27 = zext i32 %asmresult.i.25 to i64
  %conv2.i.28 = zext i32 %asmresult1.i.26 to i64
  %shl.i.29 = shl nuw i64 %conv2.i.28, 32
  %or.i.30 = or i64 %shl.i.29, %conv.i.27
  %sub = sub i64 %or.i.30, %or.i
  %call2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str, i64 0, i64 0), i64 %sub, i64 4)
  %2 = tail call { i32, i32 } asm sideeffect "rdtsc", "={ax},={dx},~{dirflag},~{fpsr},~{flags}"() #2, !srcloc !1
  %asmresult1.i.32 = extractvalue { i32, i32 } %2, 1
  %conv2.i.34 = zext i32 %asmresult1.i.32 to i64
  %shl.i.35 = shl nuw i64 %conv2.i.34, 32
  br label %for.cond.5

for.cond.5:                                       ; preds = %for.cond.5, %entry
  %i4.0 = phi i32 [ 0, %entry ], [ %inc10.18, %for.cond.5 ]
  %inc10.18 = add nsw i32 %i4.0, 19
  %exitcond.18 = icmp eq i32 %inc10.18, 1000000001
  br i1 %exitcond.18, label %for.cond.cleanup.7, label %for.cond.5

for.cond.cleanup.7:                               ; preds = %for.cond.5
  %asmresult.i.31 = extractvalue { i32, i32 } %2, 0
  %conv.i.33 = zext i32 %asmresult.i.31 to i64
  %or.i.36 = or i64 %shl.i.35, %conv.i.33
  %3 = tail call { i32, i32 } asm sideeffect "rdtsc", "={ax},={dx},~{dirflag},~{fpsr},~{flags}"() #2, !srcloc !1
  %asmresult.i.37 = extractvalue { i32, i32 } %3, 0
  %asmresult1.i.38 = extractvalue { i32, i32 } %3, 1
  %conv.i.39 = zext i32 %asmresult.i.37 to i64
  %conv2.i.40 = zext i32 %asmresult1.i.38 to i64
  %shl.i.41 = shl nuw i64 %conv2.i.40, 32
  %or.i.42 = or i64 %shl.i.41, %conv.i.39
  %sub13 = sub i64 %or.i.42, %or.i.36
  %call14 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str, i64 0, i64 0), i64 %sub13, i64 5)
  ret i32 0
}

如您所见,对printf的调用使用sizeof中的常量5,for.cond.5:启动空循环:    a&#34; phi&#34;节点(根据我们来自的位置选择&#34;新的&#34; i的值 - 在循环之前 - &gt; 0,在循环中 - &gt; %inc10.18)    增量    如果%inc10.18不是100000001,则跳回的条件分支。

我对clang和LLVM的了解不足以解释为什么没有优化该空循环。但它肯定不是花费时间的sizeof,因为循环中没有sizeof

值得注意的是sizeof在编译时始终是一个常数,它永远不会花费时间&#34;除了将常数值加载到寄存器中之外。

答案 1 :(得分:0)

不同之处在于,sizeof()不是函数调用。 sizeof()返回的值在编译时是已知的。 同时,strlen是函数调用(显然在运行时执行)并且根本没有优化。它在字符串中搜索'\ 0',如果它是动态分配的字符串或字符串文字,它甚至没有一条线索。

因此,对于字符串文字,sizeof预计总是更快。

我不是专家,但您的结果可能会通过调度算法或时间变量溢出来解释。