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)

Позволить компилятору оптимизировать случай, когда пути выполнения, включающие данную инструкцию, более или менее вероятны, чем любой альтернативный путь выполнения, не содержащий такой инструкции.

Содержание

Синтаксис

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

Объяснение

Эти атрибуты могут применяться к меткам и операторам (кроме операторов объявления). Они не могут одновременно применяться к одной и той же метке или оператору.

1) Применяется к оператору, чтобы позволить компилятору оптимизировать случай, когда пути выполнения, включающие этот оператор, более вероятны, чем любой альтернативный путь выполнения, не включающий такой оператор.
2) Применяется к оператору, чтобы позволить компилятору оптимизировать случай, когда пути выполнения, включающие этот оператор, менее вероятны, чем любой альтернативный путь выполнения, который не включает такой оператор.

Путь выполнения считается включающим метку тогда и только тогда, когда он содержит переход к этой метке:

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

i == 2 считается более вероятным, чем любое другое значение i , но [ [ likely ] ] не влияет на случай i == 1 , даже несмотря на то, что он переходит через метку case 2 : .

Пример

#include <chrono>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <random>
namespace with_attributes
{
    constexpr double pow(double x, long long n) noexcept
    {
        если (n > 0) [[likely]]
            return x * pow(x, n - 1);
        else [[unlikely]]
            return 1;
    }
    constexpr long long fact(long long n) noexcept
    {
        если (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
    {
        если (n > 0)
            return x * pow(x, n - 1);
        else
            return 1;
    }
    constexpr long long fact(long long n) noexcept
    {
        если (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{}; // обеспечивает побочный эффект
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) ? "равно" : "отличаться") << '\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 << "Время: " << std::fixed << std::setprecision(6) << diff.count()
                  << " сек " << rem << std::endl; 
    };
    benchmark(with_attributes::cos, "(с атрибутами)");
    benchmark(no_attributes::cos, "(без атрибутов)");
    benchmark([](double t) { return std::cos(t); }, "(std::cos)");
}

Возможный вывод:

x = 0.125
0.99219766722932900560039115589461289346218109130859375
0.99219766722932900560039115589461289346218109130859375
равно
x = 0.25
0.96891242171064473343022882545483298599720001220703125
0.96891242171064473343022882545483298599720001220703125
равно
x = 0.5
0.8775825618903727587394314468838274478912353515625
0.8775825618903727587394314468838274478912353515625
равно
x = 1.490116119384765625e-08
0.99999999999999988897769753748434595763683319091796875
0.99999999999999988897769753748434595763683319091796875
равно
Время: 0.579122 сек (с атрибутами)
Время: 0.722553 сек (без атрибутов)
Время: 0.425963 сек (std::cos)

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 9.12.7 Атрибуты вероятности [dcl.attr.likelihood]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 9.12.6 Атрибуты вероятности [dcl.attr.likelihood]