Prange Cython не улучшает производительность

Я пытаюсь улучшить производительность некоторых метрических вычислений с помощью Cython prange. Вот мои коды:

def shausdorff(float64_t[:,::1] XA not None, float64_t[:,:,::1] XB not None):
    cdef:
        Py_ssize_t i
        Py_ssize_t n  = XB.shape[2]
        float64_t[::1] hdist = np.zeros(n)

    #arrangement to fix contiguity
    XB = np.asanyarray([np.ascontiguousarray(XB[:,:,i]) for i in range(n)])

    for i in range(n):
        hdist[i] = _hausdorff(XA, XB[i])
    return hdist

def phausdorff(float64_t[:,::1] XA not None, float64_t[:,:,::1] XB not None):
    cdef:
        Py_ssize_t i
        Py_ssize_t n  = XB.shape[2]
        float64_t[::1] hdist = np.zeros(n)

    #arrangement to fix contiguity (EDITED)
    cdef float64_t[:,:,::1] XC = np.asanyarray([np.ascontiguousarray(XB[:,:,i]) for i in range(n)])

    with nogil, parallel(num_threads=4):
        for i in prange(n, schedule='static', chunksize=1):
            hdist[i] = _hausdorff(XA, XC[i])
    return hdist

По сути, на каждой итерации метрика Хаусдорфа вычисляется между XA и каждым XB[i]. Вот сигнатура функции _hausdorff:

cdef inline float64_t _hausdorff(float64_t[:,::1] XA, float64_t[:,::1] XB) nogil:
    ...

моя проблема в том, что и последовательный shausdorff, и параллельный phausdorff имеют одинаковые тайминги. Кроме того, кажется, что phausdorff вообще не создает никакой темы.

Итак, мой вопрос в том, что не так с моим кодом, и как я могу это исправить, чтобы заставить работать многопоточность.

Вот мой setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

ext_modules=[
    Extension("custom_metric",
              ["custom_metric.pyx"],
              libraries=["m"],
              extra_compile_args = ["-O3", "-ffast-math", "-march=native", "-fopenmp" ],
              extra_link_args=['-fopenmp']
              ) 
]

setup( 
  name = "custom_metric",
  cmdclass = {"build_ext": build_ext},
  ext_modules = ext_modules
) 

РЕДАКТИРОВАНИЕ 1: Вот ссылка на HTML-код, сгенерированный cython -a: custom_metric. html

EDIT 2: Вот пример вызова соответствующих функций (необходимо скомпилировать файл Cython сначала)

import custom_metric as cm
import numpy as np

XA = np.random.random((9000, 210))
XB = np.random.random((1000, 210, 9))

#timing 'parallel' version
%timeit cm.phausdorff(XA, XB)

#timing sequential version
%timeit cm.shausdorff(XA, XB)

person mavillan    schedule 19.08.2016    source источник
comment
Вы пытались напечатать эквивалент omp_get_thread_num() в теле цикла prange. См. cython.readthedocs.io/en/latest/src/userguide/parallelism. .html   -  person Harald    schedule 19.08.2016
comment
Может быть, XB является объектом Python? Запустите cython -a custom_metric.pyx с аннотацией.   -  person cgohlke    schedule 19.08.2016
comment
Есть ли какие-либо изменения, если phausdorff украшен @cython.boundscheck(False) и @cython.wraparound(False)?   -  person J.J. Hakala    schedule 19.08.2016
comment
@cgohlke Я изменил это, явно определив его как представление памяти, и изменений нет. @Дж.Дж. Хакала макросы #cython: boundscheck=False и #cython: wraparound=False включены в начало файла.   -  person mavillan    schedule 19.08.2016
comment
@ Harald, когда я пытаюсь запустить это, Cython говорит: Converting to Python object not allowed without gil   -  person mavillan    schedule 19.08.2016
comment
@mavillan Вы используете OSX? Поскольку информация, которую я вижу в Интернете, предполагает, что версия clang, которую они предоставляют, не поддерживает OpenMP. См. stackoverflow.com/questions/ 33668323/ возможно, но я сам не использую OSX, поэтому не могу подтвердить...   -  person DavidW    schedule 22.08.2016
comment
@DavidW нет, я использую Ubuntu 16.04 со средой Anaconda.   -  person mavillan    schedule 22.08.2016
comment
@mavillan, можете ли вы привести небольшой пример того, как вызывать shausdorff и phausdorff?   -  person Harald    schedule 24.08.2016
comment
@Harald Я добавил пример использования функций.   -  person mavillan    schedule 27.08.2016
comment
Рассматривали ли вы возможность использования большего chunk_size? В зависимости от алгоритма вы можете иметь очень плохую локализацию данных, используя chunk_size из 1.   -  person smateo    schedule 27.08.2016


Ответы (1)


Я думаю, что распараллеливание работает, но дополнительные накладные расходы на распараллеливание съедают время, которое можно было бы сэкономить. Если я попробую с массивами разного размера, то я начну видеть ускорение в параллельной версии.

XA = np.random.random((900, 2100))
XB = np.random.random((100, 2100, 90))

Здесь параллельная версия занимает у меня ~ 2/3 времени последовательной версии, что, конечно, не 1/4, как вы ожидаете, но, по крайней мере, дает некоторое преимущество.


Одно улучшение, которое я могу предложить, — заменить код, который исправляет смежность:

XB = np.asanyarray([np.ascontiguousarray(XB[:,:,i]) for i in range(n)]) 

с

XB = np.ascontiguousarray(np.transpose(XB,[2,0,1]))

Это довольно значительно ускоряет как параллельные, так и непараллельные функции (в 2 раза с массивами, которые вы изначально дали). Это делает немного более очевидным, что вы замедляетесь из-за накладных расходов в prange - последовательная версия на самом деле быстрее для массивов в вашем примере.

person Community    schedule 27.08.2016
comment
(Опубликовано как вики сообщества, так как это не представляет решения, поэтому я хотел удалить его из состязания за награду) - person DavidW; 27.08.2016