c++ - 高效的函子调度器
问题描述
我需要帮助理解函子调度程序的两个不同版本,请参见此处:
#include <cmath>
#include <complex>
double* psi;
double dx = 0.1;
int range;
struct A
{
double operator()(int x) const
{
return dx* (double)x*x;
}
};
template <typename T>
void dispatchA()
{
constexpr T op{};
for (int i=0; i<range; i++)
psi[i]+=op.operator()(i);
}
template <typename T>
void dispatchB(T op)
{
for (int i=0; i<range; i++)
psi[i]+=op.operator()(i);
}
int main(int argc, char** argv)
{
range= argc;
psi = new double[range];
dispatchA<A>();
// dispatchB<A>(A{});
}
住在https://godbolt.org/z/93h5T46oq
调度程序会在一个大循环中被多次调用,所以我需要确保我做对了。在我看来,这两个版本都不必要地复杂,因为函子的类型在编译时是已知的。DispatchA,因为它不必要地创建了一个 (constexpr) 对象。DispatchB,因为它一遍又一遍地传递对象。
当然,这些可以通过 a) 在函子中创建一个静态函数来解决,但是静态函数是不好的做法,对吧?b) 在调度程序中创建函子的静态实例,但随后对象的生命周期会增长到程序的生命周期。
话虽这么说,我不知道足够的组装来有意义地比较这两种方法。有没有更优雅/更有效的方法?
解决方案
假设A
是无状态的,就像在您的示例中一样,并且没有非静态数据成员,它们是相同的。编译器足够聪明,可以看到对象的构造是空操作并忽略它。让我们稍微清理一下您的代码以获得干净的程序集,我们可以很容易地推断出:
struct A {
double operator()(int) const noexcept;
};
void useDouble(double);
int genInt();
void dispatchA() {
constexpr A op{};
auto const range = genInt();
for (int i = 0; i < range; i++) useDouble(op(genInt()));
}
void dispatchB(A op) {
auto const range = genInt();
for (int i = 0; i < range; i++) useDouble(op(genInt()));
}
在这里,输入来自哪里以及输出去哪里被抽象出来。生成的程序集仅因op
对象的创建方式而异。用 GCC 11.1 编译它,我得到相同的程序集生成。不会发生任何创建或初始化A
。
推荐阅读
- php - 将 JSON 与 MySQL 数据库进行比较以查找已删除的记录
- php - Laravel:在负载平衡环境中获取另一个用户的会话 ID
- mongodb - 将 mongodb 嵌套数据结构结果缩减为列表
- ios - 在 nativescript-ui-sidedrawer 的抽屉下是否仍然可以有透明背景
- c# - 图片 url 正确,但通过 .net core 2.0 应用程序中的 httpClient 请求给出空响应
- c# - 从部分视图编辑器获取更新的数据并传递给控制器
- elasticsearch - ElasticSearch 搜索有时无法搜索文档
- c# - 如何在 Forge API 中向元素添加参数
- javascript - jQuery可拖动功能导致PrimeFaces JSF页面出错
- html - CSS:为什么颜色代码 #999 的颜色在 Chrome 和 Firefox 中不同?