creating small arrays in cython takes a humongous amount of time

前端 未结 2 1815
既然无缘
既然无缘 2021-01-05 06:32

I was writing a new random number generator for numpy that produces random numbers according to an arbitrary distribution when I came across this really weird behavior:

相关标签:
2条回答
  • 2021-01-05 06:41

    There is a discussion of this on the Cython list that might have some useful suggestions: https://groups.google.com/forum/#!topic/cython-users/CwtU_jYADgM

    Generally though I try to allocate small arrays outside of Cython, pass them in and re-use them in subsequent calls to the method. I understand that this is not always an option.

    0 讨论(0)
  • 2021-01-05 06:47

    Creating np.empty inside the Cython function has some overhead as you already saw. Here you will see an example about how to create the empty array and pass it to the Cython module in order to fill with the correct values:

    n=10:

    numpy.searchsorted: 1.30574745517
    cython O(1): 3.28732016088
    cython no array declaration 1.54710909596
    

    n=100:

    numpy.searchsorted: 4.15200545373
    cython O(1): 13.7273431067
    cython no array declaration 11.4186086744
    

    As you already pointed out, the numpy version scales better since it is O(len(u)*long(len(a))) and this algorithm here is O(len(u)*len(a))...

    I also tried to use Memoryview, basically changing np.ndarray[double, ndim=1] by double[:], but the first option was faster in this case.

    The new .pyx file is:

    from __future__ import division
    import numpy as np
    cimport numpy as np
    cimport cython
    
    @cython.boundscheck(False)
    @cython.wraparound(False)
    def JustLoop(np.ndarray[double, ndim=1] a, np.ndarray[double, ndim=1] u,
                 np.ndarray[int, ndim=1] r):
        cdef int i,j
        for j in range(u.shape[0]):
            if u[j] < a[0]:
                r[j] = 0
                continue
    
            if u[j] > a[a.shape[0]-1]:
                r[j] = a.shape[0]-1
                continue
    
            for i in range(1, a.shape[0]):
                if u[j] >= a[i-1] and u[j] < a[i]:
                    r[j] = i
                    break
    
    @cython.boundscheck(False)
    @cython.wraparound(False)
    def WithArray(np.ndarray[double, ndim=1] a, np.ndarray[double, ndim=1] u):
        cdef np.ndarray[np.int_t, ndim=1] r=np.empty(u.shape[0],dtype=int)
        cdef int i,j
        for j in range(u.shape[0]):
            if u[j] < a[0]:
                r[j] = 0
                continue
    
            if u[j] > a[a.shape[0]-1]:
                r[j] = a.shape[0]-1
                continue
    
            for i in range(1, a.shape[0]):
                if u[j] >= a[i-1] and u[j] < a[i]:
                    r[j] = i
                    break
        return r
    

    The new .py file:

    import numpy
    import subprocess
    import timeit
    
    #Compile the cython modules before importing them
    subprocess.call(['python', 'setup.py', 'build_ext', '--inplace'])
    from test import *
    
    sstr="""
    import test
    import numpy
    u=numpy.random.random(10)
    a=numpy.random.random(10)
    a=numpy.cumsum(a)
    a/=a[-1]
    a.sort()
    r = numpy.empty(u.shape[0], dtype=int)
    """
    
    print "numpy.searchsorted:",timeit.timeit('numpy.searchsorted(a,u)',sstr)
    print "cython O(1):",timeit.timeit('test.WithArray(a,u)',sstr)
    print "cython no array declaration",timeit.timeit('test.JustLoop(a,u,r)',sstr)
    
    0 讨论(0)
提交回复
热议问题