【问题标题】:xtensor's "operator/" slower than numpy's "/"xtensor 的 "operator/" 比 numpy 的 "/" 慢
【发布时间】:2021-06-15 17:31:58
【问题描述】:

我正在尝试将我之前用 python 编写的一些代码转移到 C++ 中,我目前正在测试 xtensor,看看它是否可以比 numpy 更快地完成我需要的工作。

我的一个函数采用方阵 d 和标量 alpha,并执行元素运算alpha/(alpha+d)。背景:该函数用于测试alpha 的哪个值是“最佳”的,因此它处于循环中,d 始终相同,但alpha 不同。

以下所有时间尺度都是运行该函数的 100 个实例的平均值。

在numpy中,执行此操作大约需要0.27秒,代码如下:

def kfun(d,alpha):
    k = alpha /(d+alpha)
    return k

但 xtensor 大约需要 0.36 秒,代码如下所示:

xt::xtensor<double,2> xk(xt::xtensor<double,2> d, double alpha){
    return alpha/(alpha+d);
}

我也尝试过使用std::vector 的以下版本,但我不想长期使用这个版本,即使它只花了 0.22 秒。

std::vector<std::vector<double>> kloops(std::vector<std::vector<double>> d, double alpha, int d_size){
    for (int i = 0; i<d_size; i++){
        for (int j = 0; j<d_size; j++){
            d[i][j] = alpha/(alpha + d[i][j]);
        }
    }
    return d;
}

我注意到 xtensor 中的 operator/ 使用“延迟广播”,有没有办法让它立即生效?

编辑:

在Python中,函数调用如下,并使用“time”包进行计时

t0 = time.time()
for i in range(100):
    kk = k(dsquared,alpha_squared)
print(time.time()-t0)

在 C++ 中,我调用的函数如下,并使用 chronos 进行计时:

//d is saved as a 1D npy file, an artefact from old code
auto sd2 = xt::load_npy<double>("/path/to/d.npy");

shape = {7084, 7084};
    xt::xtensor<double, 2> xd2(shape);
    for (int i = 0; i<7084;i++){
        for (int j=0; j<7084;j++){
            xd2(i,j) = (sd2(i*7084+j));
        }
    }

auto start = std::chrono::steady_clock::now();
for (int i = 0;i<10;i++){
    matrix<double> kk = kfun(xd2,4000*4000,7084);
}
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
std::cout << "k takes: " << elapsed_seconds.count() << "\n";

如果您希望运行此代码,我建议使用 xd2 作为对角线上为零的对称 7084x7084 随机矩阵。

该函数的输出,一个名为k 的矩阵,然后继续用于其他函数,但我仍然需要d 保持不变,因为它稍后会被重用。

结束编辑

要运行我的 C++ 代码,我在终端中使用以下行:

cd "/path/to/src/" && g++ -mavx2 -ffast-math -DXTENSOR_USE_XSIMD -O3 ccode.cpp -o ccode -I/path/to/xtensorinclude && "/path/to/src/"ccode

提前致谢!

【问题讨论】:

  • 好问题!改进问题的一般评论是最小的可重现示例会更好。具体来说,您可以包含用于调用该函数的几行代码。这将更容易判断副本的微妙之处。更详细的一点是您的std::vector 示例似乎没有分配回报。此外,总的来说,您应该使用for (int i = 0; i&lt;d.size();i++)for (int j = 0; j&lt;d[i].size();j++)(更好的是用int 替换size_t。如果您可以编辑以澄清这些事情,那就太好了
  • @TomdeGeus 嗨!感谢您的评论。我只是想澄清一下,显然我对此很陌生,但我想我认为如果我只是指定大小而不是每次都要求它计算出来,那么函数会更快吗?这是错的吗?此函数在循环中调用,特别是使用不同的 alpha 值。另外,我的 std::vector 示例没有分配回报是什么意思?我知道您可以为更改输入的函数执行 void 函数,例如,我是否不小心这样做了,而不是输出更改后的“d”?
  • 编译器可能会优化(某些)大小调用,但老实说,您的 d_size 选项从我的屏幕上掉了出来,所以我没有注意到它并假设您可能有错字。对于向量示例,您有一些未定义的 d2,但您更正了这个,所以一切都很好!
  • 关于这个问题的小评论。通过最新的编辑,问题变得更好了。更好的是确保它是可重现的:任何人都可以复制你的代码 sn-p 并直接编译和运行它。为此,您可以将dsquaredxd2 简单地作为随机数矩阵引入
  • @TomdeGeus 你介意吗,我在编辑底部写的好吗?还是我应该上传一个示例?

标签: python c++ numpy xtensor


【解决方案1】:

C++ 实现的一个问题可能是它创建了一个甚至两个可以避免的临时副本。第一个副本来自不通过引用(或完美转发)传递参数。如果不查看其余代码,很难判断这是否对性能有影响。如果保证在方法xk() 之后不使用d,编译器可能会将其移动到方法中,但更有可能将数据复制到d

要通过引用传递,方法可以改为

xt::xtensor<double,2> xk(const xt::xtensor<double,2>& d, double alpha){
    return alpha/(alpha+d);
}

要使用完美转发(并且还启用其他 xtensor 容器,如 xt::xarrayxt::xtensor_fixed),方法可以更改为

template<typename T>
xt::xtensor<double,2> xk(T&& d, double alpha){
    return alpha/(alpha+d);
}

此外,您可以避免为返回值保留内存。同样,如果不查看其余代码,很难判断。但是,如果该方法在循环内使用,并且返回值始终具有相同的形状,那么在循环外创建返回值并通过引用返回可能是有益的。为此,可以将方法更改为:

template<typename T, typename U>
void xk(T& r, U&& d, double alpha){
    r = alpha/(alpha+d);
}

如果保证dr不指向同一个内存,可以进一步将r包裹在xt::noalias()中,避免在分配结果前临时复制。如果你不通过引用返回,函数的返回值也是如此。

祝你好运,编码愉快!

【讨论】:

  • 嗨!谢谢您的回答!我只是想澄清一下,因为我认为我没有在我的问题中指定,我需要保留原始 d 矩阵,并输出一个名为“k”的矩阵,这是因为我本质上需要尝试许多不同的 alpha 值看看哪个给出了“最好”的结果。我相信您的建议不允许这样做,对吗?抱歉,这是我的第一个 stackoverflow 问题,我想我没有提供足够的信息。
  • 对我来说,在 C++ 方面的这个问题中重要的是@abic011 发现向量示例相当快,而原则上包含您在此答案中引用的相同副本。因此,消除副本可以解释与确实不会复制的 NumPy 的差异(如果@abic011 可以确认,但std::vector 版本不能确认。使用相同数量的副本,两者在最坏的情况下应该同样快/慢
  • 亲爱的@abic001,关于保持矩阵d不变的问题,这个没问题。您仍然可以将d 作为 const 参考(我的第一个建议)传递,并且方法调用会快很多。矩阵d 不会被函数改变。关于 Matrix k,也许编辑您的问题以显示其用法?
  • 抱歉!复制时我打错了,才意识到!现在实际上是 0.2 秒,好多了,非常感谢!
  • @abic011 很高兴听到它解决了所有问题。只是为了确保它不是const 而是&amp;。后者强制对象通过引用(仅一个内存地址)而不是作为副本(整个数据,在您的情况下为 4900 万个条目)传递
猜你喜欢
  • 1970-01-01
  • 2018-12-13
  • 2022-08-18
  • 2018-03-03
  • 1970-01-01
  • 1970-01-01
  • 2014-02-24
  • 1970-01-01
  • 2018-04-24
相关资源
最近更新 更多