복붙노트

[PYTHON] 유형이 지정된 메모리보기에 대해 메모리를 할당하는 권장 방법은 무엇입니까?

PYTHON

유형이 지정된 메모리보기에 대해 메모리를 할당하는 권장 방법은 무엇입니까?

형식화 된 메모리보기에 대한 Cython 문서에는 형식화 된 메모리보기에 할당하는 세 가지 방법이 나열되어 있습니다.

외부에서 내 cython 함수에 전달 된 데이터가 없다고 가정하지만 대신 메모리를 할당하고 np.ndarray로 반환하려면 해당 옵션 중 어느 것을 선택해야합니까? 또한 해당 버퍼의 크기가 컴파일 타임 상수가 아니므로 스택에 할당 할 수 없지만 옵션 1에 대해 malloc이 필요하다고 가정합니다.

따라서 다음과 같은 3 가지 옵션이 있습니다.

from libc.stdlib cimport malloc, free
cimport numpy as np
from cython cimport view

np.import_array()

def memview_malloc(int N):
    cdef int * m = <int *>malloc(N * sizeof(int))
    cdef int[::1] b = <int[:N]>m
    free(<void *>m)

def memview_ndarray(int N):
    cdef int[::1] b = np.empty(N, dtype=np.int32)

def memview_cyarray(int N):
    cdef int[::1] b = view.array(shape=(N,), itemsize=sizeof(int), format="i")

놀라운 점은 세 가지 경우 모두 Cython이 메모리 할당을위한 코드를 많이 생성한다는 것입니다. 특히 __Pyx_PyObject_to_MemoryviewSlice_dc_int를 호출하는 것이 좋습니다. 이것은 파이썬 객체를 먼저 생성 한 다음이를 메모리 뷰로 "캐스트"하여 불필요한 오버 헤드가되는 것처럼 보이기 때문에 (여기서는 잘못된 것일 수 있습니다. Cython의 내부 동작에 대한 통찰력은 매우 제한적입니다).

간단한 벤치 마크에서는 세 가지 방법의 차이점을 찾아 낼 수 없습니다. 2. 가장 빠른 것은 마진입니다.

다음 세 가지 방법 중 어느 것을 권장합니까? 또는 다른 옵션이 있습니까?

후속 질문 : 함수에서 메모리보기로 작업 한 후 결과를 np.ndarray로 반환하고 싶습니다. 형식화 된 메모리보기가 최상의 선택입니까 아니면 오히려 아래의 오래된 버퍼 인터페이스를 사용하여 처음에 ndarray를 만들겠습니까?

cdef np.ndarray[DTYPE_t, ndim=1] b = np.empty(N, dtype=np.int32)

해결법

  1. ==============================

    1.여기에 답을 찾아보십시오.

    여기에 답을 찾아보십시오.

    기본 아이디어는 cpython.array.array 및 cpython.array.clone (cython.array. *이 아님)을 원한다는 것입니다.

    from cpython.array cimport array, clone
    
    # This type is what you want and can be cast to things of
    # the "double[:]" syntax, so no problems there
    cdef array[double] armv, templatemv
    
    templatemv = array('d')
    
    # This is fast
    armv = clone(templatemv, L, False)
    

    편집하다

    그 스레드의 벤치 마크는 쓰레기였습니다. 내 타이밍은 다음과 같습니다.

    # cython: language_level=3
    # cython: boundscheck=False
    # cython: wraparound=False
    
    import time
    import sys
    
    from cpython.array cimport array, clone
    from cython.view cimport array as cvarray
    from libc.stdlib cimport malloc, free
    import numpy as numpy
    cimport numpy as numpy
    
    cdef int loops
    
    def timefunc(name):
        def timedecorator(f):
            cdef int L, i
    
            print("Running", name)
            for L in [1, 10, 100, 1000, 10000, 100000, 1000000]:
                start = time.clock()
                f(L)
                end = time.clock()
                print(format((end-start) / loops * 1e6, "2f"), end=" ")
                sys.stdout.flush()
    
            print("μs")
        return timedecorator
    
    print()
    print("INITIALISATIONS")
    loops = 100000
    
    @timefunc("cpython.array buffer")
    def _(int L):
        cdef int i
        cdef array[double] arr, template = array('d')
    
        for i in range(loops):
            arr = clone(template, L, False)
    
        # Prevents dead code elimination
        str(arr[0])
    
    @timefunc("cpython.array memoryview")
    def _(int L):
        cdef int i
        cdef double[::1] arr
        cdef array template = array('d')
    
        for i in range(loops):
            arr = clone(template, L, False)
    
        # Prevents dead code elimination
        str(arr[0])
    
    @timefunc("cpython.array raw C type")
    def _(int L):
        cdef int i
        cdef array arr, template = array('d')
    
        for i in range(loops):
            arr = clone(template, L, False)
    
        # Prevents dead code elimination
        str(arr[0])
    
    @timefunc("numpy.empty_like memoryview")
    def _(int L):
        cdef int i
        cdef double[::1] arr
        template = numpy.empty((L,), dtype='double')
    
        for i in range(loops):
            arr = numpy.empty_like(template)
    
        # Prevents dead code elimination
        str(arr[0])
    
    @timefunc("malloc")
    def _(int L):
        cdef int i
        cdef double* arrptr
    
        for i in range(loops):
            arrptr = <double*> malloc(sizeof(double) * L)
            free(arrptr)
    
        # Prevents dead code elimination
        str(arrptr[0])
    
    @timefunc("malloc memoryview")
    def _(int L):
        cdef int i
        cdef double* arrptr
        cdef double[::1] arr
    
        for i in range(loops):
            arrptr = <double*> malloc(sizeof(double) * L)
            arr = <double[:L]>arrptr
            free(arrptr)
    
        # Prevents dead code elimination
        str(arr[0])
    
    @timefunc("cvarray memoryview")
    def _(int L):
        cdef int i
        cdef double[::1] arr
    
        for i in range(loops):
            arr = cvarray((L,),sizeof(double),'d')
    
        # Prevents dead code elimination
        str(arr[0])
    
    
    
    print()
    print("ITERATING")
    loops = 1000
    
    @timefunc("cpython.array buffer")
    def _(int L):
        cdef int i
        cdef array[double] arr = clone(array('d'), L, False)
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arr[i]
    
        # Prevents dead-code elimination
        str(d)
    
    @timefunc("cpython.array memoryview")
    def _(int L):
        cdef int i
        cdef double[::1] arr = clone(array('d'), L, False)
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arr[i]
    
        # Prevents dead-code elimination
        str(d)
    
    @timefunc("cpython.array raw C type")
    def _(int L):
        cdef int i
        cdef array arr = clone(array('d'), L, False)
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arr[i]
    
        # Prevents dead-code elimination
        str(d)
    
    @timefunc("numpy.empty_like memoryview")
    def _(int L):
        cdef int i
        cdef double[::1] arr = numpy.empty((L,), dtype='double')
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arr[i]
    
        # Prevents dead-code elimination
        str(d)
    
    @timefunc("malloc")
    def _(int L):
        cdef int i
        cdef double* arrptr = <double*> malloc(sizeof(double) * L)
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arrptr[i]
    
        free(arrptr)
    
        # Prevents dead-code elimination
        str(d)
    
    @timefunc("malloc memoryview")
    def _(int L):
        cdef int i
        cdef double* arrptr = <double*> malloc(sizeof(double) * L)
        cdef double[::1] arr = <double[:L]>arrptr
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arr[i]
    
        free(arrptr)
    
        # Prevents dead-code elimination
        str(d)
    
    @timefunc("cvarray memoryview")
    def _(int L):
        cdef int i
        cdef double[::1] arr = cvarray((L,),sizeof(double),'d')
    
        cdef double d
        for i in range(loops):
            for i in range(L):
                d = arr[i]
    
        # Prevents dead-code elimination
        str(d)
    

    산출:

    INITIALISATIONS
    Running cpython.array buffer
    0.100040 0.097140 0.133110 0.121820 0.131630 0.108420 0.112160 μs
    Running cpython.array memoryview
    0.339480 0.333240 0.378790 0.445720 0.449800 0.414280 0.414060 μs
    Running cpython.array raw C type
    0.048270 0.049250 0.069770 0.074140 0.076300 0.060980 0.060270 μs
    Running numpy.empty_like memoryview
    1.006200 1.012160 1.128540 1.212350 1.250270 1.235710 1.241050 μs
    Running malloc
    0.021850 0.022430 0.037240 0.046260 0.039570 0.043690 0.030720 μs
    Running malloc memoryview
    1.640200 1.648000 1.681310 1.769610 1.755540 1.804950 1.758150 μs
    Running cvarray memoryview
    1.332330 1.353910 1.358160 1.481150 1.517690 1.485600 1.490790 μs
    
    ITERATING
    Running cpython.array buffer
    0.010000 0.027000 0.091000 0.669000 6.314000 64.389000 635.171000 μs
    Running cpython.array memoryview
    0.013000 0.015000 0.058000 0.354000 3.186000 33.062000 338.300000 μs
    Running cpython.array raw C type
    0.014000 0.146000 0.979000 9.501000 94.160000 916.073000 9287.079000 μs
    Running numpy.empty_like memoryview
    0.042000 0.020000 0.057000 0.352000 3.193000 34.474000 333.089000 μs
    Running malloc
    0.002000 0.004000 0.064000 0.367000 3.599000 32.712000 323.858000 μs
    Running malloc memoryview
    0.019000 0.032000 0.070000 0.356000 3.194000 32.100000 327.929000 μs
    Running cvarray memoryview
    0.014000 0.026000 0.063000 0.351000 3.209000 32.013000 327.890000 μs
    

    ( "반복"벤치 마크의 이유는 일부 메소드가이 점에서 놀랍게도 다른 특성을 가지고 있기 때문입니다.)

    초기화 속도의 순서로 :

    malloc : 이것은 가혹한 세상이지만 빠릅니다. 많은 일을 할당하고 반복되는 반복 및 색인 생성 성능을 유지해야하는 경우이 작업을 수행해야합니다. 그러나 일반적으로 당신은 좋은 내기입니다 ...

    cpython.array 원시 C 타입 : 글쎄 젠장, 빠르다. 그리고 그것은 안전합니다. 불행히도 파이썬을 통해 데이터 필드에 액세스합니다. 멋진 속임수를 사용하여이를 피할 수 있습니다.

    arr.data.as_doubles[i]
    

    안전을 제거하는 동안 그것을 표준 속도로 가져옵니다! 이것은 malloc을 훌륭하게 대체하는 것으로, 기본적으로 꽤 참고가되는 버전입니다!

    cpython.array buffer : malloc의 셋업 시간이 3-4 배에 불과합니다. 이것은 멋지게 보입니다. 불행하게도 이것은 상당한 오버 헤드를 가지고있다. (boundscheck와 wraparound 지시어에 비해서는 작지만). 즉, 전체 안전 변형에 대해서만 경쟁하지만, 초기화하는 것이 가장 빠릅니다. 너의 선택.

    cpython.array memoryview : 이제 malloc을 초기화하는 것보다 훨씬 느리다. 그것은 부끄러운 일이지만 빠른 속도로 반복됩니다. boundscheck 또는 둘러 감기가 켜져 있지 않으면 (Cpython.array 버퍼가 더 강력한 거래 일 수 있음) 제안하지 않는 표준 솔루션입니다.

    나머지. 그 중 유일하게 가치가있는 것은 numpy입니다. 많은 재미있는 방법이 객체에 첨부되어 있기 때문입니다. 그것은 그렇다.

  2. ==============================

    2.최대 Veedrac의 답변 : cpython.array의 memoryview 지원을 사용하면 Python 2.7이 메모리 누수로 이어지는 것으로 인식됩니다. 파이썬 2.7.6과 파이썬 2.7.9에서 Cython 버전 0.22를 사용하는 Veedrac의 벤치 마크 스크립트를 실행하면 다음과 같은 결과가 나온다. buffer 또는 memoryview 인터페이스를 사용하여 cpython.array를 초기화 할 때 메모리 누수가 많습니다. Python 3.4에서 스크립트를 실행할 때 메모리 누수가 발생하지 않습니다. 나는 이것에 대한 버그 보고서를 Cython 개발자 메일 링리스트에 제출했다.

    최대 Veedrac의 답변 : cpython.array의 memoryview 지원을 사용하면 Python 2.7이 메모리 누수로 이어지는 것으로 인식됩니다. 파이썬 2.7.6과 파이썬 2.7.9에서 Cython 버전 0.22를 사용하는 Veedrac의 벤치 마크 스크립트를 실행하면 다음과 같은 결과가 나온다. buffer 또는 memoryview 인터페이스를 사용하여 cpython.array를 초기화 할 때 메모리 누수가 많습니다. Python 3.4에서 스크립트를 실행할 때 메모리 누수가 발생하지 않습니다. 나는 이것에 대한 버그 보고서를 Cython 개발자 메일 링리스트에 제출했다.

  3. from https://stackoverflow.com/questions/18462785/what-is-the-recommended-way-of-allocating-memory-for-a-typed-memory-view by cc-by-sa and MIT license