плохая производительность Cython с C ++ STL - PullRequest
0 голосов
/ 03 октября 2018

Я хочу ускорить мою программу с помощью openmp, поэтому мне нужно изменить класс Python на класс C ++.Однако классы C ++ работают очень медленно: код класса Python:

class Obj(object):
    def __init__(self,lind,pxlmean,pxlstd,pxlcnt,bandcnt):
        #(2,n)
        self.lind=lind
        self.pxlmean=pxlmean
        self.pxlstd=pxlstd
        self.pxlcnt=pxlcnt
        self.bandcnt=bandcnt
import numpy as np
def pcreateobj(num):
    lst=[]
    for i in range(num):
        lst.append(Obj({0:range(1000)}, range(1000), range(1000), 1000, np.sqrt(10000)))

Время выполнения:% timeit pcreateobj (10000)

10 циклов, лучшее из 3: 31,6 мс на цикл

Код класса C ++:

from libcpp.map cimport map as cpp_map
from libcpp.vector cimport vector as cpp_vector
from libcpp.list cimport list as cpp_list
from libc.math cimport sqrt
ctypedef cpp_map[int,cpp_list[int]] coordslind
ctypedef cpp_vector[double] objpp

cdef cppclass Obj:
    coordslind lind
    objpp pxlmead
    objpp pxlstd
    int pxlcnt
    int bandcnt
cpdef createobj(num):
    cdef Obj* obj
    cdef cpp_vector[Obj*] pool
    cdef int i
    for i in range(num):
        obj=new Obj()
        obj.lind[0]=range(1000)
        obj.pxlmead=range(1000)
        obj.pxlstd=range(1000)
        obj.pxlcnt=1000
        obj.bandcnt=<int>sqrt(10000)
        pool.push_back(obj)

Продолжительность:% timeit createobj (10000)

1 цикл, лучшее из 3: 3,04 с наloop

Итак, как мне улучшить этот код, чтобы получить скорость, аналогичную Python?Спасибо

1 Ответ

0 голосов
/ 04 октября 2018

Cython не может предоставить конструктор python range для std::vector напрямую.Он должен сначала создать буфер range как объект python, а затем преобразовать / заполнить его в std::vector.(В основном звоните __Pyx_PyObject_Call, а затем __pyx_convert_vector_from_py_double).Следовательно, переписать функцию createobj следующим образом:

cpdef createobjNEW(int num):
    cdef Obj* obj
    cdef cpp_vector[Obj*] pool
    pool.reserve(num)
    cdef int i, j, cnt = 1000

    for i in range(num):
        obj=new Obj()
        obj.pxlmead.reserve(cnt)
        obj.pxlstd.reserve(cnt)
        for j in range(cnt):
            obj.lind[0].push_back(j)
            obj.pxlmead.push_back(j)
            obj.pxlstd.push_back(j)
        obj.pxlcnt=cnt
        obj.bandcnt=<int>sqrt(10000)
        pool.push_back(obj)

Время:

На моей машине с Python 2.7 с вышеупомянутыми изменениями я получаю

In[0]: %timeit pcreateobj(10000) # python version
1 loop, best of 3: 468 ms per loop
In[0]: %timeit createobj(10000) # original cython version
1 loop, best of 3: 3.74 s per loop
In[1]: %timeit createobjNEW(10000) # modified cython function
1 loop, best of 3: 1.07 s per loop

Обратите внимание, что способ создания std::map с pair[int,cpp_list[int]] в cython не такой оптимальный / легкий, как создание списков в python.Вместо этого вы должны использовать вектор:

ctypedef cpp_map[int,cpp_vector[int]] coordslind

С этим изменением я получу

In[2]: %timeit createobjNEW(10000) 
1 loop, best of 3: 257 ms per loop

По крайней мере, по скорости это приведет вас к земле C ++.Дальнейшая оптимизация / настройка производительности для этой функции связана с C ++, и Cython ничего не может сделать.Например, вы можете разместить в стеке свой экземпляр Obj и иметь cpp_vector[Obj] вместо cpp_vectorpool[Obj*].В моей машине это приводит к тому, что время выполнения функции увеличивается почти вдвое (114 ms).

Также обратите внимание, что в Python 3 range s являются генераторами и не создаютфактический список значений, тогда как в C ++ вы создаете векторы вручную.

...