Namespaces
Variants

C++ attribute: likely, unlikely (since C++20)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Attributes
(C++23)
(C++11) (until C++26)
(C++14)
likely
(C++20)
(C++17)
(C++11)
unlikely
(C++20)

Permettre au compilateur d'optimiser pour le cas où les chemins d'exécution incluant cette instruction sont plus ou moins probables que tout autre chemin d'exécution alternatif qui n'inclut pas une telle instruction.

Table des matières

Syntaxe

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

Explication

Ces attributs peuvent être appliqués aux étiquettes et aux instructions (autres que les instructions de déclaration). Ils ne peuvent pas être appliqués simultanément à la même étiquette ou instruction.

1) S'applique à une instruction pour permettre au compilateur d'optimiser le cas où les chemins d'exécution incluant cette instruction sont plus probables que tout autre chemin d'exécution alternatif n'incluant pas une telle instruction.
2) S'applique à une instruction pour permettre au compilateur d'optimiser le cas où les chemins d'exécution incluant cette instruction sont moins probables que tout autre chemin d'exécution alternatif n'incluant pas une telle instruction.

Un chemin d'exécution est considéré comme incluant une étiquette si et seulement s'il contient un saut vers cette étiquette :

int f(int i)
{
    switch (i)
    {
        case 1: [[fallthrough]];
        [[likely]] case 2: return 1;
    }
    return 2;
}
**Note:** Le code C++ n'a pas été traduit conformément aux instructions, car il se trouve dans des balises `
` et contient des termes spécifiques au C++ qui doivent être préservés. Seul le texte environnant (s'il y en avait) aurait été traduit en français.

i == 2 est considéré comme plus probable que toute autre valeur de i , mais l'attribut [ [ likely ] ] n'a aucun effet sur le cas i == 1 même s'il passe par l'étiquette case 2 : .

Exemple

#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{}; // garantit un effet secondaire
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) ? "égal" : "différer") << '\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 << "Temps : " << std::fixed << std::setprecision(6) << diff.count()
                  << " sec " << rem << std::endl; 
    };
    benchmark(with_attributes::cos, "(avec attributs)");
    benchmark(no_attributes::cos, "(sans attributs)");
    benchmark([](double t) { return std::cos(t); }, "(std::cos)");
}

Sortie possible :

x = 0.125
0.99219766722932900560039115589461289346218109130859375
0.99219766722932900560039115589461289346218109130859375
égal
x = 0.25
0.96891242171064473343022882545483298599720001220703125
0.96891242171064473343022882545483298599720001220703125
égal
x = 0.5
0.8775825618903727587394314468838274478912353515625
0.8775825618903727587394314468838274478912353515625
égal
x = 1.490116119384765625e-08
0.99999999999999988897769753748434595763683319091796875
0.99999999999999988897769753748434595763683319091796875
égal
Temps : 0.579122 s (avec attributs)
Temps : 0.722553 s (sans attributs)
Temps : 0.425963 s (std::cos)

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 9.12.7 Attributs de vraisemblance [dcl.attr.likelihood]
  • Norme C++20 (ISO/IEC 14882:2020) :
  • 9.12.6 Attributs de vraisemblance [dcl.attr.likelihood]