assembly 为什么在std::array上循环时,使用std::find生成的汇编代码与手工编写的代码不同(而且更慢)?[duplicate]

qgzx9mmu  于 2023-01-17  发布在  其他
关注(0)|答案(1)|浏览(115)
    • 此问题在此处已有答案**:

Why do gcc and clang generate so much code for std::find?(1个答案)
昨天关门了。
我有这段代码在std::array<int, 3>上循环(请参见Compiler Explorer),并查找数组中是否有元素。

#include <algorithm>
#include <iterator>
#include <array>
constexpr std::array<int, 3> arr = { 0, 1, 2};
bool forLoop(int inp) 
{
    for (int i {0}; i < arr.size(); ++i)
    {
        if (arr[i] == inp) 
        {
            return true;
        }
    }
    return false;
}
bool forEachLoop(int inp)
{
    for (int i : arr) 
    {
        if (i == inp) 
        {
            return true;
        }
    }
    return false;
}
bool STL(int inp)
{
    return std::find(arr.begin(), arr.end(), inp) != arr.end();
}

使用x86-64 clang 15.0.0-std=c++17 -O3进行编译,forLoop()forEachLoop()都将生成:

cmp     edi, 3
        setb    al
        ret

但是STL()生成的程序集大不相同

test    edi, edi
        je      .LBB2_1
        cmp     edi, 1
        jne     .LBB2_3
        lea     rax, [rip + arr+4]
        lea     rcx, [rip + arr+12]
        cmp     rax, rcx
        setne   al
        ret
.LBB2_1:
        lea     rax, [rip + arr]
        lea     rcx, [rip + arr+12]
        cmp     rax, rcx
        setne   al
        ret
.LBB2_3:
        xor     eax, eax
        cmp     edi, 2
        setne   al
        lea     rcx, [rip + arr]
        lea     rax, [rcx + 4*rax]
        add     rax, 8
        lea     rcx, [rip + arr+12]
        cmp     rax, rcx
        setne   al
        ret

我尝试使用gcc代替,但STL()仍然生成更长的程序集
当我试图改变它,使arr有其他数量的元素(如4),这3个函数生成相同的汇编。
那么这是一个遗漏的优化问题吗?为什么它只发生在3个元素上?

lrpiutwd

lrpiutwd1#

std::find使用libstdc++中的std::__find_if,它有一个随机访问迭代器的特殊化。虽然一般的实现是一个简单的循环,线性迭代通过一次测试一个元素的范围,特殊化将循环展开为四个连续元素测试的组,并处理剩余的元素,这些元素不适合任何四个元素的组。
显然,编译器很难优化这种特殊化,我不确定函数的这种特殊化是为了在更大的范围内执行而编写的,还是它背后的思想已经不再适用了(浏览存储库,这从1998年就在那里了),但我也不知道为什么编译器要特别努力地优化这个部分展开的循环。也许是因为在实现结束时对剩余元素的测试依赖于初始循环中对__first的修改(顺便说一句,我也看不出有什么好的理由)。

相关问题