我有一個廣泛使用的函式 foo(int a, int b),我想提供一個特殊版本的 foo,如果 a 是 1,它的表現會有所不同。
a) 我不想遍歷整個代碼庫并將所有出現的 foo(1, b) 更改為 foo1(b) 因為引數的規則可能會改變并且我不想在任何時候繼續瀏覽代碼庫論據的規則改變。
b) 由于性能問題,我不想用“if (a == 1)”測驗來加重函式 foo 的負擔。
在我看來,根據它可以看到的內容呼叫正確的代碼是編譯器的一項基本技能。或者這是 C 可能缺少的功能,需要宏或當前處理的東西。
uj5u.com熱心網友回復:
簡單地寫
inline int foo(int a, int b)
{
if (a==1) {
// skip complex code and call easy code
call_easy(b);
} else {
// complex code here
do_complex(a, b);
}
}
你打電話的時候
foo(1, 10);
優化器將/應該簡單地插入一個call_easy(b).
任何合適的優化器都會行內該函式并檢測該函式是否已被 a==1 呼叫。此外,我認為constexpr其他帖子中提到的整個內容都很好,但在您的情況下并不是必需的。constexpr非常有用,如果您想在編譯時決議值。但是您只是要求在運行時根據值切換代碼路徑。優化器應該能夠檢測到這一點。
為了檢測到這一點,優化器需要在呼叫函式的所有位置查看函式定義。因此inline需要 - 盡管諸如 Visual Studio 之類的編譯器具有“在鏈接時生成代碼”的功能,但這在一定程度上降低了這一要求。
最后,您可能想查看 C 屬性 [[可能]](我認為)。我還沒有與他們合作過,但他們應該告訴編譯器可能的執行路徑并向優化器提供提示。
為什么不嘗試一下并查看除錯器/反匯編中生成的代碼。這會讓您對優化器有所了解。不要忘記優化器可能只在發布版本中處于活動狀態:)
uj5u.com熱心網友回復:
模板在編譯時作業,你想在運行時決定這是不可能的。當且僅當您真的可以使用 constexpr 值呼叫您的函式時,您才可以更改為模板,但呼叫變為 foo<1,2>() 而不是 foo(1,2); “性能問題”……真的很有趣!如果那條比較匯編指令是性能問題......是的,你所做的一切都非常完美:-)
順便說一句:如果您已經使用 constexpr 值呼叫并且該函式在編譯單元中可見,您可以確定編譯器已經知道將其優化掉......
但是,如果您有時確實有 constexpr 值并且可以對函式內部的演算法進行 constexpr 評估,則還有另一種方法可以處理此類事情。在這種情況下,您可以在函式內部決定是否在 constexpr 背景關系中呼叫您的函式。如果是這種情況,您可以執行完整的編譯時演算法,該演算法也可以包含 if ( a== 1)將在編譯時完全評估的演算法。如果該函式未在 constexpr 背景關系中呼叫,則該函式將像以前一樣運行,沒有任何額外的開銷。
要在編譯時做出這樣的決定,我們需要實際的 C 標準 (C 20)!
constexpr int foo( int a, int)
{
if (std::is_constant_evaluated() )
{ // this part is fully evaluated in compile time!
if ( a == 1 )
{
return 1;
}
else
{
return 2;
}
}
else
{ // and the rest runs as before in runtime
if ( a == 0 )
{
return 3;
}
else
{
return 4;
}
}
}
int main()
{
constexpr int res1 = foo( 1,0 ); // fully evaluated during compile time
constexpr int res2 = foo( 2,0 ); // also full compile time
std::cout << res1 << std::endl;
std::cout << res2 << std::endl;
std::cout << foo( 5, 0) << std::endl; // here we go in runtime
std::cout << foo( 0, 0) << std::endl; // here we go in runtime
}
該代碼將回傳:
1
2
4
3
所以我們不需要使用經典模板,不需要更改其余代碼,但如果可能的話,可以進行完整的編譯時優化。
uj5u.com熱心網友回復:
@Sebastian 的建議至少在所有優化級別的簡單情況下都有效,除了在 c 20 模式下的 Ubuntu 20.04 上的 g 9.3.0 中的 -O0 之外。再次感謝。
參見下面的反匯編總是直接呼叫正確的子函式 func1 或 func2 而不是頂級函式 func()。-O0 之后的類似反匯編僅顯示頂級 func() 被呼叫,將決定留給運行時,這是不需要的。
我希望這將適用于生產代碼,并且可能適用于多個硬編碼引數。
Breakpoint 1, main () at p1.cpp:24
24 int main() {
(gdb) disass /m
Dump of assembler code for function main():
6 inline void func(int a, int b) {
7
8 if (a == 1)
9 func1(b);
10 else
11 func2(a,b);
12 }
13
14 void func1(int b) {
15 std::cout << "func1 " << " " << " " << b << std::endl;
16 }
17
18 void func2(int a, int b) {
19 std::cout << "func2 " << a << " " << b << std::endl;
20 }
21
22 };
23
24 int main() {
=> 0x0000555555555286 < 0>: endbr64
0x000055555555528a < 4>: push %rbp
0x000055555555528b < 5>: push %rbx
0x000055555555528c < 6>: sub $0x18,%rsp
0x0000555555555290 < 10>: mov $0x28,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/402516.html
標籤:
