2014-01-24 2 views
3

Рассматривают оптимизированного кода Cython в двух случаях:Cython 0,2: Прейндж замедляя код неожиданно

for j in xrange(8): 
     for x in xrange(1, 600): 
      tmp[j] = 0.0 
      for y in xrange(1, 800): 
       tmp[j] += mag[j, x - 1, y - 1] 
       hgi_out[j, x, y] = tmp[j] + hgi_out[j, x - 1, y]  

и:

for j in prange(8): # < prange used for parallelization with openmp 
     for x in xrange(1, 600): 
      tmp[j] = 0.0 
      for y in xrange(1, 800): 
       tmp[j] += mag[j, x - 1, y - 1] 
       hgi_out[j, x, y] = tmp[j] + hgi_out[j, x - 1, y]  

В обоих случаях код находится в пределах собственной функции с nogil объявленных и числовых массивов с представлениями памяти и оптимизированной компоновкой. Рабочее время для первого случая: 14.97 мсек, а для второго - 26.64 примерно удвоение !!

У меня есть другие функции, где использование prange значительно улучшает производительность на моей многоядерной машине, за исключением вышеуказанных случаев, я не понимаю, что происходит.

Любые идеи о том, почему prange замедляет код?

FWIW, вот полный исходный код:

# cython: boundscheck=False 
# cython: wraparound=False 
# cython: nonecheck=False 
# cython: overflowcheck.fold=True 
# cython: embedsignature=False 
# cython: cdivision=True 
# cython: cdivision_warnings=False 
# cython: always_allow_keywords=False 
# cython: profile=False 
# cython: linetrace=False 
# cython: infer_types=False 
# cython: language_level=2 
# cython: c_string_type=unicode 
# cython: c_string_encoding=utf-8 
# cython: type_version_tag=True 
# cython: unraisable_tracebacks=True 
from __future__ import division 
import numpy as np 
cimport numpy as np 
cimport cython 
from cython.parallel import prange 

DTYPE = np.int 
ctypedef np.int_t DTYPE_t 
UITYPE = np.uint 
ctypedef np.uint_t UITYPE_t 
U8TYPE = np.uint8 
ctypedef np.uint8_t U8TYPE_t 
F32TYPE = np.float32 
ctypedef np.float32_t F32TYPE_t 
F64TYPE = np.float64 
ctypedef np.float64_t F64TYPE_t 
ctypedef Py_ssize_t DSIZE_t 

cdef void native_hog_integral_b(F64TYPE_t [:, :, ::1] mag, 
           F64TYPE_t [:, :, ::1] hgi_out) nogil: 
    cdef DSIZE_t m, n, x, y, j, dims = mag.shape[0] 
    cdef F64TYPE_t [32] tmp 
    cdef F64TYPE_t val = 0 
    m, n = mag.shape[1] + 1, mag.shape[2] + 1 
    for j in prange(dims): 
     for x in xrange(1, m): 
      tmp[j] = 0.0 
      for y in xrange(1, n): 
       tmp[j] += mag[j, x - 1, y - 1] 
       hgi_out[j, x, y] = tmp[j] + hgi_out[j, x - 1, y] 

def hog_integral_b(mag, hgi_out=None, orientations=8): 
    if hgi_out is None: 
     hgi_out = np.zeros((orientations + 1, mag.shape[0] + 1, mag.shape[1] + 1), dtype=F64TYPE) 
    native_hog_integral_b(mag, hgi_out) 
    return hgi_out 

Чтобы проверить код выше, попробуйте:

mg2 = np.random.rand(9, 600, 800).astype(F64TYPE) 
hg2 = np.zeros((9, mg2.shape[1] + 1, mg2.shape[2] + 1), dtype=F64TYPE) 
print timeit(lambda:hog_integral_b(mg2, hgi_out=hg2), number=10) 

UPDATE:

Хорошо, я внимательно посмотрел на мои настройки .py для компилятора:

from distutils.core import setup 
from distutils.extension import Extension 
from Cython.Distutils import build_ext 
import numpy as np 

ext_modules = [Extension("hog_cy", ["hog_cy.pyx"], 
         #extra_compile_args = ["-O3", "-fopenmp", "-fno-strict-aliasing"], 
         extra_compile_args = ["-O3", "-fopenmp"], 
         extra_link_args=["-fopenmp"] 
         )] 

setup (
    name = 'performance test app', 
    cmdclass = {'build_ext': build_ext}, 
    include_dirs = [np.get_include()], 
    ext_modules = ext_modules, 
) 

вариант -fno-strict-aliasing, кажется, создает проблему, после отключения я не получаю ускорения, но не теряю.

+0

Может ли вы опубликовать полный пример? – YXD

+1

Надеюсь, что вопрос станет более четким размещением всего кода. –

+0

Извините, что вы так педантичны, но можете ли вы добавить функцию для вызова 'hog_integral_b' с образцовыми (случайными) данными правой формы и dtype? – YXD

ответ

1

Вы производите битву GIL, потому что prange не находится внутри блока ногиля. Там нет параллелизма в коде, только несколько потоков, конкурирующих за владение GIL:

cimport cython 
from cython.parallel cimport prange, parallel 

cdef void native_hog_integral_b(F64TYPE_t [:, :, ::1] mag, 
           F64TYPE_t [:, :, ::1] hgi_out): 

    cdef DSIZE_t m, n, j, dims = mag.shape[0] 
    cdef F64TYPE_t val = 0 
    m, n = mag.shape[1] + 1, mag.shape[2] + 1 
    with nogil, parallel(): 
     cdef DSIZE_t x, y 
     cdef F64TYPE_t tmp 
     for j in prange(dims): 
      for x in range(1, m): 
       tmp = 0.0 
       for y in range(1, n): 
        tmp += mag[j, x - 1, y - 1] 
        hgi_out[j, x, y] = tmp + hgi_out[j, x - 1, y] 
+0

Собственная функция (native_hog_integral_b) объявлена ​​с ногилем в конце своей подписи, эта же модель декларации отлично работает для других функции (не показаны в коде выше). Я думал, возможно, компилятор не рассматривает часть внутренней переменной как локальную потокобезопасную (ex: tmp), но еще не пробовал ее. –

+1

Объявление nogil функции не требует освобождения GIL и не освобождает GIL, что позволяет использовать функцию внутри блока nogil. Вы можете использовать функции nogil, удерживая GIL. GIL не будет выпущен до тех пор, пока вы не наберете «с nogil:» –

0
cimport cython 
from cython.parallel cimport prange, parallel 

cdef void native_hog_integral_b(F64TYPE_t [:, :, ::1] mag, 
           F64TYPE_t [:, :, ::1] hgi_out): 

    cdef DSIZE_t m, n, j, dims = mag.shape[0] 
    cdef F64TYPE_t val = 0 
    m, n = mag.shape[1] + 1, mag.shape[2] + 1 
    with nogil, parallel(): 
     cdef DSIZE_t x, y 
     cdef F64TYPE_t tmp 
     for j in prange(dims): 
      for x in range(1, m): 
       tmp = 0.0 
       for y in range(1, n): 
        tmp += mag[j, x - 1, y - 1] 
        hgi_out[j, x, y] = tmp + hgi_out[j, x - 1, y] 
Смежные вопросы