[英]choose a constexpr based on a runtime value and use it inside a hot loop
我需要遍歷一個vector
,讀取每個元素,並將 map 取模除法值。 對於 power2 的除數,模除法很快。 因此,我需要在運行時在mod
和mod_power2
之間進行選擇。 以下是一個粗略的提綱。 請假設我正在使用模板訪問向量。
位操作技巧取自https://graphics.stanford.edu/~seander/bithacks.html
static inline constexpr bool if_power2(int v) {
return v && !(v & (v - 1));
}
static inline constexpr int mod_power2(int val, int num_partitions) {
return val & (num_partitions - 1);
}
static inline constexpr int mod(int val, int num_partitions) {
return val % num_partitions;
}
template<typename Func>
void visit(const std::vector<int> &data, Func &&func) {
for (size_t i = 0; i < data.size(); i++) {
func(i, data[i]);
}
}
void run1(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
if (if_power2(num_partitions)) {
visit(v1,
[&](size_t i, int v) {
v2[i] = mod_power2(v, num_partitions);
});
} else {
visit(v1,
[&](size_t i, int v) {
v2[i] = mod(v, num_partitions);
});
}
}
void run2(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
const auto part = if_power2(num_partitions) ? mod_power2 : mod;
visit(v1, [&](size_t i, int v) {
v2[i] = part(v, num_partitions);
});
}
我的問題是, run1
與run2
。 我更喜歡run2
,因為它易於閱讀並且沒有代碼重復。 但是,當我在 godbolt ( https://godbolt.org/z/3ov59rb5s ) AFAIU 中檢查兩者時, run1
的內聯效果優於run2
。
那么,有沒有更好的方法在不影響性能的情況下編寫run
function?
跟進:
我對此進行了快速測試。 https://quick-bench.com/q/zO5YJtDMbnd10pk53SauOT6Bu0g
看起來對於 clang,沒有區別。 但是對於run1
(10.3),run1 有 2 倍的性能增益。
“問題”與cond? mod_power2: mod
cond? mod_power2: mod
是一個function指針,比較難內聯。
不同的 lambda 沒有共同的類型。 使用諸如std::function
類的類型擦除會產生開銷,而且去虛擬化更難優化。
所以,我看到的唯一選擇是能夠以“更好”的方式編寫run1
:
分解創建lambda; 您需要將mod
/ mod_power2
轉換為仿函數(否則我們會遇到與run2
Demo相同的問題):
void run3(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
auto make_lambda = [&](auto&& f){
return [&](std::size_t i, int v){ v2[i] = f(v, num_partitions); };
};
if (if_power2(num_partitions)) {
visit(v1, make_lambda(mod_power2));
} else {
visit(v1, make_lambda(mod));
}
}
如果您不能將mod
/ mod_power2
更改為仿函數,則創建一個仿函數而不是 lambda 以在編譯時獲取它們的地址:
template <int (*f)(int val, int num_partitions)>
struct Functor
{
std::vector<int> &v2;
int num_partitions;
void operator ()(size_t i, int v) const
{
v2[i] = f(v, num_partitions);
}
};
void run4(const std::vector<int> &v1, int num_partitions, std::vector<int> &v2) {
if (if_power2(num_partitions)) {
visit(v1, Functor<mod_power2>{v2, num_partitions});
} else {
visit(v1, Functor<mod>{v2, num_partitions});
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.