gpt4 book ai didi

c++ - Clang sizeof ("literal") 优化

转载 作者:行者123 更新时间:2023-11-28 02:17:38 25 4
gpt4 key购买 nike

使用 C++ 的经验,我试图了解字符串文字的 sizeofstrlen 之间的性能差异。

这是我的小基准代码:

#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++ 为带有 sizeof 的循环生成的程序集:

rdtsc  
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, jne} 循环,看起来没用。这是一个错误吗?

信息:

g++ (GCC) 5.1.0
clang version 3.6.2 (tags/RELEASE_362/final)

编辑2:它可能是 clang 中的错误。我开了一个bug report .

最佳答案

我会称之为 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: 开始空循环: 一个“phi”节点(它根据我们来自的位置选择 i 的"new"值 - 在循环之前 -> 0,在循环内 -> % inc10.18) 增量 如果 %inc10.18 不是 100000001,则跳回的条件分支。

我对 clang 和 LLVM 的了解还不够,无法解释为什么没有优化那个空循环。但肯定不是 sizeof 需要时间,因为循环内没有 sizeof

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

关于c++ - Clang sizeof ("literal") 优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33562365/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com