2015-06-01 4 views
7

Рассмотрим следующий минимальный пример:Cython boundscheck = True быстрее, чем boundscheck = False

#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True 
cimport cython 
from libc.stdlib cimport malloc 

def main(size_t ni, size_t nt, size_t nx): 
    cdef: 
     size_t i, j, t, x, y 
     double[:, :, ::1] a = <double[:ni, :ni, :nx]>malloc(ni * ni * nx * sizeof(double)) 
     double[:, :, ::1] b = <double[:nt, :ni, :nx]>malloc(nt * ni * nx * sizeof(double)) 
     size_t[:, :, ::1] best = <size_t[:nt, :ni, :nx]>malloc(nt * ni * nx * sizeof(size_t)) 
     size_t mxi 
     double s, mxs 
    for t in range(nt): 
     for j in range(ni): 
      for y in range(nx): # this loops does nothing but is needed for the effect below. 
       mxs = -1e300 
       for i in range(ni): 
        for x in range(nx): 
         with cython.boundscheck(False): # Faster!?!? 
          s = b[t, i, x] + a[i, j, x] 
         if s >= mxs: 
          mxs = s 
          mxi = i 
       best[t + 1, j, y] = mxi 
    return best[0, 0, 0] 

по существу суммированием двух 2D массивов вдоль некоторых конкретных осей и найти индекс Максимизация вдоль другой оси.

При компиляции с помощью gcc -O3 и вызываемых с аргументами (1, 2000, 2000) добавление boundscheck = True приводит к выполнению в два раза быстрее, чем при boundscheck = False.

Любой намек на то, почему это так? (Ну, я, наверное, думаю, что это опять-таки связано с GOV autovectorization ...)

Заранее спасибо.

(кросс отправленный от Cython пользователей)

+1

В моих тестах, версия с 'с cython.boundscheck (True) 'примерно в 3 раза медленнее. Я думаю, что память для 'a',' b', 'best' неинициализирована из-за' malloc'. Я изменил это на эквивалентный вызов calloc. Кроме того, строка 'best [t + 1, j, y]', кажется, индексирует недопустимую память, когда 't == nt - 1'. –

+0

Изменение mallocs на callocs и замену 't + 1' на' t' не меняет (меняет) результаты. У моего 'setup.py' есть' extra_compile_args = ["- O3"] ', и я использую gcc 5.1.0. – antony

+1

ОК, я пробовал с -O3, и с обеих версий получаю ту же скорость. Я использую gcc 4.9.1. Если посмотреть на сгенерированный код C, я думаю, что возможно, что gcc достаточно умен, чтобы знать, что проверки дополнительных границ никогда не будут инициированы (из-за условия цикла for). Я не знаю, почему вы получаете такие разные скорости. –

ответ

-1

Boundscheck является проверка безопасности, что вы обращаетесь к индексам в пределах векторов. Если вы не потрудитесь сделать проверку, могут ли индексы выйти за границы, то это быстрее. Для выполнения проверки требуется время.

То есть, если boundcheck is true, он проверяет, находится ли индекс внутри диапазона вектора перед чтением или записью в память. И если нет, это вызовет ошибку. Если boundcheck is false, он будет читать или записывать указатель, даже если индекс выходит за рамки, выдавать ложные данные, считывая и записывая в память, искажая данные путем записи.

Из документации:

В поиски массива по-прежнему тормозится двумя факторами:

1) Bounds проверка выполняется.

2) Отрицательные показатели проверяются и обрабатываются правильно.

Последствие не связанная проверка существ:

Теперь проверки границы не выполняются (и, как побочный эффект, если вы «» делать «» случиться, чтобы получить доступ вне границ вы будете в лучшем случае сбой вашей программы и в худшем случае испорченные данные).

Если это особенно важно, у вас нет ни одного вектора. Вот предупреждение из документации:

Предупреждение

Скорость поставляется с некоторыми затратами. Особенно опасно устанавливать типизированные объекты (например, f, g и h в нашем примере кода) на None. Установка таких объектов в None полностью легальна, но все, что вы можете с ними сделать , это проверить, являются ли они None. Все другое использование (поиск атрибута или индексация ) может потенциально segfault или испортить данные (а не , что создает исключения, как в Python).

Фактические правила немного сложнее, но главное сообщение clear: Не используйте типизированные объекты, не зная, что они не установлены в None.

http://docs.cython.org/src/userguide/numpy_tutorial.html

+2

Я думаю, что причина, по которой был задан вопрос, заключается в том, что в этом случае 'boundscheck (True)' работает быстрее, что противоречит интуиции. – DavidW

+0

Ох !!!!! Хорошая точка зрения! Это настолько противоречиво, что я даже неправильно понял вопрос! Я думал, что вопрос задал обратное. –