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)

Permita que el compilador optimice para el caso en que las rutas de ejecución que incluyen esa sentencia sean más o menos probables que cualquier ruta de ejecución alternativa que no incluya dicha sentencia.

Contenidos

Sintaxis

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

Explicación

Estos atributos pueden aplicarse a etiquetas y sentencias (distintas de sentencias-de-declaración). No pueden aplicarse simultáneamente a la misma etiqueta o sentencia.

1) Se aplica a una declaración para permitir al compilador optimizar para el caso en que las rutas de ejecución que incluyen dicha declaración sean más probables que cualquier otra ruta de ejecución alternativa que no incluya dicha declaración.
2) Se aplica a una declaración para permitir al compilador optimizar para el caso en que las rutas de ejecución que incluyen dicha declaración sean menos probables que cualquier ruta de ejecución alternativa que no incluya dicha declaración.

Se considera que una ruta de ejecución incluye una etiqueta si y solo si contiene un salto a esa etiqueta:

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

i == 2 se considera más probable que cualquier otro valor de i , pero el [ [ likely ] ] no tiene efecto en el caso i == 1 aunque caiga a través de la etiqueta case 2 : .

Ejemplo

#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{}; // asegura un efecto secundario
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) ? "igual" : "diferir") << '\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 << "Tiempo: " << std::fixed << std::setprecision(6) << diff.count()
                  << " seg " << rem << std::endl; 
    };
    benchmark(with_attributes::cos, "(con atributos)");
    benchmark(no_attributes::cos, "(sin atributos)");
    benchmark([](double t) { return std::cos(t); }, "(std::cos)");
}

Salida posible:

x = 0.125
0.99219766722932900560039115589461289346218109130859375
0.99219766722932900560039115589461289346218109130859375
igual
x = 0.25
0.96891242171064473343022882545483298599720001220703125
0.96891242171064473343022882545483298599720001220703125
igual
x = 0.5
0.8775825618903727587394314468838274478912353515625
0.8775825618903727587394314468838274478912353515625
igual
x = 1.490116119384765625e-08
0.99999999999999988897769753748434595763683319091796875
0.99999999999999988897769753748434595763683319091796875
igual
Tiempo: 0.579122 seg (con atributos)
Tiempo: 0.722553 seg (sin atributos)
Tiempo: 0.425963 seg (std::cos)

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 9.12.7 Atributos de probabilidad [dcl.attr.likelihood]
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 9.12.6 Atributos de probabilidad [dcl.attr.likelihood]