gpt4 book ai didi

c++ - 使用 VC++ 的 __assume 是否可能带来可衡量的性能提升?

转载 作者:可可西里 更新时间:2023-11-01 15:50:16 25 4
gpt4 key购买 nike

使用 VC++ 的 __assume 是否可能带来可衡量的性能提升?如果是这样,请在您的答案中发布带有代码和基准的证明。

关于 __assume 的稀疏 MSDN 文章:http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=vs.100).aspx

文中提到使用__assume(0)通过__assume(0)使switch语句更快>默认大小写。我测量到以这种方式使用 __assume(0) 没有性能提升:

void NoAssumeSwitchStatement(int i)
{
switch (i)
{
case 0:
vector<int>();
break;
case 1:
vector<int>();
break;
default:
break;
}
}

void AssumeSwitchStatement(int i)
{
switch (i)
{
case 0:
vector<int>();
break;
case 1:
vector<int>();
break;
default:
__assume(0);
}
}

int main(int argc, char* argv[])
{
const int Iterations = 1000000;
LARGE_INTEGER start, middle, end;
QueryPerformanceCounter(&start);
for (int i = 0; i < Iterations; ++i)
{
NoAssumeSwitchStatement(i % 2);
}
QueryPerformanceCounter(&middle);
for (int i = 0; i < Iterations; ++i)
{
AssumeSwitchStatement(i % 2);
}
QueryPerformanceCounter(&end);
LARGE_INTEGER cpuFrequency;
QueryPerformanceFrequency(&cpuFrequency);
cout << "NoAssumeSwitchStatement: " << (((double)(middle.QuadPart - start.QuadPart)) * 1000) / (double)cpuFrequency.QuadPart << "ms" << endl;
cout << " AssumeSwitchStatement: " << (((double)(end.QuadPart - middle.QuadPart)) * 1000) / (double)cpuFrequency.QuadPart << "ms" << endl;
return 0;
}

圆形控制台输出,1000000 次迭代:

NoAssumeSwitchStatement:46 毫秒
AssumeSwitchStatement:46ms

最佳答案

基准谎言。他们很少衡量你想让他们做什么。在这种特殊情况下,这些方法可能是内联的,因此 __assume 只是多余的。

至于实际问题,是的,它可能会有所帮助。开关通常由跳转表实现,通过减小该表的大小或删除一些条目,编译器可能能够选择更好的 CPU 指令来实现开关

在您的极端情况下,它可以将 switch 转换为 if (i == 0) { } else { } 结构,这通常是高效的。

此外,修剪死分支有助于保持代码整洁,更少的代码意味着更好地使用 CPU 指令缓存。

然而,这些都是微观优化,它们很少有返回:你需要一个分析器来指出它们,即使是它们也可能很难理解要进行的特定转换(是 __assume 最好的?)。这是专家的工作。

编辑:使用 LLVM

void foo(void);
void bar(void);

void regular(int i) {
switch(i) {
case 0: foo(); break;
case 1: bar(); break;
}
}

void optimized(int i) {
switch(i) {
case 0: foo(); break;
case 1: bar(); break;
default: __builtin_unreachable();
}
}

请注意,唯一的区别是是否存在 __builtin_unreachable(),它类似于 MSVC __assume(0)

define void @regular(i32 %i) nounwind uwtable {
switch i32 %i, label %3 [
i32 0, label %1
i32 1, label %2
]

; <label>:1 ; preds = %0
tail call void @foo() nounwind
br label %3

; <label>:2 ; preds = %0
tail call void @bar() nounwind
br label %3

; <label>:3 ; preds = %2, %1, %0
ret void
}

define void @optimized(i32 %i) nounwind uwtable {
%cond = icmp eq i32 %i, 1
br i1 %cond, label %2, label %1

; <label>:1 ; preds = %0
tail call void @foo() nounwind
br label %3

; <label>:2 ; preds = %0
tail call void @bar() nounwind
br label %3

; <label>:3 ; preds = %2, %1
ret void
}

注意这里的switch语句是如何在regular中被优化成optimized中的简单比较的。

这映射到以下 x86 程序集:

    .globl  regular                  |      .globl  optimized
.align 16, 0x90 | .align 16, 0x90
.type regular,@function | .type optimized,@function
regular: | optimized:
.Ltmp0: | .Ltmp3:
.cfi_startproc | .cfi_startproc
# BB#0: | # BB#0:
cmpl $1, %edi | cmpl $1, %edi
je .LBB0_3 | je .LBB1_2
# BB#1: |
testl %edi, %edi |
jne .LBB0_4 |
# BB#2: | # BB#1:
jmp foo | jmp foo
.LBB0_3: | .LBB1_2:
jmp bar | jmp bar
.LBB0_4: |
ret |
.Ltmp1: | .Ltmp4:
.size regular, .Ltmp1-regular | .size optimized, .Ltmp4-optimized
.Ltmp2: | .Ltmp5:
.cfi_endproc | .cfi_endproc
.Leh_func_end0: | .Leh_func_end1:

请注意,在第二种情况下:

  • 代码更紧凑(指令更少)
  • 在所有路径上都有一个比较/跳转(cmpl/je)(而不是一个路径有一个跳转和一个路径有两个)

另请注意,它是如此之近,以至于我不知道如何测量噪音以外的任何东西......

另一方面,从语义上讲,它确实指示了一个意图,尽管 assert 可能更适合仅用于语义。

关于c++ - 使用 VC++ 的 __assume 是否可能带来可衡量的性能提升?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9504206/

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