NumPy:同时显示max()和min()的函数


109

numpy.amax()将在数组中找到最大值,numpy.amin()对最小值进行相同操作。如果要同时找到max和min,则必须调用两个函数,这需要两次(非常大)数组传递,这似乎很慢。

numpy API中是否有一个函数可以只通过一次数据就找到max和min?


1
多大是多大?如果有时间,我将运行一些测试,将fortran实施与amaxandamin
mgilson 2012年

1
我承认“很大”是主观的。就我而言,我说的是几GB的阵列。
斯图尔特·伯格

很大 我已经编写了一个示例,以便在fortran中进行计算(即使您不了解fortran,也应该很容易理解代码)。与fortran和numpy相比,运行它确实有所不同。(大概,您应该能够从C中获得相同的性能...)我不确定-我想我们需要一个numpy的开发人员来评论为什么我的功能比它们的功能好得多……
mgilson

当然,这并不是一个新颖的想法。例如,boost minmax库(C ++)提供了我正在寻找的算法的实现。
斯图尔特·伯格

3
并不是对所提问题的真正答案,但对此线程的人们可能很感兴趣。向NumPy询问有关添加minmax到有问题的库(github.com/numpy/numpy/issues/9836)。
jakirkham

Answers:


49

numpy API中是否有一个函数可以只通过一次数据就找到max和min?

否。在撰写本文时,尚无此功能。(是的,如果出现这样的功能,其性能会显著优于呼吁numpy.amin()numpy.amax()先后在大阵列。)


31

我认为两次通过数组都不是问题。 考虑以下伪代码:

minval = array[0]
maxval = array[0]
for i in array:
    if i < minval:
       minval = i
    if i > maxval:
       maxval = i

虽然这里只有1个循环,但仍然有2个检查。(而不是有2个循环,每个循环1个检查)。真正节省的唯一事情是1个循环的开销。如果数组确实如您所说很大,那么与实际循环的工作量相比,开销很小。(请注意,这全部是用C实现的,因此循环无论如何都是自由的)。


编辑抱歉,你们四个人对我充满信心。您绝对可以优化它。

这是一些可以通过以下方式编译为python模块的fortran代码f2py(也许有一位Cython专家可以将其与优化的C版本进行比较...):

subroutine minmax1(a,n,amin,amax)
  implicit none
  !f2py intent(hidden) :: n
  !f2py intent(out) :: amin,amax
  !f2py intent(in) :: a
  integer n
  real a(n),amin,amax
  integer i

  amin = a(1)
  amax = a(1)
  do i=2, n
     if(a(i) > amax)then
        amax = a(i)
     elseif(a(i) < amin) then
        amin = a(i)
     endif
  enddo
end subroutine minmax1

subroutine minmax2(a,n,amin,amax)
  implicit none
  !f2py intent(hidden) :: n
  !f2py intent(out) :: amin,amax
  !f2py intent(in) :: a
  integer n
  real a(n),amin,amax
  amin = minval(a)
  amax = maxval(a)
end subroutine minmax2

通过以下方式进行编译:

f2py -m untitled -c fortran_code.f90

现在我们可以测试它了:

import timeit

size = 100000
repeat = 10000

print timeit.timeit(
    'np.min(a); np.max(a)',
    setup='import numpy as np; a = np.arange(%d, dtype=np.float32)' % size,
    number=repeat), " # numpy min/max"

print timeit.timeit(
    'untitled.minmax1(a)',
    setup='import numpy as np; import untitled; a = np.arange(%d, dtype=np.float32)' % size,
    number=repeat), '# minmax1'

print timeit.timeit(
    'untitled.minmax2(a)',
    setup='import numpy as np; import untitled; a = np.arange(%d, dtype=np.float32)' % size,
    number=repeat), '# minmax2'

结果对我来说有点惊人:

8.61869883537 # numpy min/max
1.60417699814 # minmax1
2.30169081688 # minmax2

我不得不说,我并不完全理解它。只是比较np.minminmax1minmax2仍然是一场败仗,所以它不只是一个内存问题...

注意 -将大小增加一个因子10**a并将重复性减少一个因子10**a(保持问题大小恒定)确实会改变性能,但是似乎并不一致,这表明内存性能和函数调用开销之间存在一些相互作用。蟒蛇。即使将minfortran 的简单实现与numpy的效果进行比较也要大约2倍...


21
单遍的优势是存储效率。特别是如果您的阵列足够大以便可以换出,则可能会很大。
Dougal 2012年

4
那不是很正确,它几乎快一半,因为使用这种类型的阵列,内存速度通常是限制因素,因此它可以快一半...
seberg 2012年

3
您并不总是需要两次检查。如果i < minval为true,则i > maxval始终为false,因此,当第二次if替换为时,平均每次迭代只需要进行1.5次检查elif
弗雷德·富

2
小注:我怀疑Cython是获得最优化的Python可调用C模块的方法。Cython的目标是成为一种带类型注释的Python,然后将其机器翻译为C,而f2py只需包装手工编码的Fortran,以便Python可以调用它。一个“更公平”的测试可能是手工编码C,然后使用f2py(!)将其包装为Python。如果您允许使用C ++,那么Shed Skin可能是平衡编码轻松性和性能的最佳选择。
约翰Y

4
从numpy开始,在amd64平台上矢量化了1.8 min和max,在我的core2duo上numpy的性能和此fortran代码一样好。但是,如果数组超过较大的cpu缓存的大小,则单次通过将是有利的。
jtaylor 2014年

23

如果对您有用,有一个用于查找(max-min)的函数称为numpy.ptp

>>> import numpy
>>> x = numpy.array([1,2,3,4,5,6])
>>> x.ptp()
5

但我认为没有一种方法可以一次遍历找到最小和最大值。

编辑: ptp只是在后台调用min和max


2
这很烦人,因为大概是ptp的实现方式,它必须跟踪max和min!
安迪·海登

1
或者它可能只调用max和min,但不确定
jterrace 2012年

3
@hayden原来ptp只是调用max和min
jterrace 2012年

1
那是屏蔽数组代码;ndarray的主要代码在C中。但是事实证明,C代码还在数组上两次迭代:github.com/numpy/numpy/blob/…
Ken Arnold

20

您可以使用Numba,它是使用LLVM的NumPy感知型动态Python编译器。最终的实现非常简单明了:

import numpy
import numba


@numba.jit
def minmax(x):
    maximum = x[0]
    minimum = x[0]
    for i in x[1:]:
        if i > maximum:
            maximum = i
        elif i < minimum:
            minimum = i
    return (minimum, maximum)


numpy.random.seed(1)
x = numpy.random.rand(1000000)
print(minmax(x) == (x.min(), x.max()))

它也应该比Numpy的min() & max()实现更快。所有这些都无需编写任何C / Fortran代码行。

做您自己的性能测试,因为它始终取决于您的体系结构,您的数据,您的软件包版本...


2
>它也应该比Numpy的min()和max()实现要快,我认为这是不对的。numpy不是本机python-它是C.```x = numpy.random.rand(10000000)t = time()for i在range(1000)中:minmax(x)print('numba',time() -t)t = time()for i in range(1000):x.min()x.max()print('numpy',time()-t)```结果为:('numba',10.299750089645386 )('numpy',9.898081064224243)
Authman Apatira

1
@AuthmanApatira:是的,基准总是这样,这就是为什么我说它“ 应该 ”(更快)并“ 进行自己的性能测试,因为它始终取决于您的体系结构,您的数据... ”。就我而言,我尝试使用3台计算机并获得了相同的结果(Numba比Numpy更快),但是在您的计算机中,结果可能有所不同...您是否尝试numba过在基准测试之前执行一次功能以确保它是JIT编译的?另外,如果您使用ipython,为简单起见,我建议您使用它%timeit whatever_code()来衡量时间执行情况。
Peque

3
@AuthmanApatira:无论如何,我试图用这个答案展示的是,有时Python代码(在这种情况下是用N​​umba进行JIT编译)可以和最快的C编译库一样快(至少我们在谈论相同的顺序)数量级),考虑到我们只编写了纯Python代码,这令人印象深刻,您不同意吗?^^
Peque

我确实同意=)另外,感谢先前评论中有关jupyter和在时序代码之外编译函数的提示。
Authman Apatira

1
只是碰到了这一点,在实际情况下并不重要,但是elif允许您的最小值大于您的最大值。例如,长度为1的数组,最大值将等于该值,而最小值为+无限。一次性的任务没什么大不了的,但要深入生产野兽的腹部,并不是好的代码。
Mike Williamson

12

通常,您可以一次处理两个元素,并且只将较小的元素与临时最小值进行比较,将较大的元素与临时最大值进行比较,从而减少针对minmax算法的比较量。平均而言,与单纯的方法相比,只需要比较3/4。

这可以用c或fortran(或任何其他低级语言)实现,并且在性能方面几乎是无与伦比的。我正在使用 说明原理,并获得非常快速的,与dtype无关的实现:

import numba as nb
import numpy as np

@nb.njit
def minmax(array):
    # Ravel the array and return early if it's empty
    array = array.ravel()
    length = array.size
    if not length:
        return

    # We want to process two elements at once so we need
    # an even sized array, but we preprocess the first and
    # start with the second element, so we want it "odd"
    odd = length % 2
    if not odd:
        length -= 1

    # Initialize min and max with the first item
    minimum = maximum = array[0]

    i = 1
    while i < length:
        # Get the next two items and swap them if necessary
        x = array[i]
        y = array[i+1]
        if x > y:
            x, y = y, x
        # Compare the min with the smaller one and the max
        # with the bigger one
        minimum = min(x, minimum)
        maximum = max(y, maximum)
        i += 2

    # If we had an even sized array we need to compare the
    # one remaining item too.
    if not odd:
        x = array[length]
        minimum = min(x, minimum)
        maximum = max(x, maximum)

    return minimum, maximum

绝对Peque提出的天真的方法快:

arr = np.random.random(3000000)
assert minmax(arr) == minmax_peque(arr)  # warmup and making sure they are identical 
%timeit minmax(arr)            # 100 loops, best of 3: 2.1 ms per loop
%timeit minmax_peque(arr)      # 100 loops, best of 3: 2.75 ms per loop

如预期的那样,新的minmax实现仅花费朴素实现(2.1 / 2.75 = 0.7636363636363637)的时间的3/4左右


1
在我的机器上,您的解决方案并不比Peque的解决方案快。Numba 0.33。
John Zwinck

@johnzwinck您是否在我回答的基准中使用了其他基准?如果可以,可以分享吗?但这是可能的:我也注意到较新版本中的一些回归。
MSeifert

我运行了您的基准。解决方案的时间与@Peque的时间几乎相同(〜2.8 ms)。
John Zwinck

@JohnZwinck太奇怪了,我再次对其进行了测试,并且在我的计算机上它的确快得多。也许与numba和LLVM有关,这取决于硬件。
MSeifert

我现在尝试使用另一台计算机(功能强大的工作站),获得2.4毫秒,而获得Peque的则为2.6毫秒。因此,一个小小的胜利。
约翰·兹温克

11

给出以下想法的一些想法:

import numpy as np


def extrema_np(arr):
    return np.max(arr), np.min(arr)
import numba as nb


@nb.jit(nopython=True)
def extrema_loop_nb(arr):
    n = arr.size
    max_val = min_val = arr[0]
    for i in range(1, n):
        item = arr[i]
        if item > max_val:
            max_val = item
        elif item < min_val:
            min_val = item
    return max_val, min_val
import numba as nb


@nb.jit(nopython=True)
def extrema_while_nb(arr):
    n = arr.size
    odd = n % 2
    if not odd:
        n -= 1
    max_val = min_val = arr[0]
    i = 1
    while i < n:
        x = arr[i]
        y = arr[i + 1]
        if x > y:
            x, y = y, x
        min_val = min(x, min_val)
        max_val = max(y, max_val)
        i += 2
    if not odd:
        x = arr[n]
        min_val = min(x, min_val)
        max_val = max(x, max_val)
    return max_val, min_val
%%cython -c-O3 -c-march=native -a
#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True, infer_types=True


import numpy as np


cdef void _extrema_loop_cy(
        long[:] arr,
        size_t n,
        long[:] result):
    cdef size_t i
    cdef long item, max_val, min_val
    max_val = arr[0]
    min_val = arr[0]
    for i in range(1, n):
        item = arr[i]
        if item > max_val:
            max_val = item
        elif item < min_val:
            min_val = item
    result[0] = max_val
    result[1] = min_val


def extrema_loop_cy(arr):
    result = np.zeros(2, dtype=arr.dtype)
    _extrema_loop_cy(arr, arr.size, result)
    return result[0], result[1]
%%cython -c-O3 -c-march=native -a
#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True, infer_types=True


import numpy as np


cdef void _extrema_while_cy(
        long[:] arr,
        size_t n,
        long[:] result):
    cdef size_t i, odd
    cdef long x, y, max_val, min_val
    max_val = arr[0]
    min_val = arr[0]
    odd = n % 2
    if not odd:
        n -= 1
    max_val = min_val = arr[0]
    i = 1
    while i < n:
        x = arr[i]
        y = arr[i + 1]
        if x > y:
            x, y = y, x
        min_val = min(x, min_val)
        max_val = max(y, max_val)
        i += 2
    if not odd:
        x = arr[n]
        min_val = min(x, min_val)
        max_val = max(x, max_val)
    result[0] = max_val
    result[1] = min_val


def extrema_while_cy(arr):
    result = np.zeros(2, dtype=arr.dtype)
    _extrema_while_cy(arr, arr.size, result)
    return result[0], result[1]

extrema_loop_*()方法与此处提出的方法相似,而extrema_while_*()方法基于此处的代码)

以下时间:

bm

表示extrema_while_*()最快,extrema_while_nb()最快。无论如何,extrema_loop_nb()extrema_loop_cy()解决方案的性能都优于仅使用NumPy的方法(单独使用np.max()np.min()单独使用)。

最后,请注意,所有这些都不如np.min()/ 灵活np.max()(就n-dim支持,axis参数等而言)。

(完整的代码在这里


2
似乎如果使用@njit(fastmath = True),您可以额外获得10%的速度extrema_while_nb
argenisleon

10

没有人提到numpy.percentile,所以我想我会的。如果您要求[0, 100]百分位,它将为您提供两个元素的数组,最小(第0个百分位)和最大(第100个百分位)。

但是,它不能满足OP的目的:它不比单独的min和max快。这可能是由于一些机制将允许非极端百分位数(一个困难的问题,这应该需要更长的时间)。

In [1]: import numpy

In [2]: a = numpy.random.normal(0, 1, 1000000)

In [3]: %%timeit
   ...: lo, hi = numpy.amin(a), numpy.amax(a)
   ...: 
100 loops, best of 3: 4.08 ms per loop

In [4]: %%timeit
   ...: lo, hi = numpy.percentile(a, [0, 100])
   ...: 
100 loops, best of 3: 17.2 ms per loop

In [5]: numpy.__version__
Out[5]: '1.14.4'

如果仅[0, 100]要求,Numpy的未来版本可能会出现特殊情况以跳过正常的百分位数计算。在不向接口添加任何内容的情况下,有一种方法可以在一次调用中向Numpy询问最小值和最大值(与接受的答案中所说的相反),但是该库的标准实现没有利用这种情况来实现这一点值得。


9

这是一个古老的话题,但是无论如何,如果有人再次看过这个话题……

同时查找最小值和最大值时,可以减少比较次数。如果您正在比较浮点数(我猜是这样),这可能会节省一些时间,尽管不会增加计算复杂度。

代替(Python代码):

_max = ar[0]
_min=  ar[0]
for ii in xrange(len(ar)):
    if _max > ar[ii]: _max = ar[ii]
    if _min < ar[ii]: _min = ar[ii]

您可以先比较数组中的两个相邻值,然后再将较小的一个与当前最小值进行比较,将较大的一个与当前最大值进行比较:

## for an even-sized array
_max = ar[0]
_min = ar[0]
for ii in xrange(0, len(ar), 2)):  ## iterate over every other value in the array
    f1 = ar[ii]
    f2 = ar[ii+1]
    if (f1 < f2):
        if f1 < _min: _min = f1
        if f2 > _max: _max = f2
    else:
        if f2 < _min: _min = f2
        if f1 > _max: _max = f1

此处的代码是用Python编写的,显然为了提高速度,您可以使用C或Fortran或Cython,但是通过这种方式,您每次迭代进行3个比较,使用len(ar)/ 2次迭代,得出3/2 * len(ar)比较。与此相反,以“显而易见的方式”进行比较,则每次迭代都要进行两次比较,从而得出2 * len(ar)比较。为您节省25%的比较时间。

也许某天某人会发现这很有用。


6
你有基准吗?在现代x86硬件上,您具有在第一个变体中使用的最小和最大机器指令,这些指令避免了分支的需要,同时您的代码放入了可能与硬件映射不佳的控件相关性。
jtaylor 2014年

我还没有 如果有机会,我会做的。我认为很明显,纯python代码将不适合任何明智的编译实现,但我想知道是否可以在Cython中看到提速...
Bennet

13
在numpy中有一个minmax实现,由来使用np.bincount,请参见此处。它没有使用您指出的技巧,因为事实证明它比幼稚的方法慢2倍。有从一个链接PR到的这两种方法的一些综合性基准测试。
2015年

5

乍一看,似乎可以解决问题:numpy.histogram

count, (amin, amax) = numpy.histogram(a, bins=1)

......但如果你看看为该函数,它只是简单地调用a.min()a.max()独立,因此无法避免业绩的担忧在这个问题解决。:-(

同样的,scipy.ndimage.measurements.extrema看起来像一个可能性,但它也只是调用a.min()a.max()独立。


3
np.histogram并非总是如此,因为返回的(amin, amax)值是bin的最小值和最大值。例如,如果有a = np.zeros(10),则np.histogram(a, bins=1)返回(array([10]), array([-0.5, 0.5]))(amin, amax)在这种情况下,用户正在寻找=(0,0)。
eclark '17

3

无论如何,这对我来说都是值得的,所以我将在这里为任何有兴趣的人提出最困难,最不优雅的解决方案。我的解决方案是在C ++中以一次通过算法实现多线程min-max,然后使用它创建一个Python扩展模块。这项工作需要花费一些开销来学习如何使用Python和NumPy C / C ++ API,在这里我将展示代码,并为希望沿这条路走的人提供一些小的解释和参考。

多线程最小/最大

这里没有什么太有趣的。该数组被分解为大小块length / workers。为中的每个块计算最小值/最大值future,然后对其进行扫描以获取全局最小值/最大值。

    // mt_np.cc
    //
    // multi-threaded min/max algorithm

    #include <algorithm>
    #include <future>
    #include <vector>

    namespace mt_np {

    /*
     * Get {min,max} in interval [begin,end)
     */
    template <typename T> std::pair<T, T> min_max(T *begin, T *end) {
      T min{*begin};
      T max{*begin};
      while (++begin < end) {
        if (*begin < min) {
          min = *begin;
          continue;
        } else if (*begin > max) {
          max = *begin;
        }
      }
      return {min, max};
    }

    /*
     * get {min,max} in interval [begin,end) using #workers for concurrency
     */
    template <typename T>
    std::pair<T, T> min_max_mt(T *begin, T *end, int workers) {
      const long int chunk_size = std::max((end - begin) / workers, 1l);
      std::vector<std::future<std::pair<T, T>>> min_maxes;
      // fire up the workers
      while (begin < end) {
        T *next = std::min(end, begin + chunk_size);
        min_maxes.push_back(std::async(min_max<T>, begin, next));
        begin = next;
      }
      // retrieve the results
      auto min_max_it = min_maxes.begin();
      auto v{min_max_it->get()};
      T min{v.first};
      T max{v.second};
      while (++min_max_it != min_maxes.end()) {
        v = min_max_it->get();
        min = std::min(min, v.first);
        max = std::max(max, v.second);
      }
      return {min, max};
    }
    }; // namespace mt_np

Python扩展模块

这是开始变得丑陋的地方。在Python中使用C ++代码的一种方法是实现扩展模块。可以使用distutils.core标准模块来构建和安装该模块。有关这些内容的完整描述,请参见Python文档:https : //docs.python.org/3/extending/extending.html注意:当然,还有其他获得类似结果的方法,引用https://docs.python.org/3/extending/index.html#extending-index

本指南仅涵盖此版本CPython所提供的用于创建扩展的基本工具。Cython,cffi,SWIG和Numba等第三方工具为创建Python的C和C ++扩展提供了更简单,更复杂的方法。

从本质上讲,这条路线可能比实际更学术。话虽这么说,我接下来要做的是,紧紧靠近本教程,创建一个模块文件。这实际上是distutils知道如何处理代码并从中创建Python模块的样板。在执行任何此操作之前,创建一个Python 虚拟环境可能是明智的,这样就不会污染系统软件包(请参阅https://docs.python.org/3/library/venv.html#module-venv)。

这是模块文件:

// mt_np_forpy.cc
//
// C++ module implementation for multi-threaded min/max for np

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <python3.6/numpy/arrayobject.h>

#include "mt_np.h"

#include <cstdint>
#include <iostream>

using namespace std;

/*
 * check:
 *  shape
 *  stride
 *  data_type
 *  byteorder
 *  alignment
 */
static bool check_array(PyArrayObject *arr) {
  if (PyArray_NDIM(arr) != 1) {
    PyErr_SetString(PyExc_RuntimeError, "Wrong shape, require (1,n)");
    return false;
  }
  if (PyArray_STRIDES(arr)[0] != 8) {
    PyErr_SetString(PyExc_RuntimeError, "Expected stride of 8");
    return false;
  }
  PyArray_Descr *descr = PyArray_DESCR(arr);
  if (descr->type != NPY_LONGLTR && descr->type != NPY_DOUBLELTR) {
    PyErr_SetString(PyExc_RuntimeError, "Wrong type, require l or d");
    return false;
  }
  if (descr->byteorder != '=') {
    PyErr_SetString(PyExc_RuntimeError, "Expected native byteorder");
    return false;
  }
  if (descr->alignment != 8) {
    cerr << "alignment: " << descr->alignment << endl;
    PyErr_SetString(PyExc_RuntimeError, "Require proper alignement");
    return false;
  }
  return true;
}

template <typename T>
static PyObject *mt_np_minmax_dispatch(PyArrayObject *arr) {
  npy_intp size = PyArray_SHAPE(arr)[0];
  T *begin = (T *)PyArray_DATA(arr);
  auto minmax =
      mt_np::min_max_mt(begin, begin + size, thread::hardware_concurrency());
  return Py_BuildValue("(L,L)", minmax.first, minmax.second);
}

static PyObject *mt_np_minmax(PyObject *self, PyObject *args) {
  PyArrayObject *arr;
  if (!PyArg_ParseTuple(args, "O", &arr))
    return NULL;
  if (!check_array(arr))
    return NULL;
  switch (PyArray_DESCR(arr)->type) {
  case NPY_LONGLTR: {
    return mt_np_minmax_dispatch<int64_t>(arr);
  } break;
  case NPY_DOUBLELTR: {
    return mt_np_minmax_dispatch<double>(arr);
  } break;
  default: {
    PyErr_SetString(PyExc_RuntimeError, "Unknown error");
    return NULL;
  }
  }
}

static PyObject *get_concurrency(PyObject *self, PyObject *args) {
  return Py_BuildValue("I", thread::hardware_concurrency());
}

static PyMethodDef mt_np_Methods[] = {
    {"mt_np_minmax", mt_np_minmax, METH_VARARGS, "multi-threaded np min/max"},
    {"get_concurrency", get_concurrency, METH_VARARGS,
     "retrieve thread::hardware_concurrency()"},
    {NULL, NULL, 0, NULL} /* sentinel */
};

static struct PyModuleDef mt_np_module = {PyModuleDef_HEAD_INIT, "mt_np", NULL,
                                          -1, mt_np_Methods};

PyMODINIT_FUNC PyInit_mt_np() { return PyModule_Create(&mt_np_module); }

在此文件中,Python和NumPy API都有大量使用,有关更多信息,请参阅:https : //docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple以及NumPy :https : //docs.scipy.org/doc/numpy/reference/c-api.array.html

安装模块

接下来要做的是利用distutils安装模块。这需要一个安装文件:

# setup.py

from distutils.core import setup,Extension

module = Extension('mt_np', sources = ['mt_np_module.cc'])

setup (name = 'mt_np', 
       version = '1.0', 
       description = 'multi-threaded min/max for np arrays',
       ext_modules = [module])

要最终安装该模块,请python3 setup.py install从您的虚拟环境中执行。

测试模块

最后,我们可以测试一下C ++实现是否确实优于NumPy的天真使用。为此,这是一个简单的测试脚本:

# timing.py
# compare numpy min/max vs multi-threaded min/max

import numpy as np
import mt_np
import timeit

def normal_min_max(X):
  return (np.min(X),np.max(X))

print(mt_np.get_concurrency())

for ssize in np.logspace(3,8,6):
  size = int(ssize)
  print('********************')
  print('sample size:', size)
  print('********************')
  samples = np.random.normal(0,50,(2,size))
  for sample in samples:
    print('np:', timeit.timeit('normal_min_max(sample)',
                 globals=globals(),number=10))
    print('mt:', timeit.timeit('mt_np.mt_np_minmax(sample)',
                 globals=globals(),number=10))

这是我从所有这些操作中获得的结果:

8  
********************  
sample size: 1000  
********************  
np: 0.00012079699808964506  
mt: 0.002468645994667895  
np: 0.00011947099847020581  
mt: 0.0020772050047526136  
********************  
sample size: 10000  
********************  
np: 0.00024697799381101504  
mt: 0.002037393998762127  
np: 0.0002713389985729009  
mt: 0.0020942929986631498  
********************  
sample size: 100000  
********************  
np: 0.0007130410012905486  
mt: 0.0019842900001094677  
np: 0.0007540129954577424  
mt: 0.0029724110063398257  
********************  
sample size: 1000000  
********************  
np: 0.0094779249993735  
mt: 0.007134920000680722  
np: 0.009129883001151029  
mt: 0.012836456997320056  
********************  
sample size: 10000000  
********************  
np: 0.09471094200125663  
mt: 0.0453535050037317  
np: 0.09436299200024223  
mt: 0.04188535599678289  
********************  
sample size: 100000000  
********************  
np: 0.9537652180006262  
mt: 0.3957935369980987  
np: 0.9624398809974082  
mt: 0.4019058070043684  

这些结果远没有线程早期的结果令人鼓舞,后者表明速度大约是3.5倍,并且没有包含多线程。我获得的结果在一定程度上是合理的,我希望线程的开销会占据主导地位,直到阵列变得非常大为止,这时性能将开始接近std::thread::hardware_concurrency x的提高。

结论

对于某些NumPy代码,当然存在针对特定应用程序进行优化的空间,尤其是在多线程方面。对我而言,是否值得付出努力尚不明确,但这显然是一项不错的练习(或其他方法)。我认为也许学习一些像Cython这样的“第三方工具”可能会更好地利用时间,但是谁知道呢。


1
我开始研究您的代码,知道一些C ++,但仍然没有使用std :: future和std :: async。在您的“ min_max_mt”模板函数中,如何知道每个工作人员在触发和检索结果之间都已完成?(要求理解,而不是说这有什么问题)
ChrCury78

v = min_max_it->get();。该get方法将阻塞,直到结果准备就绪并返回为止。由于循环遍历每个未来,因此只有完成所有循环后,循环才会结束。future.get()
内森·查佩尔

0

我想出的最短方法是:

mn, mx = np.sort(ar)[[0, -1]]

但是由于它对数组进行排序,所以它不是最有效的。

另一个简短的方法是:

mn, mx = np.percentile(ar, [0, 100])

这应该更有效,但是会计算结果并返回浮点数。


可耻的是,与本页面中的其他解决方案相比,这两个解决方案是最慢的:m = np.min(a); M = np.max(a)-> 0.54002 ||| m,M = f90_minmax1(a)-> 0.72134 ||| m,M = numba_minmax(a)-> 0.77323 ||| m,M = np.sort(a)[[0,-1]]-> 12.01456 ||| m,M = np.percentile(a,[0,100])-> 11.09418 ||| 以秒为单位的100k元素阵列进行10000次重复
伊萨亚斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.