Namensräume
Varianten
Aktionen

C++ Attribut: likely, unlikely (seit C++20)

Von cppreference.com
< cpp‎ | language‎ | attributes
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 
Attribute
(C++23)
(C++11)(bis C++26)
(C++14)
likely
(C++20)
(C++17)
(C++11)
unlikely
(C++20)
 

Ermöglicht es dem Compiler, für den Fall zu optimieren, dass Ausführungspfade, die diese Anweisung einschließen, wahrscheinlicher oder unwahrscheinlicher sind als jeder alternative Ausführungspfad, der eine solche Anweisung nicht einschließt.

Inhalt

[bearbeiten] Syntax

[[likely]] (1)
[[unlikely]] (2)

[bearbeiten] Erklärung

Diese Attribute können auf Labels und Anweisungen (außer Deklarationsanweisungen) angewendet werden. Sie dürfen nicht gleichzeitig auf dasselbe Label oder dieselbe Anweisung angewendet werden.

1) Wird auf eine Anweisung angewendet, um dem Compiler die Optimierung für den Fall zu ermöglichen, dass Ausführungspfade, die diese Anweisung einschließen, wahrscheinlicher sind als jeder alternative Ausführungspfad, der eine solche Anweisung nicht einschließt.
2) Wird auf eine Anweisung angewendet, um dem Compiler die Optimierung für den Fall zu ermöglichen, dass Ausführungspfade, die diese Anweisung einschließen, unwahrscheinlicher sind als jeder alternative Ausführungspfad, der eine solche Anweisung nicht einschließt.

Ein Ausführungspfad wird als ein Label enthaltend betrachtet, wenn und nur wenn er einen Sprung zu diesem Label enthält.

int f(int i)
{
    switch (i)
    {
        case 1: [[fallthrough]];
        [[likely]] case 2: return 1;
    }
    return 2;
}

i == 2 wird als wahrscheinlicher betrachtet als jeder andere Wert von i, aber das [[likely]] hat keine Auswirkung auf den Fall i == 1, obwohl dieser das case 2: Label durchläuft.

[bearbeiten] Beispiel

#include <chrono>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <random>
 
namespace with_attributes
{
    constexpr double pow(double x, long long n) noexcept
    {
        if (n > 0) [[likely]]
            return x * pow(x, n - 1);
        else [[unlikely]]
            return 1;
    }
    constexpr long long fact(long long n) noexcept
    {
        if (n > 1) [[likely]]
            return n * fact(n - 1);
        else [[unlikely]]
            return 1;
    }
    constexpr double cos(double x) noexcept
    {
        constexpr long long precision{16LL};
        double y{};
        for (auto n{0LL}; n < precision; n += 2LL) [[likely]]
            y += pow(x, n) / (n & 2LL ? -fact(n) : fact(n));
        return y;
    }
} // namespace with_attributes
 
namespace no_attributes
{
    constexpr double pow(double x, long long n) noexcept
    {
        if (n > 0)
            return x * pow(x, n - 1);
        else
            return 1;
    }
    constexpr long long fact(long long n) noexcept
    {
        if (n > 1)
            return n * fact(n - 1);
        else
            return 1;
    }
    constexpr double cos(double x) noexcept
    {
        constexpr long long precision{16LL};
        double y{};
        for (auto n{0LL}; n < precision; n += 2LL)
            y += pow(x, n) / (n & 2LL ? -fact(n) : fact(n));
        return y;
    }
} // namespace no_attributes
 
double gen_random() noexcept
{
    static std::random_device rd;
    static std::mt19937 gen(rd());
    static std::uniform_real_distribution<double> dis(-1.0, 1.0);
    return dis(gen);
}
 
volatile double sink{}; // ensures a side effect
 
int main()
{
    for (const auto x : {0.125, 0.25, 0.5, 1. / (1 << 26)})
        std::cout
            << std::setprecision(53)
            << "x = " << x << '\n'
            << std::cos(x) << '\n'
            << with_attributes::cos(x) << '\n'
            << (std::cos(x) == with_attributes::cos(x) ? "equal" : "differ") << '\n';
 
    auto benchmark = [](auto fun, auto rem)
    {
        const auto start = std::chrono::high_resolution_clock::now();
        for (auto size{1ULL}; size != 10'000'000ULL; ++size)
            sink = fun(gen_random());
        const std::chrono::duration<double> diff =
            std::chrono::high_resolution_clock::now() - start;
        std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count()
                  << " sec " << rem << std::endl; 
    };
 
    benchmark(with_attributes::cos, "(with attributes)");
    benchmark(no_attributes::cos, "(without attributes)");
    benchmark([](double t) { return std::cos(t); }, "(std::cos)");
}

Mögliche Ausgabe

x = 0.125
0.99219766722932900560039115589461289346218109130859375
0.99219766722932900560039115589461289346218109130859375
equal
x = 0.25
0.96891242171064473343022882545483298599720001220703125
0.96891242171064473343022882545483298599720001220703125
equal
x = 0.5
0.8775825618903727587394314468838274478912353515625
0.8775825618903727587394314468838274478912353515625
equal
x = 1.490116119384765625e-08
0.99999999999999988897769753748434595763683319091796875
0.99999999999999988897769753748434595763683319091796875
equal
Time: 0.579122 sec (with attributes)
Time: 0.722553 sec (without attributes)
Time: 0.425963 sec (std::cos)

[bearbeiten] Referenzen

  • C++23 Standard (ISO/IEC 14882:2024)
  • 9.12.7 Likelihood attributes [dcl.attr.likelihood]
  • C++20 Standard (ISO/IEC 14882:2020)
  • 9.12.6 Likelihood attributes [dcl.attr.likelihood]