Неожиданная потеря производительности при вызове функции Cython внутри скрипта Python? - PullRequest
3 голосов
/ 18 августа 2011

Итак, у меня есть критический по времени раздел кода внутри скрипта Python, и я решил написать модуль Cython (с одной функцией - все, что мне нужно), чтобы заменить его.К сожалению, скорость выполнения функции, которую я вызываю из модуля Cython (который я вызываю в своем скрипте Python), не так высока, как я проверял, чтобы быть в различных других сценариях.Обратите внимание, что я не могу поделиться кодом самостоятельно из-за договорного права!Посмотрите следующие случаи и возьмите их в качестве начального описания моей проблемы:

(1) Выполните функцию Cython с помощью интерпретатора Python для импорта модуля и запуска функции.Выполняется относительно быстро (~ 0,04 сек на ~ 100 отдельных тестах, по сравнению с оригинальными ~ 0,24 сек).

(2) Вызов функции Cython внутри скрипта Python на «глобальном» уровне (т.е. не внутри какой-либо функции).Та же скорость, что и в случае (1).

(3) Вызов функции Cython внутри скрипта Python, с функцией Cython внутри главной функции моего скрипта Python;протестировано с функцией Cython в глобальном и локальном пространствах имен, все с той же скоростью, что и case (1).

(4) То же, что (3), но внутри простого цикла for внутри указанной функции Python.Та же скорость, что и в случае (1).

(5) проблема! То же, что и (4), но внутри еще одного цикла for: время выполнения функции Cython (независимо от того, вызывается ли оно глобально или локально)в 10 раз больше, чем в других случаях, и здесь мне нужно вызвать функцию.Ничего странного, чтобы сообщить об этом цикле, и я протестировал все компоненты этого цикла (корректируя / удаляя то, что я мог).Я также попытался использовать цикл while для хихиканья, но безрезультатно.

«Одна вещь, которую мне еще предстоит попробовать, - сделать этот самый внутренний цикл функцией и идти оттуда». РЕДАКТИРОВАТЬ: Только что попробовал - не повезло.

Спасибо за любые ваши предложения - я глубоко сожалею, что не смог поделиться своим кодом ... это немного ранит мою душу, но мойклиент просто не может иметь этот код, плавающий вокруг.Дайте мне знать, если есть какая-либо другая информация, которую я могу предоставить!

- Реальная проблема и первоначальное (некрасивое) решение -

Оказывается, что лучший совет в этом сценарии былочевидный (как обычно): проблема заключалась не в цикле for;с чего бы это?После еще нескольких тестов стало очевидно, что что-то в том, как я вызывал свою функцию Cython, было неправильным, потому что я мог вызывать ее в другом месте (используя входную переменную, отличную от той, которая идет в «настоящую» функцию Cython) без производительностипроблема потерь.

Основная проблема: типы данных.Я написал свою функцию Cython, ожидающую список, полный стандартных чисел с плавающей точкой.К сожалению, мой код сделал это:

function_input = list(numpy_array_containing_npfloat64_data) # yuck.
type(function_input[0]) = numpy.float64
output = Cython_Function(function_input)

внутри функции Cython:

def Cython_Function(list function_input):
    cdef many_vars
    """process lots of vars expecting C floats""" # Slowness from converting numpy.float64's --> floats???
    type(output) = list
    return output

Я знаю, что могу больше поиграться с типами вФункция Cython, которую я очень хорошо могу сделать, чтобы предотвратить «перечисление» существующего массива.В любом случае, вот мое текущее решение:

function_input = [float(x) for x in function_input]

Я приветствую любые отзывы и предложения по улучшению.Массиву function_input numpy на самом деле не нужна точность numpy.float64, но он используется несколько раз, прежде чем перейти к моей функции Cython.

Ответы [ 2 ]

2 голосов
/ 18 августа 2011

Может случиться так, что, хотя каждый вызов функции с реализацией Cython по отдельности выполняется быстрее, чем соответствующая ему функция Python, при вызове функции Cython возникают дополнительные издержки, поскольку он должен искать имя в пространстве имен модуля.Вы можете сначала попытаться присвоить функцию локальному вызываемому объекту, например:

from module import function

def main():
    my_func = functon
    for i in sequence:
        my_func()

Если возможно, вы должны попытаться включить циклы в функцию Cython, что уменьшит накладные расходы цикла Python на(очень минимальные) издержки скомпилированного цикла C.Я понимаю, что это может быть невозможно (т.е. нужны ссылки из глобальной / более широкой области), но с вашей стороны стоит провести некоторое исследование.Удачи!

1 голос
/ 20 августа 2011
function_input = list(numpy_array_containing_npfloat64_data)

def Cython_Function(list function_input):
    cdef many_vars

Я думаю, что проблема заключается в использовании массива numpy в качестве списка ... вы не можете использовать np.ndarray в качестве ввода для функции Cython?

def Cython_Function(np.ndarray[dtype=np.float64] input):
   ....
...