Spec-Zone.ru › NumPy 1.20

Оптимизации SIMD

NumPy предоставляет набор макросов, которые определяют Универсальные инструкции, чтобы абстрагировать типичные платформенно-зависимые инструкции, так что код SIMD нужно писать только один раз. Есть три уровня:

  • Код пишется с использованием универсальных макросов инструкций с защитными условиями, которые включат использование макросов только тогда, когда компилятор их распознаёт. В NumPy они используются для построения нескольких петель ufunc. Текущая политика заключается в создании трёх петель: Одна петля — по умолчанию и не использует никаких инструкций. Одна использует минимальный набор инструкций, необходимых для архитектуры. И третья написана с использованием максимального возможного набора инструкций.
  • На стадии компиляции команда distutils используется для определения минимальных и максимальных функций для поддержки, исходя из выбора пользователя и поддержки компилятора. Соответствующие макросы накладываются на платформенно/архитектурно-зависимые инструкции, и компилируются три петли.
  • Во время импорта во время выполнения процессор проверяется на наличие поддерживаемых функций инструкций. Используется механизм для получения указателя на наиболее подходящую функцию, и именно она будет вызвана для функции.

Параметры сборки для компиляции

  • --cpu-baseline: минимальный набор необходимых оптимизаций. Значение по умолчанию — min , которое предоставляет минимальные функции процессора, которые могут безопасно работать на широком спектре платформ в рамках семейства процессоров.
  • --cpu-dispatch: набор дополнительных оптимизаций, которые применяются динамически. Значение по умолчанию — max -xop -fma4 , которое включает все функции процессора, за исключением устаревших функций AMD (в случае x86).

Аргументы команд доступны в build, build_clib, и build_ext. Если build_clib или build_ext не указаны пользователем, то вместо этого будут использованы аргументы build, которые также содержат значения по умолчанию.

Имена оптимизаций могут быть функциями процессора или группами функций, которые объединяют несколько функций или специальными параметрами для выполнения ряда процедур.

В следующих таблицах представлены текущие поддерживаемые оптимизации, отсортированные по убыванию важности.

x86 — Названия функций процессора

Имя

Подразумевает

SSE

SSE2

SSE2

SSE

SSE3

SSE SSE2

SSSE3

SSE SSE2 SSE3

SSE41

SSE SSE2 SSE3 SSSE3

POPCNT

SSE SSE2 SSE3 SSSE3 SSE41

SSE42

SSE SSE2 SSSE3 SSE41 POPCNT

AVX

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42

XOP

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX

FMA4

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX

F16C

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX

FMA3

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C

AVX2

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C

AVX512F

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2

AVX512CD

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F

x86 — Группы имён

Имя

Объединяет

Подразумевает

AVX512_KNL

AVX512ER AVX512PF

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512CD

AVX512_KNM

AVX5124FMAPS AVX5124VNNIW AVX512VPOPCNTDQ

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512CD AVX512_KNL

AVX512_SKX

AVX512VL AVX512BW AVX512DQ

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512CD

AVX512_CLX

AVX512VNNI

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512CD AVX512_SKX

AVX512_CNL

AVX512IFMA AVX512VBMI

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512CD AVX512_SKX

AVX512_ICL

AVX512VBMI2 AVX512BITALG AVX512VPOPCNTDQ

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512CD AVX512_SKX AVX512_CLX AVX512_CNL

IBM/POWER big-endian — Имена функций процессора

Имя

Подразумевает

VSX

VSX2

VSX

VSX3

VSX VSX2

IBM/POWER little-endian — Имена функций процессора

Имя

Подразумевает

VSX

VSX2

VSX2

VSX

VSX3

VSX VSX2

ARMv7/A32 — Имена функций процессора

Имя

Подразумевает

NEON

NEON_FP16

NEON

NEON_VFPV4

NEON NEON_FP16

ASIMD

NEON NEON_FP16 NEON_VFPV4

ASIMDHP

NEON NEON_FP16 NEON_VFPV4 ASIMD

ASIMDDP

NEON NEON_FP16 NEON_VFPV4 ASIMD

ASIMDFHM

NEON NEON_FP16 NEON_VFPV4 ASIMD ASIMDHP

ARMv8/A64 — Имена функций процессора

Имя

Подразумевает

NEON

NEON_FP16 NEON_VFPV4 ASIMD

NEON_FP16

NEON NEON_VFPV4 ASIMD

NEON_VFPV4

NEON NEON_FP16 ASIMD

ASIMD

NEON NEON_FP16 NEON_VFPV4

ASIMDHP

NEON NEON_FP16 NEON_VFPV4 ASIMD

ASIMDDP

NEON NEON_FP16 NEON_VFPV4 ASIMD

ASIMDFHM

NEON NEON_FP16 NEON_VFPV4 ASIMD ASIMDHP

В то время как таблицы выше основаны на компиляторе GCC, следующие таблицы показывают различия в других компиляторах:

x86::Intel Compiler - Названия функций процессора

Имя

Подразумевает

FMA3

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C AVX2

AVX2

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3

AVX512F

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512CD

Примечание

Следующие функции не поддерживаются компилятором x86::Intel: XOP FMA4

x86::Microsoft Visual C/C++ - Названия функций процессора

Имя

Подразумевает

FMA3

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C AVX2

AVX2

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3

AVX512F

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512CD AVX512_SKX

AVX512CD

SSE SSE2 SSE3 SSSE3 SSE41 POPCNT SSE42 AVX F16C FMA3 AVX2 AVX512F AVX512_SKX

Примечание

Следующие функции не поддерживаются x86::Microsoft Visual C/C++: AVX512_KNL AVX512_KNM

Специальные опции

  • NONE: включить отсутствие функций
  • NATIVE: Enables all CPU features that supported by the current

    в данной машине, эта операция основана на флагах компилятора (-march=native, -xHost, /QxHost)

  • MIN: Включает минимальные функции процессора, которые могут безопасно работать на широком спектре платформ:

    Для архитектуры

    Возвращает

    x86

    SSE SSE2

    x86 64-bit mode

    SSE SSE2 SSE3

    IBM/POWER big-endian mode

    NONE

    IBM/POWER little-endian mode

    VSX VSX2

    ARMHF

    NONE

    ARM64 AARCH64

    NEON NEON_FP16 NEON_VFPV4 ASIMD

  • MAX: Включает все поддерживаемые компилятором и платформой функции процессора.
  • Operators-/+: удаляет или добавляет функции, полезно с опциями MAX, MIN и NATIVE.

ЗАМЕЧАНИЯ

  • Функции процессора и другие опции регистронезависимы.
  • Порядок запрошенных оптимизаций не имеет значения.
  • В качестве разделителя можно использовать запятые или пробелы, например --cpu-dispatch= “avx2 avx512f” или --cpu-dispatch= “avx2, avx512f”, но аргументы должны быть заключены в кавычки.
  • Операнд + добавлен только для номинальных целей. Например: --cpu-basline= "min avx2" эквивалентно --cpu-basline="min + avx2". --cpu-basline="min,avx2" эквивалентно --cpu-basline`="min,+avx2".
  • Если функция процессора не поддерживается целевой платформой или компилятором, она будет пропущена, а не вызовет ошибку.
  • Любая указанная функция процессора для --cpu-dispatch будет пропущена, если она является частью базовых функций процессора.
  • Аргумент --cpu-baseline принудительно включает подразумеваемые функции, например --cpu-baseline=”sse42” эквивалентно --cpu-baseline=”sse sse2 sse3 ssse3 sse41 popcnt sse42”
  • Значение --cpu-baseline будет рассматриваться как «родное», если флаг родного компилятора -march=native или -xHost или QxHost включен через переменную окружения CFLAGS.
  • Процесс проверки запрошенных оптимизаций в отношении --cpu-baseline не является строгим. Например, если пользователь запросил AVX2, но компилятор его не поддерживает, мы просто пропускаем его и возвращаем максимальную оптимизацию, которую компилятор может обработать, в зависимости от подразумеваемых функций AVX2, допустим AVX.
  • Пользователь всегда должен проверять окончательный отчет в журнале сборки, чтобы убедиться в включенных функциях.

Особые случаи

Взаимосвязанные функции процессора: Некоторые исключительные условия заставляют нас связывать некоторые функции вместе при работе с определенными компиляторами или архитектурами, что делает невозможным их сборку по отдельности. Эти условия можно разделить на две части:

  • Архитектурная совместимость: Необходимость выравнивания определенных функций процессора, которые гарантированно поддерживаются последующими поколениями одной и той же архитектуры, например:

    • На ppc64le VSX(ISA 2.06) и VSX2(ISA 2.07) подразумевают друг друга, так как первое поколение, поддерживающее режим little-endian, это Power-8`(ISA 2.07)`
    • На AArch64 NEON FP16 VFPV4 ASIMD подразумевают друг друга, так как они являются частью базового оборудования.
  • Совместимость компиляции: Не все компиляторы C/C++ обеспечивают независимую поддержку всех функций процессора. Например, компилятор Intel не предоставляет отдельные флаги для AVX2 и FMA3, что имеет смысл, поскольку все процессоры Intel, которые поставляются с AVX2, также поддерживают FMA3 и наоборот, но такой подход несовместим с другими процессорами x86 от AMD или VIA. Поэтому, в представлении функций процессора между компиляторами C/C++ наблюдаются различия, как показано в таблицах выше.

Поведение и ошибки

Использование и примеры

Отчет и трассировка

Понимание диспетчеризации процессора, как работает диспетчер NumPy?

Диспетчер NumPy основан на многократной компиляции из разных источников, что означает взятие определенного исходного кода и его компиляцию несколько раз с различными флагами компилятора, а также с различными определениями C, которые влияют на пути кода для включения определенных наборов команд для каждого скомпилированного объекта в зависимости от необходимых оптимизаций, затем объединение полученных объектов вместе.

../../_images/opt-infra.png

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

1- Конфигурация

Конфигурирование необходимой оптимизации пользователем перед началом сборки исходных файлов с помощью двух аргументов командной строки, как объяснено выше:

  • --cpu-baseline: минимальный набор необходимых оптимизаций.
  • --cpu-dispatch: набор дополнительных оптимизаций, подлежащих диспетчеризации.

2- Обнаружение среды

В этой части мы проверяем компилятор и архитектуру платформы и кэшируем некоторые промежуточные результаты для ускорения пересборки.

3- Проверка запрошенных оптимизаций

Проверяя их на компиляторе и оценивая, что компилятор может поддержать в соответствии с запрошенными оптимизациями.

4- Генерация основного заголовка конфигурации

Сгенерированный заголовок _cpu_dispatch.h содержит все определения и заголовки наборов инструкций для необходимых оптимизаций, проверенных на предыдущем шаге.

Он также содержит дополнительные определения C, используемые для определения атрибутов модуля NumPy на уровне Python __cpu_baseline__ и __cpu_dispaٍtch__.

Что содержится в этом заголовке?

Пример заголовка был динамически сгенерирован компилятором gcc на машине x86. Компилятор поддерживает --cpu-baseline="sse sse2 sse3" и --cpu-dispatch="ssse3 sse41", и результат представлен ниже.

// The header should be located at numpy/numpy/core/src/common/_cpu_dispatch.h
/**NOTE
 ** C definitions prefixed with "NPY_HAVE_" represent
 ** the required optimzations.
 **
 ** C definitions prefixed with 'NPY__CPU_TARGET_' are protected and
 ** shouldn't be used by any NumPy C sources.
 */
/******* baseline features *******/
/** SSE **/
#define NPY_HAVE_SSE 1
#include <xmmintrin.h>
/** SSE2 **/
#define NPY_HAVE_SSE2 1
#include <emmintrin.h>
/** SSE3 **/
#define NPY_HAVE_SSE3 1
#include <pmmintrin.h>

/******* dispatch-able features *******/
#ifdef NPY__CPU_TARGET_SSSE3
  /** SSSE3 **/
  #define NPY_HAVE_SSSE3 1
  #include <tmmintrin.h>
#endif
#ifdef NPY__CPU_TARGET_SSE41
  /** SSE41 **/
  #define NPY_HAVE_SSE41 1
  #include <smmintrin.h>
#endif

Базовые функции — это минимальный набор необходимых оптимизаций, настроенных через --cpu-baseline. Они не имеют препроцессорных защит и всегда включены, что означает, что они могут быть использованы в любом источнике.

Это означает, что инфраструктура NumPy передает флаги компилятора для базовых функций всем источникам?

Безусловно, да. Но передаваемые источники обрабатываются по-разному.

Что если пользователь указывает определенные базовые функции во время сборки, но во время выполнения машина не поддерживает даже эти функции? Будет ли скомпилированный код вызываться с помощью одного из этих определений, или, возможно, сам компилятор сгенерирует/векторизует определенный фрагмент кода на основе предоставленных флагов компилятора?

Во время загрузки модуля NumPy существует этап проверки, который обнаруживает это поведение. Он сгенерирует ошибку Python во время выполнения, чтобы проинформировать пользователя. Это делается для предотвращения достижения ЦП ошибки нелегальной инструкции, которая может привести к ошибке сегментации.

Передаваемые функции — это наш переданный набор дополнительных оптимизаций, настроенных через --cpu-dispatch. Они не активируются по умолчанию и всегда защищены другими определениями C, начинающимися с NPY__CPU_TARGET_. Определения C NPY__CPU_TARGET_ активируются только в передаваемых источниках.

5- Передаваемые источники и конфигурационные выражения

Передаваемые источники — это специальные файлы C, которые могут быть скомпилированы несколько раз с различными флагами компилятора и различными определениями C. Это влияет на пути кода для включения определенных наборов инструкций для каждого скомпилированного объекта в соответствии с «конфигурационными выражениями», которые должны быть объявлены между комментариями C(/**/) и начинаться со специальной метки @targets в начале каждого передаваемого источника. В то же время передаваемые источники будут обрабатываться как обычные источники C, если оптимизация была отключена аргументом командной строки --disable-optimization.

Что такое конфигурационные выражения?

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

Пример:

/*@targets avx2 avx512f vsx2 vsx3 asimd asimdhp */
// C code

Ключевые слова в основном представляют дополнительные оптимизации, настроенные через --cpu-dispatch, но также могут представлять другие параметры, такие как:

  • Группы целей: предварительно сконфигурированные конфигурационные выражения, используемые для управления необходимыми оптимизациями извне передаваемого источника.
  • Политики: наборы параметров, используемых для изменения поведения по умолчанию или принуждения компиляторов к выполнению определенных действий.
  • «baseline»: уникальное ключевое слово, представляющее минимальные оптимизации, настроенные через --cpu-baseline

Инфраструктура Numpy обрабатывает передаваемые источники в четыре этапа:

  • (A) Распознавание: Как и шаблоны источников и F2PY, передаваемые источники требуют специального расширения *.dispatch.c для маркировки файлов C передаваемых источников, и для C++ *.dispatch.cpp или *.dispatch.cxx ПРИМЕЧАНИЕ: C++ пока не поддерживается.
  • (B) Разбор и проверка: На этом этапе передаваемые источники, отфильтрованные на предыдущем шаге, анализируются и проверяются конфигурационными выражениями для каждого из них по отдельности, чтобы определить необходимые оптимизации.
  • (C) Оборачивание: Это подход, принятый инфраструктурой NumPy, который доказал свою достаточную гибкость для компиляции одного источника несколько раз с разными определениями C и флагами, влияющими на пути кода. Этот процесс достигается путем создания временного источника C для каждой необходимой оптимизации, связанной с дополнительной оптимизацией, который содержит объявления определений C и включает связанный источник с помощью директивы C #include. Для более подробной информации см. следующий код для AVX512F:

    /*
     * this definition is used by NumPy utilities as suffixes for the
     * exported symbols
     */
    #define NPY__CPU_TARGET_CURRENT AVX512F
    /*
     * The following definitions enable
     * definitions of the dispatch-able features that are defined within the main
     * configuration header. These are definitions for the implied features.
     */
    #define NPY__CPU_TARGET_SSE
    #define NPY__CPU_TARGET_SSE2
    #define NPY__CPU_TARGET_SSE3
    #define NPY__CPU_TARGET_SSSE3
    #define NPY__CPU_TARGET_SSE41
    #define NPY__CPU_TARGET_POPCNT
    #define NPY__CPU_TARGET_SSE42
    #define NPY__CPU_TARGET_AVX
    #define NPY__CPU_TARGET_F16C
    #define NPY__CPU_TARGET_FMA3
    #define NPY__CPU_TARGET_AVX2
    #define NPY__CPU_TARGET_AVX512F
    // our dispatch-able source
    #include "/the/absuolate/path/of/hello.dispatch.c"
    
  • (D) Заголовок конфигурации передаваемого источника: Инфраструктура генерирует заголовок конфигурации для каждого передаваемого источника. Этот заголовок в основном содержит два абстрактных макроса C, используемых для идентификации сгенерированных объектов, чтобы они могли использоваться для диспетчеризации во время выполнения определенных символов из сгенерированных объектов любым источником C. Он также используется для предварительных объявлений.

    Сгенерированный заголовок берет имя передаваемого источника после исключения расширения и заменяет его на «.h». Например, предположим, что у нас есть передаваемый источник под названием hello.dispatch.c, содержащий следующее:

    // hello.dispatch.c
    /*@targets baseline sse42 avx512f */
    #include <stdio.h>
    #include "numpy/utils.h" // NPY_CAT, NPY_TOSTR
    
    #ifndef NPY__CPU_TARGET_CURRENT
      // wrapping the dispatch-able source only happens to the addtional optimizations
      // but if the keyword 'baseline' provided within the configuration statments,
      // the infrastructure will add extra compiling for the dispatch-able source by
      // passing it as-is to the compiler without any changes.
      #define CURRENT_TARGET(X) X
      #define NPY__CPU_TARGET_CURRENT baseline // for printing only
    #else
      // since we reach to this point, that's mean we're dealing with
        // the addtional optimizations, so it could be SSE42 or AVX512F
      #define CURRENT_TARGET(X) NPY_CAT(NPY_CAT(X, _), NPY__CPU_TARGET_CURRENT)
    #endif
    // Macro 'CURRENT_TARGET' adding the current target as suffux to the exported symbols,
    // to avoid linking duplications, NumPy already has a macro called
    // 'NPY_CPU_DISPATCH_CURFX' similar to it, located at
    // numpy/numpy/core/src/common/npy_cpu_dispatch.h
    // NOTE: we tend to not adding suffixes to the baseline exported symbols
    void CURRENT_TARGET(simd_whoami)(const char *extra_info)
    {
        printf("I'm " NPY_TOSTR(NPY__CPU_TARGET_CURRENT) ", %s\n", extra_info);
    }
    

    Теперь предположим, что вы добавили hello.dispatch.c в дерево исходного кода, тогда инфраструктура должна сгенерировать временный заголовок конфигурации под названием hello.dispatch.h, доступный для любого источника в дереве исходного кода, и он должен содержать следующий код:

    #ifndef NPY__CPU_DISPATCH_EXPAND_
      // To expand the macro calls in this header
        #define NPY__CPU_DISPATCH_EXPAND_(X) X
    #endif
    // Undefining the following macros, due to the possibility of including config headers
    // multiple times within the same source and since each config header represents
    // different required optimizations according to the specified configuration
    // statements in the dispatch-able source that derived from it.
    #undef NPY__CPU_DISPATCH_BASELINE_CALL
    #undef NPY__CPU_DISPATCH_CALL
    // nothing strange here, just a normal preprocessor callback
    // enabled only if 'baseline' spesfied withiin the configration statments
    #define NPY__CPU_DISPATCH_BASELINE_CALL(CB, ...) \
      NPY__CPU_DISPATCH_EXPAND_(CB(__VA_ARGS__))
    // 'NPY__CPU_DISPATCH_CALL' is an abstract macro is used for dispatching
    // the required optimizations that specified within the configuration statements.
    //
    // @param CHK, Expected a macro that can be used to detect CPU features
    // in runtime, which takes a CPU feature name without string quotes and
    // returns the testing result in a shape of boolean value.
    // NumPy already has macro called "NPY_CPU_HAVE", which fit this requirment.
    //
    // @param CB, a callback macro that expected to be called multiple times depending
    // on the required optimizations, the callback should receive the following arguments:
    //  1- The pending calls of @param CHK filled up with the required CPU features,
    //     that need to be tested first in runtime before executing call belong to
    //     the compiled object.
    //  2- The required optimization name, same as in 'NPY__CPU_TARGET_CURRENT'
    //  3- Extra arguments in the macro itself
    //
    // By default the callback calls are sorted depending on the highest interest
    // unless the policy "$keep_sort" was in place within the configuration statements
    // see "Dive into the CPU dispatcher" for more clarification.
    #define NPY__CPU_DISPATCH_CALL(CHK, CB, ...) \
      NPY__CPU_DISPATCH_EXPAND_(CB((CHK(AVX512F)), AVX512F, __VA_ARGS__)) \
      NPY__CPU_DISPATCH_EXPAND_(CB((CHK(SSE)&&CHK(SSE2)&&CHK(SSE3)&&CHK(SSSE3)&&CHK(SSE41)), SSE41, __VA_ARGS__))
    

    Пример использования заголовка конфигурации в свете вышеизложенного:

    // NOTE: The following macros are only defined for demonstration purposes only.
    // NumPy already has a collections of macros located at
    // numpy/numpy/core/src/common/npy_cpu_dispatch.h, that covers all dispatching
    // and declarations scenarios.
    
    #include "numpy/npy_cpu_features.h" // NPY_CPU_HAVE
    #include "numpy/utils.h" // NPY_CAT, NPY_EXPAND
    
    // An example for setting a macro that calls all the exported symbols at once
    // after checking if they're supported by the running machine.
    #define DISPATCH_CALL_ALL(FN, ARGS) \
        NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, DISPATCH_CALL_ALL_CB, FN, ARGS) \
        NPY__CPU_DISPATCH_BASELINE_CALL(DISPATCH_CALL_BASELINE_ALL_CB, FN, ARGS)
    // The preprocessor callbacks.
    // The same suffixes as we define it in the dispatch-able source.
    #define DISPATCH_CALL_ALL_CB(CHECK, TARGET_NAME, FN, ARGS) \
      if (CHECK) { NPY_CAT(NPY_CAT(FN, _), TARGET_NAME) ARGS; }
    #define DISPATCH_CALL_BASELINE_ALL_CB(FN, ARGS) \
      FN NPY_EXPAND(ARGS);
    
    // An example for setting a macro that calls the exported symbols of highest
    // interest optimization, after checking if they're supported by the running machine.
    #define DISPATCH_CALL_HIGH(FN, ARGS) \
      if (0) {} \
        NPY__CPU_DISPATCH_CALL(NPY_CPU_HAVE, DISPATCH_CALL_HIGH_CB, FN, ARGS) \
        NPY__CPU_DISPATCH_BASELINE_CALL(DISPATCH_CALL_BASELINE_HIGH_CB, FN, ARGS)
    // The preprocessor callbacks
    // The same suffixes as we define it in the dispatch-able source.
    #define DISPATCH_CALL_HIGH_CB(CHECK, TARGET_NAME, FN, ARGS) \
      else if (CHECK) { NPY_CAT(NPY_CAT(FN, _), TARGET_NAME) ARGS; }
    #define DISPATCH_CALL_BASELINE_HIGH_CB(FN, ARGS) \
      else { FN NPY_EXPAND(ARGS); }
    
    // NumPy has a macro called 'NPY_CPU_DISPATCH_DECLARE' can be used
    // for forward declrations any kind of prototypes based on
    // 'NPY__CPU_DISPATCH_CALL' and 'NPY__CPU_DISPATCH_BASELINE_CALL'.
    // However in this example, we just handle it manually.
    void simd_whoami(const char *extra_info);
    void simd_whoami_AVX512F(const char *extra_info);
    void simd_whoami_SSE41(const char *extra_info);
    
    void trigger_me(void)
    {
        // bring the auto-gernreated config header
        // which contains config macros 'NPY__CPU_DISPATCH_CALL' and
        // 'NPY__CPU_DISPATCH_BASELINE_CALL'.
        // it highely recomaned to include the config header before exectuing
      // the dispatching macros in case if there's another header in the scope.
        #include "hello.dispatch.h"
        DISPATCH_CALL_ALL(simd_whoami, ("all"))
        DISPATCH_CALL_HIGH(simd_whoami, ("the highest interest"))
        // An example of including multiple config headers in the same source
        // #include "hello2.dispatch.h"
        // DISPATCH_CALL_HIGH(another_function, ("the highest interest"))
    }
    

Погружение в диспетчер ЦП

Базовый уровень

Диспетчер

Группы и политики

Примеры

Отчет и трассировка

© 2005–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/simd/simd-optimizations.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API