Bug 92356 - Missed optimization of std::find looking for item in array of items [0..n]
Summary: Missed optimization of std::find looking for item in array of items [0..n]
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 9.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: missed-optimization
Depends on:
Blocks:
 
Reported: 2019-11-04 14:58 UTC by Scott Freeman
Modified: 2020-10-06 16:42 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-10-06 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Scott Freeman 2019-11-04 14:58:53 UTC
Performing a std::find on an array with elements [0, 1, 2,..., n] does not produce similar optimizations as implementing the find using a raw for loop.  This is true when using clang 9.0 or gcc 9.2 with flags `-O2 -std=c++17`.  Using libc++ with clang produces the same result with std::find as the raw loop version.

Here is a link to compiler explore demonstrating the difference in generated assembly.
https://godbolt.org/z/pM1FQQ

One item I just discovered that I did not expect is that a range-for loop in gcc also has inefficiencies.  That might be a separate issue, and I can file one if needed.

I understand this looks like a contrived example, but I feel like this scenario does come up sometimes with enums.  People will have a large enum list where they want to check if a value is in a small subset of the enum and that subset just happens to be the first few items in the enum.
Comment 1 Jonathan Wakely 2020-10-06 16:42:32 UTC
Please provide source, not jus a link to another site, as stated at https://gcc.gnu.org/bugs/

The testcase is:

#include <algorithm>
#include <iterator>

constexpr static int validValues[] = { 0, 1, 2 };

bool isValidValueWithRawLoop(int value) {
    for (int i = 0; i < std::size(validValues); ++i) {
        if (validValues[i] == value) {
            return true;
        }
    }

    return false;
}

bool isValidValueWithRangeForLoop(int value)
{
    for (int item : validValues) {
        if (item == value) {
            return true;
        }
    }

    return false;
}

bool isValidValueWithFind(int value)
{
    return std::find(std::begin(validValues), std::end(validValues), value) != std::end(validValues);
}

Clang with libc++ compiles to the same code for all three functions:

        cmp     edi, 3
        setb    al
        ret

GCC compiles the first one similarly:

        cmp     edi, 2
        setbe   al
        ret

but generates much worse code for the range-based for loop and std::find.

Clang with libstdc++ also generates poor code, suggesting it's not just a compiler issue. Presumably the manual unrolling we do in std::__find_if hurts the optimizer.

The range-based for case is definitely a separate bug and should be filed separately against component=c++ or component=tree-optimization.