Numpy: Использовать согласованный распределитель для NumPy?

Созданный на 26 нояб. 2014  ·  68Комментарии  ·  Источник: numpy/numpy

Что касается регрессии f2py в NumPy 1.9 с ошибками в 32-битной Windows, вопрос в том, следует ли NumPy начать использовать распределитель, который дает гарантированное выравнивание.

https://github.com/scipy/scipy/issues/4168

01 - Enhancement

Самый полезный комментарий

Эта функция была бы мне очень полезна. Я использую устройство FPGA (Altera A10GX), где контроллер DMA требует использования 64-байтовых выровненных данных, это ускоряет мой код в 40 раз (!!!). Я подозреваю, что у @nachiket такая же проблема, как и у меня. Я написал что-то похожее на то, что использует @eamartin, но это что-то вроде хака.

Все 68 Комментарий

Вот один пример распределителя, который должен работать на всех платформах. На этом бессовестно основано:

https://sites.google.com/site/ruslancray/lab/bookshelf/interview/ci/low-level/write-an-aligned-malloc-free-function

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

Перетаскивание этого кода в numpy/core/include/numpy/ndarraytypes.h должно гарантировать, что только что выделенные ndarrays правильно выровнены на всех платформах.

Этот независимый от платформы код можно было бы заменить на posix_memalign() в POSIX и _aligned_malloc() в Windows. Однако объединение posix_memalign() с realloc() невозможно, поэтому, вероятно, лучше реализовать его самостоятельно.

#define NPY_MEMALIGN 32   /* 16 for SSE2, 32 for AVX, 64 for Xeon Phi */ 

static NPY_INLINE
void *PyArray_realloc(void *p, size_t n)
{
    void *p1, **p2, *base;
    size_t old_offs, offs = NPY_MEMALIGN - 1 + sizeof(void*);    
    if (NPY_UNLIKELY(p != NULL)) {
        base = *(((void**)p)-1);
        if (NPY_UNLIKELY((p1 = PyMem_Realloc(base,n+offs)) == NULL)) return NULL;
        if (NPY_LIKELY(p1 == base)) return p;
        p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1));
        old_offs = (size_t)((Py_uintptr_t)p - (Py_uintptr_t)base);
        memmove(p2,(char*)p1+old_offs,n);    
    } else {
        if (NPY_UNLIKELY((p1 = PyMem_Malloc(n + offs)) == NULL)) return NULL;
        p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1));   
    }
    *(p2-1) = p1;
    return (void*)p2;
}    

static NPY_INLINE
void *PyArray_malloc(size_t n)
{
    return PyArray_realloc(NULL, n);
}

static NPY_INLINE
void *PyArray_calloc(size_t n, size_t s)
{
    void *p;
    if (NPY_UNLIKELY((p = PyArray_realloc(NULL,n*s)) == NULL)) return NULL;
    memset(p, 0, n*s);
    return p;
}

static NPY_INLINE        
void PyArray_free(void *p)
{
    void *base = *(((void**)p)-1);
    PyMem_Free(base);
} 

У меня уже есть ветка, которая добавляет выровненный распределитель, я откопаю ее.

Используя что-то вроде этого, мы отказываемся от возможности использовать фреймворк pythons tracemalloc и разреженную память (нет align_calloc).
@njsmith. Не могли бы вы снова

Предположительно можно было передать выравнивание в данных контекста PyMemAllocatorEx ? Но NumPy должен поддерживать версии Python от 2.6 и выше, поэтому выполнение этого в Python 3.5 может не решить проблему.

Я думаю, что пообщаться с разработчиками python по этому вопросу до версии 3.5 - хорошая идея,
но я все еще не уверен, что у нас есть веская причина использовать выровненный
распределитель в ближайшем будущем. Не может быть, чтобы struct {
double, double} на самом деле требует выравнивания лучше, чем malloc на win32 или
SPARC, потому что если бы это было правдой, ничего бы не получилось.
26 ноября 2014 г. в 09:10 «Джулиан Тейлор» [email protected] написал:

У меня уже есть ветка, которая добавляет выровненный распределитель, я откопаю ее.

Используя что-то подобное, мы отказываемся от возможности использовать питонов.
Фреймворк tracemalloc и разреженная память (нет align_calloc).
@njsmith https://github.com/njsmith хотели бы вы взаимодействовать с
разработчикам python снова добавить еще один распределитель в свой слот до того, как 3.5
выпущенный? Они уже добавили calloc только для нас, было бы обидно, если бы мы
теперь не мог его использовать.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312#issuecomment -64534021.

Вопрос относительно f2py заключался в том, какое выравнивание потребуется Fortran, а не минимальные требования C. Скорость также является проблемой. И индексация, и SIMD работают лучше, если данные правильно выровнены.

Причиной использования выровненного распределителя действительно может быть скорость и обеспечение SSE / AVX
совместимость устранит числовой джиттер, возникающий из-за разных
пути кода для данных, выровненных по-разному.
.
f2py старше стандарта привязки ISO C в Фортране, и то, как он
работает, по сути, является стандартным де-факто способом взаимодействия с Фортраном.
с C, широко используемый всеми. В свете этого опыта это
ясно, что выравнивания, обеспечиваемого системой malloc, достаточно для
Практически важные для нас компиляторы Fortran.

Обратите внимание, что Intel рекомендует 32-байтовое выравнивание для AVX: https://software.intel.com/en-us/articles/practical-intel-avx-optimization-on-2nd-generation-intel-core-processors

@pitrou И для Xeon Phi рекомендуется 64-байтовое выравнивание. Взгляните на комментарий к определению NPY_MEMALIGN в моем примере кода.

Основная сложность при обеспечении согласованного распределения заключается в том, что через банкомат мы можем
либо подключиться к инфраструктуре tracemalloc, либо выполнить согласованное распределение,
и для исправления этого потребуется некоторая координация с восходящим потоком CPython (см.

4663).

5 декабря 2014 г., 16:44, «Стурла Молден» [email protected] написала:

@pitrou https://github.com/pitrou И для Xeon рекомендуется 64 бит
Phi. Взгляните на комментарий к определению NPY_MEMALIGN в
мой пример кода.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312#issuecomment -65816753.

Итак, проблема с CPython находится на http://bugs.python.org/issue18835.

Учитывая сложности с realloc (), было бы нереалистично ожидать, что CPython решит эту проблему за таймфрейм 3.5. Numpy, возможно, должен вместо этого использовать свою собственную выровненную выделенную оболочку (которая должна иметь возможность полагаться на API PyMem и в любом случае использовать tracemalloc).

Код для такого распределителя включен выше. Я не понимаю аргумента @juliantaylor , но он, вероятно, понимает это лучше меня.

Но я могу понять, что он имел в виду, говоря о Каллоке. Calloc - это не просто malloc и нулевое значение memset. Memset потребует, чтобы ОС загружала страницы до того, как они понадобятся. AFAIK нет PyMem_Calloc.

На самом деле у CPython 3.5 есть PyMem_Calloc и друзья.
Думаю, @juliantaylor рассматривал вариант реализации с использованием функций ОС (posix_memalign и т. Д.). Но это не кажется необходимым.

Кстати, @sturlamolden , ваш сниппет переопределяет PyArray_Malloc и его друзей, но при распределении массивов, похоже, используется PyDataMem_NEW. Я что-то не понимаю?

Другая мысль заключается в том, что выровненное распределение может быть расточительным для небольших массивов. Может быть, должен быть порог, ниже которого используется стандартное распределение?
Кроме того, следует ли настраивать выравнивание?

Распределители называются PyArray_malloc и PyArray_free в NumPy 1.9. В NumPy 1.10 многое изменилось.

Вы уверены? PyArray_NewFromDescr_int () вызывает npy_alloc_cache (), а npy_alloc_cache () вызывает PyDataMem_NEW ().

Numpy имеет несколько интерфейсов распределения, и они не очень очевидны
имена. PyArray_malloc / free используются для "обычных" распределений (например, объект
структуры). Буферы данных (ndarray -> указатели данных, временные буферы внутри
ufuncs и т. д.), однако выделяются через PyDataMem_NEW.

15 января 2015 г., в 19:48, Стурла Молден [email protected]
написал:

Распределители называются PyArray_malloc и PyArray_free в Numpy 1.9.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312#issuecomment -70149126.

Натаниэль Дж. Смит
Постдокторант - информатика - Эдинбургский университет
http://vorpus.org

@njsmith Да, когда-

Я создал PR # 5457 с патчем. Было бы неплохо оставить отзыв о подходе.

Насколько я знаю, в настоящее время нет никакой пользы от использования выровненного
распределитель в numpy?

Пт, 16 января 2015 г., в 19:15, Антуан Питру [email protected]
написал:

Я создал PR № 5457 https://github.com/numpy/numpy/pull/5457 с помощью
патч. Было бы неплохо оставить отзыв о подходе.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312#issuecomment -70305997.

Натаниэль Дж. Смит
Постдокторант - информатика - Эдинбургский университет
http://vorpus.org

С помощью Numba мы определили, что векторные инструкции AVX требуют 32-байтового выравнивания для оптимальной производительности. Если вы компилируете Numpy с включенным AVX (я думаю, требуются определенные параметры компилятора), выравнивание также должно иметь значение.

Из любопытства, у вас есть какие-нибудь измерения в реальном мире? Я спрашиваю б / ц там
на эти вещи влияет так много факторов (разные накладные расходы / скорость
компромиссы при разных размерах массивов, детали распределителей памяти - которые
также действуют по-разному при разных размерах массивов и т. д.), что мне сложно
чтобы угадать, получится ли в итоге сквозное ускорение на 0,5% или на 50%
сквозное ускорение что ли.

Пт, 16 января 2015 г., в 20:16, Антуан Питру [email protected]
написал:

С помощью Numba мы определили, что векторные инструкции AVX требуют 32-байтового
выравнивание для оптимальной производительности. Если вы компилируете Numpy с включенным AVX
(я думаю, требуются определенные параметры компилятора), выравнивание должно сделать
разница тоже.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312#issuecomment -70315434.

Натаниэль Дж. Смит
Постдокторант - информатика - Эдинбургский университет
http://vorpus.org

fwiw на моем i5-4210u Я не вижу существенной разницы между данными, выровненными по 16 и 32 байта, в простом тесте на добавление нагрузки, минимальное количество циклов кажется ниже на 5%, но медиана и 10-й процентиль идентичны 1%

Это с AVX?

@seibert сделал некоторые измерения AVX с помощью Numba (т.е. генерация кода точно в срок с помощью LLVM) с использованием массивов Numpy, я думаю, он попытается запустить их снова, чтобы получить точные числа :-)

Вот тест с последним мастером Numba:

http://nbviewer.ipython.org/gist/seibert/6957baddc067140e55fe

Для массивов float32 (размер = 10000), выполняющих операцию a + b * fabs(a) , мы видим разницу в 40%, работающую на процессоре Intel Core i7-4820K @ 3,70 ГГц (Ivy Bridge).

(Мы немного озадачены тем, что автовекторизатор LLVM 3.5 не генерирует код отслаивания цикла для исправления проблем с выравниванием. Я не удивлюсь, если gcc или clang исправят это.)

Для других архитектур, на которые мы планируем ориентироваться (например, APU AMD, поддерживающих стандарт HSA), требования к выравниванию более строгие, и разработчики OpenCL предполагают, что у нас есть 256-байтовое выравнивание для массивов для лучшей производительности.

Я не знаю, что 40% кажется чрезмерным штрафом, я вижу ноль на i5 haswell (у которого действительно низкая производительность avx)
Может быть, компилятор jit создает две разные версии цикла?
у вас есть профиль на уровне сборки (например, через запись perf)?

также, возможно, турбо-ускорение запускается во втором цикле, отключение его или отслеживание его подходящего pmu может быть интересно

Поскольку мы используем эту систему для тестирования производительности, мы отключили TurboBoost в BIOS.

JIT запускается только один раз (как только Numba компилирует функцию для заданного набора типов ввода, она кэширует ее), и JIT запускается перед сравнительным анализом Cell 6 в связанной записной книжке.

Я раньше не использовал perf record, но я разберусь. Как вы выполнили свои тесты? (На самом деле я ожидал, что Haswell будет лучше, чем Ivy Bridge, если неправильное выравнивание вызывает какое-то неэффективное использование доступной полосы пропускания кэша L2.)

Этот технический документ от Intel дает немного больше информации о проблеме выравнивания внизу страницы 6:

https://software.intel.com/sites/default/files/m/d/4/1/d/8/Practical_Optimization_with_AVX.pdf

Использование инструкций Intel AVX для невыровненных 32-байтовых векторов означает, что каждая вторая загрузка будет происходить через разделение строки кэша, поскольку длина строки кэша составляет 64 байта. Это вдвое увеличивает скорость разделения строк кэша по сравнению с кодом Intel SSE, который использует 16-байтовые векторы. Высокая скорость разделения строк кэша в коде с интенсивным использованием памяти с большой вероятностью приведет к снижению производительности. По этой причине настоятельно рекомендуется выровнять данные по 32 байтам для использования с Intel AVX.

Haswell имеет вдвое большую пропускную способность кэша L2, чем Sandy / Ivy Bridge, поэтому возможно, что влияние несовпадающих массивов не имеет значения для Haswell ...

мой простой тест таков:
https://gist.github.com/juliantaylor/68e578d140f427ed80bb
было бы интересно посмотреть на тот i7

Результаты на Core i5-2500K (Sandy Bridge):
4644 6656 7704 10100

Обратите внимание, мне пришлось добавить более длительную фазу разминки в начале теста:

    for (i=0; i<10; i++)
        add(a, 1);

это среднее / медианное значение / ...? это выглядит довольно значительным, похоже, Intel много над этим поработала для haswell.

Это почти стабильный результат теста после нескольких сотен запусков в цикле.

Я добавил упакованную реализацию SSE2, вот цифры (i5-2500K):
4660 6492 7468 5108 10096
(32B выровненный AVX, 16B (не) выровненный AVX, 8B (не) выровненный AVX, выровненный SSE2, скаляр)

Вот обновленный источник: https://gist.github.com/pitrou/892219a7d4c6d37de201

Почти стабильный результат от Core i5-4200U (ноутбук Haswell CPU):
4120 4152 4148 4260 7308

По крайней мере, здесь смещенный AVX не хуже SSE2 с аналогичным выравниванием.

Вот мой MBP (четырехъядерный i7-3635QM, 2,4 ГГц, Ivy Bridge):
5060 4932 5820 5704 5040

Мне пришлось изменить avxintrin.h на immintrin.h и скомпилировать с Intel icc, потому что gcc 4.8.1 отказался компилировать код (как и лязг).

@sturlamolden , может быть, icc векторизует скалярный цикл (последний результат в числах)?

(обратите внимание, что эталонный тест, вероятно, оказал бы большее давление на подсистему кеширования, если бы добавление включало два отдельных входных массива)

Понятия не имею.

@sturlamolden Этот код был очень полезен для взаимодействия с некоторыми SSE2.

У меня была одна проблема в Windows (все версии ВК, включая 2015) не нравятся эта строчка

memmove((void*)p2,p1+old_offs,n);    

поскольку они не поддерживают арифметику указателей на void* . В качестве краткосрочного исправления я перевел его на char* чтобы посчитать. Вероятно, это неправильно - у вас есть лучшее представление, как заставить его правильно компилироваться в Windows?

Виноват. Арифметика указателя на void * недопустима. C. Преобразование в char * правильно.
Обновлен пример кода.

Как получить массивы numpy с выравниванием по 64 байта? Адреса «ВЫРАВНИВАНИЕ» из https://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.require.html кажутся выровненными по разной длине. Есть ли какой-либо настраиваемый пользователем параметр для длины выравнивания?

Я сделал свой собственный (Python) выровненный распределитель, который работает с (невыровненной) памятью, предоставленной Numpy.

import numpy as np

def empty_aligned(n, align):
    """                                                                                                                                     
    Get n bytes of memory wih alignment align.                                                                                              
    """

    a = np.empty(n + (align - 1), dtype=np.uint8)
    data_align = a.ctypes.data % align
    offset = 0 if data_align == 0 else (align - data_align)
    return a[offset : offset + n]


def test(n):
    for i in xrange(1, 1024):
        b = empty_aligned(n, i)

        try:
            assert b.ctypes.data % i == 0
            assert b.size == n
        except AssertionError:
            print i, b.ctypes.data % i, b.size

Возможно, такой обходной путь Python является жизнеспособным решением?

@eamartin Речь идет о внутреннем коде C NumPy и коде интерфейса для Fortran, сгенерированном f2py (также код C). По очевидным причинам реализация NumPy не может зависеть от NumPy.

Однако вы можете использовать этот трюк для своих проектов Python.

@sturlamolden : Это может помочь @nachiket и другим в подобной ситуации.

@sturlamolden : Я не читал достаточно внимательно, чтобы понять, что

Однако предложение лучших вариантов выравнивания в интерфейсах Python было бы полезно для разработки интерфейсов Python между Numpy и собственными библиотеками, которые имеют требования к выравниванию аргументов.

Я не против предложения лучших вариантов выравнивания в интерфейсах Python 🙂

Предлагаемое мной решение «Выровненный по Python» - это хитрость. Я думаю, что предлагать выравнивание в интерфейсах Python было бы неплохо, но правильный способ сделать это - обработать выравнивание на уровне C.

Эта функция была бы мне очень полезна. Я использую устройство FPGA (Altera A10GX), где контроллер DMA требует использования 64-байтовых выровненных данных, это ускоряет мой код в 40 раз (!!!). Я подозреваю, что у @nachiket такая же проблема, как и у меня. Я написал что-то похожее на то, что использует @eamartin, но это что-то вроде хака.

Я определенно рекомендую 64-байтовое выравнивание:

  1. это размер строки кеша
  2. подходит для любого выравнивания SIMD до AVX512

Вот и мы почти 5 лет спустя.

Есть ли мысли о том, как сделать это (в частности, выравнивание по 64 байта) стандартной функцией? ..

Этот код cython теперь находится в NumPy. Конечно, это не меняет значение по умолчанию.

мои 2cents: выровненный распределитель поможет при взаимодействии с аппаратными устройствами и вызовами уровня ядра. Эти интерфейсы могут выиграть от выравнивания буферов по страницам.

При объединении randomgen мы получили PyArray_realloc_aligned и друзей. Следует ли переместить эти процедуры в numpy/core/include ?

Это, безусловно, было бы полезно, @mattip. Можно ли будет получить доступ к этой функции и из Python?

При объединении randomgen мы получили PyArray_realloc_aligned и друзей. Следует ли переместить эти процедуры в numpy/core/include ?

Содрал мой код, а? 😂

Похоже, что я не участвую в логировании кода, который я написал для NumPy 🧐:

https://github.com/numpy/numpy/blob/v1.17.2/numpy/_build_utils/src/apple_sgemv_fix.c
https://github.com/numpy/numpy/blob/v1.17.2/numpy/random/src/aligned_malloc/aligned_malloc.h

Наверное, заблудился в какой-то момент при переходе от randomgen к numpy. я
Думаю, у меня есть запись, что это твое.

В вс, 17 ноября 2019 г., 10:29 Стурла Молден [email protected] написала:

Похоже, я не участвую в журналировании кода, для которого написал
NumPy 🧐:

https://github.com/numpy/numpy/blob/v1.17.2/numpy/_build_utils/src/apple_sgemv_fix.c

https://github.com/numpy/numpy/blob/v1.17.2/numpy/random/src/aligned_malloc/aligned_malloc.h

-
Вы получили это, потому что оставили комментарий.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312?email_source=notifications&email_token=ABKTSRLWKGXUFE4OK53SJMDQUEMJJA5CNFSM4AYDJQ42YY3PNVWWK3TUL52HS4DFVREXG63LVMV55
или отписаться
https://github.com/notifications/unsubscribe-auth/ABKTSRM7IZIKPGKT4D2W4IDQUEMJJANCNFSM4AYDJQ4Q
.

каков был исходный источник кода?

Ссылка мертва, но она была адаптирована из выровненного malloc, который выглядел так:

https://tianrunhe.wordpress.com/2012/04/23/aligned-malloc-in-c/

Это был пост на github от Sturla. Не было исходного файла с кодом.

В вс, 17 ноября 2019 г., 12:04 Матти Пикус [email protected] написал:

каков был исходный источник кода?

-
Вы получили это, потому что оставили комментарий.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312?email_source=notifications&email_token=ABKTSRKKJC4K6C4LW4GFYULQUEXNDA5CNFSM4AYDJQ42YY3PNVWWK3TUL52HS4DFVREXORJGWWK3TUL52HS4DFVREXORJGWMWNVMW9CNVMW08BWNVB0BN02HWB08BW0B0B0B0C08
или отписаться
https://github.com/notifications/unsubscribe-auth/ABKTSRIVMQFEJ5EP227PXL3QUEXNDANCNFSM4AYDJQ4Q
.

Это для выровненного malloc.

В воскресенье , 17 ноября 2019 г., 12:14 Кевин Шеппард
написал:

Это был пост на github от Sturla. Не было исходного файла с кодом.

В вс, 17 ноября 2019 г., 12:04 Матти Пикус [email protected] написал:

каков был исходный источник кода?

-
Вы получили это, потому что оставили комментарий.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/5312?email_source=notifications&email_token=ABKTSRKKJC4K6C4LW4GFYULQUEXNDA5CNFSM4AYDJQ42YY3PNVWWK3TUL52HS4DFVREXORJGWWK3TUL52HS4DFVREXORJGWMWNVMW9CNVMW08BWNVB0BN02HWB08BW0B0B0B0C08
или отписаться
https://github.com/notifications/unsubscribe-auth/ABKTSRIVMQFEJ5EP227PXL3QUEXNDANCNFSM4AYDJQ4Q
.

Все ли, кто вносит 50 строк кода в Numpy, получают специальный заголовок об авторских правах? Я мог бы просмотреть свои статьи и посмотреть, подходят ли они :-)

Похоже, я больше не участвую в написании кода для NumPy 🧐:

Ты есть и всегда будешь :)

Все ли, кто вносит 50 строк кода в Numpy, получают специальный заголовок об авторских правах? Я мог бы просмотреть свои статьи и посмотреть, подходят ли они :-)

Нет. Мы стараемся избегать кодирования таких вещей в исходном коде, поскольку он всегда будет крайне неполным и сложным в обслуживании. Мы просим людей занести себя в список THANKS.txt ; Я ищу лучшую альтернативу этому, потому что этот файл часто дает конфликты слияния.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги