呼叫系結方法this->UpdateB = std::bind(&Test::Update, this); (使用 using 呼叫test.UpdateB())時,其整體性能比直接呼叫函式要慢得多。test.Update()
性能降低接縫也會影響方法中完成的作業。
使用網站quick-bench我運行下面的代碼片段并得到以下結果

#include <functional>
#include <benchmark/benchmark.h>
typedef unsigned u32;
typedef uint64_t u64;
constexpr auto nP = nullptr;
constexpr bool _F = false;
constexpr bool _T = true;
constexpr u64 HIGH_LOAD = 1000000000;
constexpr u64 LOW_LOAD = 10;
struct Test {
u32 counter{100000};
u64 soak{0};
u64 load{10};
bool isAlive{_T};
std::function<bool()> UpdateB;
Test() { UpdateB = std::bind( &Test::Update, this); }
bool Update() {
if (counter > 0) { counter --; }
u64 i = load;
while(i--) { soak = 1; }
isAlive = counter > 0;
return isAlive;
}
};
static void DirectCallLowLoad(benchmark::State& state) {
Test test;
test.load = LOW_LOAD;
for (auto _ : state) { test.Update(); }
}
BENCHMARK(DirectCallLowLoad);
static void DirectCallHighLoad(benchmark::State& state) {
Test test;
test.load = HIGH_LOAD;
for (auto _ : state) { test.Update(); }
}
BENCHMARK(DirectCallHighLoad);
static void BoundCallLowLoad(benchmark::State& state) {
Test test;
test.load = LOW_LOAD;
for (auto _ : state) { test.UpdateB(); }
}
BENCHMARK(BoundCallLowLoad);
static void BoundCallHighLoad(benchmark::State& state) {
Test test;
test.load = HIGH_LOAD;
for (auto _ : state) { test.UpdateB(); }
}
BENCHMARK(BoundCallHighLoad);
期望是...
BoundCallHighLoadDirectCallHighLoad與方法的負載相比,呼叫開銷的影響較小,因此性能會更接近。DirectCallLowLoad性能將明顯優于DirectCallHighLoad(系結呼叫相同。)系結呼叫不會比直接呼叫慢 5 倍。
我的代碼有什么問題?
為什么系結呼叫這么慢?
如果我使用
std::function<bool(Test*)> UpdateB;
Test() { UpdateB = &Test::Update; } // Test constructor
// call using
test.UpdateB(&test);
更糟糕的是,呼叫test.UpdateB(&test);比直接呼叫慢很多數量級test.Update(),處理負載幾乎沒有區別。
uj5u.com熱心網友回復:
首先,有一個“Show Noop bar”可以查看noop指令的開銷(理論上是1個周期)。結果如下:

因此,可以清楚地看到 DirectCallLowLoad 和 DirectCallHightLoad 被優化出來,并且基準測驗有偏差。實際上,CPU 不可能在大約 2 個周期內執行 1000000000 次迭代。事實上,甚至不可能進行 10 次迭代。盡管有額外的開銷,但同樣的事情適用于其他兩個。
之所以優化這段代碼,是因為 Clang 能夠知道重復soak = 1; load次數相當于添加load到soak. 事實上,它可以進行更高級的數學運算,例如1 2 3 ... N = N*(N-1)/2. 欺騙編譯器并不容易,但一種解決方案是計算數學上證明很難的東西,例如計算冰雹序列。如果編譯器能夠對此進行優化,那么它將能夠證明尚未證明的 Collat??z 猜想。請注意,如果編譯器無法知道應該是 QuickBench 回圈狀態的目的的初始值,則效果會更好。另請注意,任何亂數一般也可能完成這項作業。
UpdateB比Update因為額外的運行時間接更慢。雖然編譯器理論上可以通過專業化優化這樣的代碼,但默認情況下它們通常不會這樣做,因為它太昂貴了。事實上,在這個特定的基準測驗中,他們幾乎無法在沒有任何幫助的情況下做到這一點,因為 QuickBench 回圈狀態應該會阻止編譯器對其進行優化。減少這種開銷的一種解決方案是使用組態檔引導優化,以便編譯器可以根據實踐中的實際函式集進行推測優化。話雖如此,由于回圈狀態,在此 QuickBench 中仍然不夠。
請注意,您可以直接在 QuickBench 中查看匯編代碼,并查看例如前兩種情況的代碼是行內的。這是第一個板凳的主回圈:
mov
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/491642.html
上一篇:Swift讀寫圖片占用大量記憶體
