MSVC不能展开简单的for循环:我错过了什么?

5
在下面的代码片段中,两个函数执行完全相同的功能并且在逻辑上是等效的。
constexpr char a[] = {50, 100};

bool contains_loop(char num) {
    for (int i = 0; i < 2; ++ i) {
        if (a[i] == num) {
            return true;
        }
    }
    return false;
}

bool contains_expanded(char num) {
    return num == a[0] || num == a[1];
}

我的测试在godbolt上显示,在启用了/O2之后,x64 MSVC 19.30中的contains_loop未被优化为展开循环。在汇编代码中仍然有计数器,而contains_expanded的编译汇编代码中没有计数器。

        inc     rax
        cmp     rax, 2

在gcc的-O3优化下,它们被编译成完全相同的汇编代码。
这种优化看起来很简单,但我无法让MSVC(自动)进行优化。 我在这里错过了什么?

也许可以使用OpenMP标记并行化(+展开)它?此外,只有两个迭代循环就像只有一个MMX指令,它可能试图执行SSE,而且需要至少4个迭代吗? - huseyin tugrul buyukisik
1个回答

2

我实际上是重新撰写这个答案,因为我错误地调用了contains_expanded而不是您所问的contains_loop

简短的回答是,即使使用VS2022(真正的版本)和/O2和/Oi,循环显然也没有展开。我甚至尝试了优先考虑大小而不是速度,结果仍然相同。

我使用了这个简单的主函数:

int main()
{
    std::vector<char> vals { 50, 49};
    std::cout << "Test" << std::endl;
    for (auto v : vals)
    {
        const auto val = contains_expanded(v);
        std::cout << "Contains = " << v << std::boolalpha << val;
    }                   
}

我的编译优化开关使用了/O2和/Oi。这是从输出命令tlog文件中的编译行:
^C:\USERS\JMOLE\SOURCE\REPOS\TESTCPP\TESTCPP\TESTCPP.CPP
/c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D NDEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /permissive- /Fo"X64\RELEASE\\" /Fd"X64\RELEASE\VC143.PDB" /external:W3 /Gd /TP /FC C:\USERS\JMOLE\SOURCE\REPOS\TESTCPP\TESTCPP\TESTCPP.CPP

这是我版本中调用 contains_loop 的列表。

    const auto val = contains_loop(v);
00007FF7234D1224  xor         eax,eax  
00007FF7234D1226  cmp         byte ptr [rax+r15],bl  
00007FF7234D122A  je          main+0AAh (07FF7234D123Ah)  
00007FF7234D122C  inc         rax  
00007FF7234D122F  cmp         rax,2  
00007FF7234D1233  jl          main+96h (07FF7234D1226h)  
00007FF7234D1235  xor         sil,sil  
00007FF7234D1238  jmp         main+0ADh (07FF7234D123Dh)  
00007FF7234D123A  mov         sil,1  

看起来在MSVC中,无论你选择什么优化方式,循环都没有被展开。如果有什么遗漏的地方,我也没看到是什么。


在MSVC 19.30上,您指定了哪些编译器标志? - xuhdev
为什么你的参数寄存器是 bl,而 OP 的 godbolt 链接中是 cl?根据 https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170,第一个参数应该在 rcx 中传递。 - user17732522
我不知道godbolt是做什么的,所以我不能保证它的准确性。我这里有真正的东西——VS2022,所以我肯定可以确定我的设置的准确性。但是我会在问题中添加我的主要函数和编译器开关。 - Joe
在我的问题中,contains_loop 是没有被优化的部分。你的代码片段使用了 contains_expanded - xuhdev
抱歉。我重新写了答案。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接