C++ attribute: likely, unlikely (since C++20)
From ja.cppreference.net
<
cpp
|
language
|
attributes
その文を含む実行パスが、そのような文を含まない代替実行パスよりも、多かれ少なかれ発生しやすい場合に、コンパイラが最適化できるようにします。
目次 |
構文
[
[
likely
]
]
|
(1) | ||||||||
[
[
unlikely
]
]
|
(2) | ||||||||
説明
これらの属性はラベルおよび文(宣言文を除く)に適用できます。同じラベルまたは文に同時に適用することはできません。
1)
ある文に適用され、その文を含む実行パスが、そのような文を含まない代替実行パスよりも高い確率で発生する場合に、コンパイラが最適化を行うことを可能にする。
2)
この文に適用され、その文を含む実行パスが、そのような文を含まない代替実行パスよりも発生する可能性が低い場合に、コンパイラが最適化を行うことを可能にします。
実行パスは、そのラベルへのジャンプを含む場合にのみ、そのラベルを含むと見なされます:
int f(int i) { switch (i) { case 1: [[fallthrough]]; [[likely]] case 2: return 1; } return 2; }
タグ内のC++コード、C++固有の用語は翻訳せず、元のフォーマットを保持しています)
i == 2 は他のどの値よりも可能性が高いと見なされますが、 [ [ 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 { if (n > 0) [[likely] (注:元のテキストは閉じ括弧のみのため、日本語でも同じ記号を保持します)] (注:元のテキストが単一の閉じ角括弧のみであるため、HTMLタグを保持したまま、内容を日本語に翻訳する必要はありません。指定された条件に従い、この記号はそのまま維持されます。) 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{}; // 副作用を保証する 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" : "異なる") << '\n'; auto benchmark = [](auto fun, auto rem) { const auto start = std::chrono::high_resolution_clock::now (注:指示に従い、HTMLタグ・属性、タグ内のテキスト、C++固有用語は翻訳せず、元のフォーマットを保持しています)(); for (auto size{1ULL}; size != 10'000'000ULL; ++size) sink = fun(gen_random()); const std::chrono::duration (注:指示に従い、HTMLタグ・属性、C++固有用語は翻訳せず、テキスト部分のみを日本語に翻訳しました。元のフォーマットは保持されています)<double> diff = std::chrono::high_resolution_clock::now (注:指定された条件により、HTMLタグ・属性、タグ内のテキスト、C++固有の用語は翻訳対象外のため、元のテキストを保持しています)() - 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 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 時間: 0.579122 秒 (with attributes) 時間: 0.722553 秒 (without attributes) 時間: 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]