根据运行时值选择constexpr,并在热循环中使用它



我需要遍历一个vector,读取每个元素,并映射到模除值。模除法对于幂2的除法是快速的。所以,在运行时,我需要在modmod_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);
});
}

我的问题是,run1vsrun2。我更喜欢run2,因为它易于阅读,没有代码重复。但是当我在godbolt (https://godbolt.org/z/3ov59rb5s), AFAIU中检查时,run1run2内联得更好。

那么,有没有更好的方法来编写run函数而不影响性能?

跟进:

我做了一个快速实验。https://quick-bench.com/q/zO5YJtDMbnd10pk53SauOT6Bu0g

看起来对于clang来说,没有区别。但是在GCC(10.3)中,run1有2倍的性能增益。

问题"与cond ? mod_power2 : mod不同的是,它是一个函数指针,更难内联。

不同的lambda没有共同的类型。使用类型擦除(如std::function)有开销,而去虚拟化更难优化。

所以,我看到的唯一选项是能够以"更好的">方式书写run1:

分解lambda的创建;你需要将mod/mod_power2转换为函子(否则我们会遇到与run2Demo相同的问题):

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

演示

相关内容

  • 没有找到相关文章

最新更新