【问题标题】:Why is numpy much slower than matlab on a digitize example?为什么在数字化示例中 numpy 比 matlab 慢得多?
【发布时间】:2012-03-15 17:01:34
【问题描述】:

我正在比较 numpy 与 matlab 的性能,在某些情况下我观察到 numpy 的速度明显较慢(索引、对数组的简单操作,例如绝对值、乘法、求和等)。让我们看看下面的例子,它涉及到函数 digitize(我打算用它来同步时间戳):

import numpy as np
import time
scale=np.arange(1,1e+6+1)
y=np.arange(1,1e+6+1,10)
t1=time.time()
ind=np.digitize(scale,y)
t2=time.time()
print 'Time passed is %2.2f seconds' %(t2-t1)

结果是:

经过的时间是 55.91 秒

现在让我们尝试使用等效函数 histc

的相同示例 Matlab
scale=[1:1e+6];
y=[1:10:1e+6];
tic
[N,bin]=histc(scale,y);
t=toc;
display(['Time passed is ',num2str(t), ' seconds'])

结果是:

经过的时间是 0.10237 秒

快了 560 倍!

在学习使用 C++ 扩展 Python 时,我实现了自己的 digitize 版本(使用 boost 库进行扩展):

import analysis # my C++ module implementing digitize
t1=time.time()
ind2=analysis.digitize(scale,y)
t2=time.time()
print 'Time passed is %2.2f seconds' %(t2-t1)
np.all(ind==ind2) #ok

结果是:

经过的时间是 0.02 秒

由于我的 digitize 版本假设输入都是单调的,所以有一点作弊,这可以解释为什么它甚至比 Matlab 更快。但是,对大小为 1e+6 的数组进行排序需要 0.16 秒(使用 numpy.sort),因此与 Matlab 函数 histc.

所以问题是:

  • 为什么 numpy.digitize 这么慢?这个函数不应该用编译和优化的代码编写吗?
  • 为什么我自己的 digitize 版本比 numpy.digitize 快得多,但仍然比 Matlab 慢(鉴于我假设输入已经排序,我很有信心使用最快的算法)?

我正在使用 Fedora 16,并且我最近安装了 ATLAS 和 LAPACK 库(但性能发生了如此大的变化)。我应该重建numpy吗?我不确定我安装的 numpy 是否使用适当的库来获得最大速度,也许 Matlab 正在使用更好的库。

更新

根据目前的答案,我想强调一下,如果有人,Matlab 函数 histc 不等同于numpy.histogram (在这种情况下像我一样)不关心直方图。我需要 hisc 的第二个输出,它是从输入值到所提供输入箱的索引的映射。这样的输出由 numpy 函数 digitizesearchsorted 提供。正如其中一个答案所说,searchsorteddigitize 快得多。然而,searchsorted 仍然比 Matlab 慢 2 倍

t1=time.time()
ind3=np.searchsorted(y,scale,"right")
t2=time.time()
print 'Time passed is %2.2f seconds' %(t2-t1)

np.all(ind==ind3) #ok

结果是

经过的时间是 0.21 秒

所以现在的问题是:

  1. 如果有一个等效的函数 numpy.searchsorted,那么拥有 numpy.digitize 是什么意思,它快了 280 倍 ?

  2. 为什么Matlab函数histc(也提供numpy.searchsorted的输出)比快2倍 >numpy.searchsorted?

【问题讨论】:

  • 您能否编辑您的问题以包含您的 C++ 版本的 digitize?很难说为什么你的版本在没有看到它的情况下可能与 numpy 中的不同......
  • 我看不出所使用的任何功能与 ATLAS 甚至 LAPACK 有什么关系。这些库不会在这里使用,因此不会影响结果。

标签: python performance matlab numpy


【解决方案1】:

首先,让我们看看为什么numpy.digitize 很慢。如果发现您的 bin 是单调的,则根据 bin 是非递减还是非递增调用这些函数之一(此代码可在 numpy git repo 的 numpy/lib/src/_compiled_base.c 中找到):

static npy_intp
incr_slot_(double x, double *bins, npy_intp lbins)
{
    npy_intp i;

    for ( i = 0; i < lbins; i ++ ) {
        if ( x < bins [i] ) {
            return i;
        }
    }
    return lbins;
}

static npy_intp
decr_slot_(double x, double * bins, npy_intp lbins)
{
    npy_intp i;

    for ( i = lbins - 1; i >= 0; i -- ) {
        if (x < bins [i]) {
            return i + 1;
        }
    }
    return 0;
}

如您所见,它正在执行线性搜索。线性搜索比二分搜索慢得多,所以你可以回答为什么它慢。我将在 numpy 跟踪器上为此打开 a ticket

其次,我认为 Matlab 实际上比您的 C++ 代码慢,因为 Matlab also assumes 箱是单调非递减的。

【讨论】:

  • 好点,考虑到我的低声望,很遗憾我不能投票赞成这个答案:-)
  • 我认为 Matlab 比我的 C++ 代码慢得多,因为我在作弊,不仅我的函数假定非递减 bin,而且非递减输入值(上面代码中的 x)。我相信如果我会概括我的代码,例如通过使用 python 包装器对输入进行排序,然后应用我的函数,然后根据初始排序重新排序,我的 C++ 代码会比 matlab 慢。我认为它也很有用告诉 digitize(或 searchsorted)输入是否已经排序,以加快速度(获得 5 倍的潜在因子,使用我的代码作为基准)。
【解决方案2】:

在问题得到回答之前,需要解决几个子问题:

  1. 为了获得更可靠的结果,您应该运行几个 测试的迭代并平均它们的结果。这个会 以某种方式消除启动效果,这没有任何关系 与算法。另外,尝试使用更大的数据来做同样的事情 目的。

  2. 跨框架使用相同的算法。这已经 已在此处的其他答案中得到解决。

  3. 确保算法非常相似。他们如何 利用系统资源?如何迭代内存?如果(只是一个 示例)Matlab 算法使用 repmat 而 numpy 不会, 比较是不公平的。

  4. 对应的框架如何并行化?这可能 连接到您的个人机器/处理器配置。 Matlab 确实并行化了一些(但不是全部)内置函数。 我不知道 numpy/CPython。

  5. 使用内存分析器来找出两种实现方式 从性能的角度来表现。

之后(这只是一个猜测)我们可能会发现,numpy 的行为通常比 Matlab 慢。 SO这里的许多问题都得出相同的结论。一种解释可能是,Matlab 更容易优化数组访问,因为它不需要考虑通用对象的整个集合(如 CPython)。对数学数组的要求远低于对一般数组的要求。另一方面,numpy 确实使用了 CPython,它必须为完整的 python 库提供服务——不仅仅是 numpy。但是,根据这个comparison test(以及许多其他人)Matlab 仍然很慢......

【讨论】:

    【解决方案3】:

    我无法回答为什么numpy.digitize() 这么慢——我可以在我的机器上确认你的时间安排。

    numpy.searchsorted() 函数与numpy.digitize() 的作用基本相同,但效率很高。

    ind = np.searchsorted(y, scale, "right")
    

    在我的机器上大约需要 0.15 秒,结果与您的代码完全相同。

    请注意,您的 Matlab 代码所做的事情与这两个函数不同——它相当于numpy.histogram()

    【讨论】:

    • 感谢 Sven,np.searchsorted 与 numpy.digitize 做同样的事情要快得多!在我的示例中,我得到 0.21 秒。正如我给@JoshAdel 写的那样,numpy.histogram() 不等同于 Matlab 中的 histc,它可能更类似于 hist(除了 hist 将提供的区间的中心作为 bin 而不是边缘)。 histc 的美妙之处在于它不仅返回直方图(我根本不需要),还返回从输入到边缘的映射(我需要用于同步目的)。仍然没有解决的是为什么 searchsorted 比 matlab 慢 2 倍(histc 的第二个输出)。
    【解决方案4】:

    我不认为您在比较 numpy 和 matlab 中的相同函数。据我查看文档可知,相当于histcnp.histogram。我没有 matlab 做比较,但是当我在我的机器上执行以下操作时:

    In [7]: import numpy as np
    
    In [8]: scale=np.arange(1,1e+6+1)
    
    In [9]: y=np.arange(1,1e+6+1,10)
    
    In [10]: %timeit np.histogram(scale,y)
    10 loops, best of 3: 135 ms per loop
    

    我得到的数字与您从histc 得到的数字大致相同。

    【讨论】:

    • 感谢您的回答,但我担心 Matlab histc 的等价物不是 np.histogram。 histc 还返回(作为第二个输出)从输入值到 bin 的映射。这就是我需要的,np.digitize提供了这个映射,help(np.digitize): "返回输入数组中每个值所属的bin的索引。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2019-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-30
    • 2019-08-28
    相关资源
    最近更新 更多