Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/attributes/likely"

From cppreference.com
< cpp‎ | language‎ | attributes
m (Example: ~ Output; to be fair, there is likely a progress in attributes support.)
m (References: Consistent order: newest to oldest)
 
(10 intermediate revisions by 5 users not shown)
Line 2: Line 2:
 
{{cpp/language/attributes/navbar}}
 
{{cpp/language/attributes/navbar}}
  
Allow the compiler to optimize for the case where paths of execution including that statement are more or less likely than any alternative path of execution that does not include such a statement
+
Allow the compiler to optimize for the case where paths of execution including that statement are more or less likely than any alternative path of execution that does not include such a statement.
  
 
===Syntax===
 
===Syntax===
 
{{sdsc begin}}
 
{{sdsc begin}}
 
{{sdsc|num=1|1=
 
{{sdsc|num=1|1=
{{ttb|<nowiki>[[</nowiki>likely<nowiki>]]</nowiki>}}
+
{{ttb|{{c/core|[[likely]]}}}}
 
}}
 
}}
 
{{sdsc|num=2|1=
 
{{sdsc|num=2|1=
{{ttb|<nowiki>[[</nowiki>unlikely<nowiki>]]</nowiki>}}
+
{{ttb|{{c/core|[[unlikely]]}}}}
 
}}
 
}}
 
{{sdsc end}}
 
{{sdsc end}}
  
 
===Explanation===
 
===Explanation===
 
 
These attributes may be applied to labels and statements (other than declaration-statements). They may not be simultaneously applied to the same label or statement.
 
These attributes may be applied to labels and statements (other than declaration-statements). They may not be simultaneously applied to the same label or statement.
  
Line 24: Line 23:
 
A path of execution is deemed to include a label if and only if it contains a jump to that label:
 
A path of execution is deemed to include a label if and only if it contains a jump to that label:
  
{{source |
+
{{source|
 
int f(int i)
 
int f(int i)
 
{
 
{
     switch(i)
+
     switch (i)
 
     {
 
     {
 
         case 1: [[fallthrough]];
 
         case 1: [[fallthrough]];
Line 36: Line 35:
 
}}
 
}}
  
{{c|1=i == 2}} is considered more likely than any other value of {{tt|i}}, but the {{attr|likely}} has no effect on the {{c|1=i == 1}} case even though it falls through the {{c|case 2:}} label.
+
{{c|1=i == 2}} is considered more likely than any other value of {{tt|i}}, but the {{c/core|[[likely]]}} has no effect on the {{c|1=i == 1}} case even though it falls through the {{c|case 2:}} label.
  
 
===Example===
 
===Example===
Line 47: Line 46:
 
#include <random>
 
#include <random>
  
namespace with_attributes {
+
namespace with_attributes
constexpr double pow(double x, long long n) noexcept {
+
{
    if (n > 0) [[likely]]
+
    constexpr double pow(double x, long long n) noexcept
        return x * pow(x, n - 1);
+
    {
    else [[unlikely]]
+
        if (n > 0) [[likely]]
         return 1;
+
            return x * pow(x, n - 1);
}
+
         else [[unlikely]]
constexpr long long fact(long long n) noexcept {
+
            return 1;
    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;
+
     constexpr long long fact(long long n) noexcept
}
+
    {
} // namespace with_attributes
+
        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 {
+
namespace no_attributes
constexpr double pow(double x, long long n) noexcept {
+
{
    if (n > 0)
+
    constexpr double pow(double x, long long n) noexcept
        return x * pow(x, n - 1);
+
    {
    else
+
        if (n > 0)
         return 1;
+
            return x * pow(x, n - 1);
}
+
         else
constexpr long long fact(long long n) noexcept {
+
            return 1;
    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;
+
     constexpr long long fact(long long n) noexcept
}
+
    {
} // namespace no_attributes
+
        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 {
+
double gen_random() noexcept
 +
{
 
     static std::random_device rd;
 
     static std::random_device rd;
 
     static std::mt19937 gen(rd());
 
     static std::mt19937 gen(rd());
Line 102: Line 108:
 
volatile double sink{}; // ensures a side effect
 
volatile double sink{}; // ensures a side effect
  
int main() {
+
int main()
     for (const auto x : {0.125, 0.25, 0.5, 1. / (2 << 25)}) {
+
{
 +
     for (const auto x : {0.125, 0.25, 0.5, 1. / (1 << 26)})
 
         std::cout
 
         std::cout
 
             << std::setprecision(53)
 
             << std::setprecision(53)
Line 110: Line 117:
 
             << with_attributes::cos(x) << '\n'
 
             << with_attributes::cos(x) << '\n'
 
             << (std::cos(x) == with_attributes::cos(x) ? "equal" : "differ") << '\n';
 
             << (std::cos(x) == with_attributes::cos(x) ? "equal" : "differ") << '\n';
    }
 
  
     auto benchmark = [](auto fun, auto rem) {
+
     auto benchmark = [](auto fun, auto rem)
 +
    {
 
         const auto start = std::chrono::high_resolution_clock::now();
 
         const auto start = std::chrono::high_resolution_clock::now();
         for (auto size{1ULL}; size != 10'000'000ULL; ++size) {
+
         for (auto size{1ULL}; size != 10'000'000ULL; ++size)
 
             sink = fun(gen_random());
 
             sink = fun(gen_random());
        }
 
 
         const std::chrono::duration<double> diff =
 
         const std::chrono::duration<double> diff =
 
             std::chrono::high_resolution_clock::now() - start;
 
             std::chrono::high_resolution_clock::now() - start;
 
         std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count()
 
         std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count()
                   << " sec " << rem << std::endl;
+
                   << " sec " << rem << std::endl; <!-- flush is intentional -->
 
     };
 
     };
  
     benchmark(with_attributes::cos, "(with attributes:)");
+
     benchmark(with_attributes::cos, "(with attributes)");
 
     benchmark(no_attributes::cos, "(without attributes)");
 
     benchmark(no_attributes::cos, "(without attributes)");
     benchmark(cos, "(std::cos)");
+
     benchmark([](double t) { return std::cos(t); }, "(std::cos)");
 
}
 
}
|p=true
+
|p=true
|output=
+
|output=
 
x = 0.125
 
x = 0.125
 
0.99219766722932900560039115589461289346218109130859375
 
0.99219766722932900560039115589461289346218109130859375
Line 145: Line 151:
 
0.99999999999999988897769753748434595763683319091796875
 
0.99999999999999988897769753748434595763683319091796875
 
equal
 
equal
// gcc-9.3:
+
Time: 0.579122 sec (with attributes)
Time: 2.110391 sec (with attributes:)
+
Time: 1.323027 sec (without attributes)
+
Time: 0.626839 sec (std::cos)
+
// gcc-11.2021.01:
+
Time: 0.579122 sec (with attributes:)
+
 
Time: 0.722553 sec (without attributes)
 
Time: 0.722553 sec (without attributes)
 
Time: 0.425963 sec (std::cos)
 
Time: 0.425963 sec (std::cos)
 
}}
 
}}
  
{{langlinks|ja|zh}}
+
===References===
 +
{{ref std c++23}}
 +
{{ref std|section=9.12.7|title=Likelihood attributes|id=dcl.attr.likelihood}}
 +
{{ref std end}}
 +
{{ref std c++20}}
 +
{{ref std|section=9.12.6|title=Likelihood attributes|id=dcl.attr.likelihood}}
 +
{{ref std end}}
 +
 
 +
{{langlinks|es|ja|zh}}

Latest revision as of 11:43, 12 August 2024

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
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
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 
Attributes
(C++23)
(C++14)
likely
(C++20)
(C++17)
(C++11)
unlikely
(C++20)
 

Allow the compiler to optimize for the case where paths of execution including that statement are more or less likely than any alternative path of execution that does not include such a statement.

Contents

[edit] Syntax

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

[edit] Explanation

These attributes may be applied to labels and statements (other than declaration-statements). They may not be simultaneously applied to the same label or statement.

1) Applies to a statement to allow the compiler to optimize for the case where paths of execution including that statement are more likely than any alternative path of execution that does not include such a statement.
2) Applies to a statement to allow the compiler to optimize for the case where paths of execution including that statement are less likely than any alternative path of execution that does not include such a statement.

A path of execution is deemed to include a label if and only if it contains a jump to that label:

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

i == 2 is considered more likely than any other value of i, but the [[likely]] has no effect on the i == 1 case even though it falls through the case 2: label.

[edit] Example

#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)");
}

Possible output:

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)

[edit] References

  • 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]