Numpy: Проблема с трекером для поддержки BLIS в NumPy

Созданный на 2 мар. 2016  ·  97Комментарии  ·  Источник: numpy/numpy

Вот общая проблема с трекером для обсуждения поддержки BLIS в NumPy. До сих пор мы обсуждали это в двух номинально не связанных между собой темах:

Так что это новая проблема для консолидации будущих дискуссий, например :-)

Некоторые нерешенные в настоящее время проблемы, которые следует выделить:

CC: @tkelman @ matthew-brett @fgvanzee

15 - Discussion build

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

Создание BLIS в Windows с помощью clang.exe, ориентированного на MSVC ABI, безусловно, возможно. Я потратил несколько часов, и вот изменения. Количество требуемых изменений было на удивление низким по сравнению с OpenBLAS.
Журнал здесь . Все тесты BLIS и BLAS проходят.

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

Я вижу, что BLIS включен в site.cfg .
Можно ли также поддерживать libFLAME ?

Описание на https://www.cs.utexas.edu/%7Eflame/web/libFLAME.html может нуждаться в исправлении, но из этого мне не ясно, что libFLAME на самом деле реализует LAPACK API.

@rgommers libflame действительно предоставляет netlib LAPACK API и реализации для всего, что libflame не предоставляет изначально.

FWIW, BLIS добилась значительных успехов с момента открытия этого выпуска. Например, BLIS теперь реализует конфигурацию времени выполнения, а его конфигурация времени настройки была переопределена с точки зрения инфраструктуры времени выполнения. BLIS теперь предлагает интегрированные тестовые драйверы BLAS в дополнение к более полному набору тестов. Также существует самоинициализация библиотеки и создание монолитных заголовков (один файл blis.h вместо 500+ заголовков разработки), что упрощает управление установленным продуктом. Он также следует более стандартизированному соглашению об именах библиотек для своих статических и общих библиотек, включая soname . Наконец, его система сборки намного умнее по сравнению с проверкой совместимости компилятора и ассемблера. И это как раз то, что я могу придумать в своей голове.

@fgvanzee спасибо. В этом случае я добавляю +1, чтобы добавить поддержку в numpy.distutils .

На данный момент у меня очень мало времени, поэтому я не могу работать над этим, вероятно, по крайней мере до сентября. Если кто-то другой хочет решить эту проблему, это должно быть относительно просто (в том же духе, что и gh-7294). Рад помочь в устранении неполадок / обзоре.

Звучит как большой прогресс. Насколько вы далеки от того, чтобы быть жизнеспособной альтернативой OpenBLAS для numpy / scipy (с точки зрения производительности и стабильности)?

(Обратите внимание, что у нас до сих пор нет scipy-openblas в Windows за conda из-за беспорядка с Фортраном: https://github.com/conda-forge/scipy-feedstock/blob/master/recipe/ meta.yaml # L14)

Если вам нужны BLAS и LAPACK, совместимые с MSVC ABI, по-прежнему не существует простых вариантов с полностью открытым исходным кодом. Хотя в настоящее время существуют clang-cl и flang, проблема не в доступности компилятора, как это было раньше, теперь это гибкость системы сборки и попытки использовать комбинации, которые авторы библиотек никогда не оценивали и не поддерживали раньше. Ссылка https://github.com/flame/blis/issues/57#issuecomment -284614032

@rgommers Я бы сказал, что BLIS в настоящее время является вполне жизнеспособной альтернативой OpenBLAS. Достаточно жизнеспособно, что AMD отказалась от ACML и полностью приняла BLIS как основу своего нового решения с математической библиотекой с открытым исходным кодом. (У нас есть корпоративные спонсоры, и в прошлом их спонсировал Национальный научный фонд.)

С точки зрения производительности точная характеристика будет зависеть от рассматриваемой операции, типа данных с плавающей запятой, оборудования и диапазона размера проблемы, который вас интересует. Однако, в общем, BLIS обычно соответствует или превышает OpenBLAS уровень 3 для всех размеров задач, кроме самых мелких (менее 300 или около того). Он также использует более гибкую стратегию распараллеливания третьего уровня, чем OpenBLAS (из-за монолитной конструкции ядра сборки).

Что касается стабильности, мне хотелось бы думать, что BLIS довольно стабильна. Мы стараемся очень быстро реагировать на сообщения об ошибках, и благодаря всплеску интереса со стороны сообщества за последние пять или около того месяцев мы смогли выявить и исправить множество проблем (в основном, связанных с системой сборки). Это сгладило взаимодействие с пользователем для конечных пользователей, а также для менеджеров пакетов.

Также имейте в виду, что BLIS предоставил (с нулевого дня) расширенный набор функций, подобных BLAS, и сделал это с помощью двух новых API-интерфейсов, отдельных и помимо уровня совместимости BLAS:

  • явно типизированный BLAS-подобный API
  • неявно типизированный объектно-ориентированный API

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

BLIS, рожденный разочарованием по поводу различных недостатков как в существующих реализациях BLAS, так и в самом BLAS API, является моим любимым делом с 2012 года. Он никуда не денется и будет только улучшаться. :)

Ах, спасибо @tkelman. Cygwin :( :(

Было бы интересно услышать некоторый опыт от людей, использующих numpy, скомпилированный для BLIS, в Linux / macOS.

Спасибо за контекст @fgvanzee. Нам будет интересно добавить поддержку libFLAME и попробовать набор тестов SciPy.

@rgommers Re: libflame: Спасибо за проявленный интерес. Просто имейте в виду, что libflame может использовать некоторый TLC; он не в такой хорошей форме, как BLIS. (У нас нет времени / ресурсов, чтобы поддерживать его, как хотелось бы, и почти 100% нашего внимания за последние шесть лет было сосредоточено на том, чтобы довести BLIS до того места, где он мог бы стать жизнеспособной и конкурентоспособной альтернативой OpenBLAS. и другие.)

В какой-то момент, когда BLIS созреет и наши исследовательские возможности будут исчерпаны, мы, вероятно, обратим свое внимание на функциональность уровня libflame / LAPACK (например, разложение на множители Cholesky, LU, QR). Это может принимать форму постепенного добавления этих реализаций в BLIS или может включать совершенно новый проект, который со временем заменит libflame. Если это последнее, то он будет разработан для использования преимуществ API нижнего уровня в BLIS, что позволит избежать некоторых накладных расходов на вызов функций и копирование памяти, которые в настоящее время неизбежны через BLAS. Это лишь одна из многих тем, которые мы с нетерпением ждем.

Я провел тест из этой статьи с NumPy 1.15 и BLIS 0.3.2 на Intel Skylake без многопоточности (у меня была ошибка аппаратной инструкции с HT):

Dotted two 4096x4096 matrices in 4.29 s.
Dotted two vectors of length 524288 in 0.39 ms.
SVD of a 2048x1024 matrix in 13.60 s.
Cholesky decomposition of a 2048x2048 matrix in 2.21 s.
Eigendecomposition of a 2048x2048 matrix in 67.65 s.

Intel MKL 2018.3:

Dotted two 4096x4096 matrices in 2.09 s.
Dotted two vectors of length 524288 in 0.23 ms.
SVD of a 2048x1024 matrix in 1.11 s.
Cholesky decomposition of a 2048x2048 matrix in 0.19 s.
Eigendecomposition of a 2048x2048 matrix in 7.83 s.

Пунктирные две матрицы 4096x4096 точек за 4,29 с.

@homocomputeris К сожалению, я никогда раньше не слышал, чтобы глагол «точка» описывал операцию над двумя матрицами. Это матричное умножение?

@fgvanzee Каков статус поддержки Windows в BLIS в наши дни? Я помню, что когда-то его сборка в Windows в основном не поддерживалась ...

@fgvanzee и да, numpy.dot - это традиционный способ вызова GEMM в Python. (Вроде странное имя, но это потому, что он обрабатывает вектор-вектор, вектор-матрицу, матрицу-матрицу в одном API.)

@njsmith Статус "нативной" поддержки Windows в основном не изменился. К сожалению, нам все еще не хватает опыта и заинтересованности в том, чтобы оказывать такую ​​поддержку. Однако с тех пор, как была выпущена Windows 10, похоже, что существует какая-то среда совместимости «Ubuntu для Windows» или bash. Это, вероятно, гораздо более многообещающий способ добиться поддержки Windows. (Но опять же, никто в нашей группе не разрабатывает и не использует Windows, поэтому мы даже не рассматривали этот вариант.)

Хорошо, последний пост на данный момент ...

@homocomputeris для такого бенчмарка действительно помогает показать некоторые хорошо известные библиотеки, такие как OpenBLAS, потому что иначе мы понятия не имеем, насколько быстро ваше оборудование.

@fgvanzee Говоря о нативной продвинутой поддержке, какие ограничения у вас есть сейчас? Должны ли они быть выровненными, положительными, неотрицательными, точными кратными размеру данных, ...? (Как вы, возможно, помните, массивы numpy допускают абсолютно произвольные шаги, измеряемые в байтах.)

@fgvanzee «bash для Windows» фактически эквивалентен запуску виртуальной машины Linux в Windows - особенно быстрой и бесперебойной виртуальной машины, но это не родная среда. Итак, хорошая новость в том, что вы уже поддерживаете bash для Windows :-), но плохая новость в том, что он не заменяет встроенную поддержку Windows.

@njsmith Мои результаты примерно такие же, как в статье.
Последний MKL, например:

Dotted two 4096x4096 matrices in 2.09 s.
Dotted two vectors of length 524288 in 0.23 ms.
SVD of a 2048x1024 matrix in 1.11 s.
Cholesky decomposition of a 2048x2048 matrix in 0.19 s.
Eigendecomposition of a 2048x2048 matrix in 7.83 s.

Я хочу отметить, что я понятия не имею, как скомпилировать BLIS, чтобы использовать все, что мой процессор может оптимизировать и многопоточность. Пока у MKL есть вещи более-менее из коробки.

@njsmith Спасибо за это обновление. Я согласен, что ничто не может сравниться с поддержкой родных ОС. Я также согласен с тем, что нам нужно увидеть запуск теста с другими библиотеками, чтобы мы могли правильно интерпретировать тайминги @homocomputeris .

Говоря о нативной ступенчатой ​​поддержке, какие ограничения на шаги у вас сейчас есть? Должны ли они быть выровненными, положительными, неотрицательными, точными кратными размеру данных, ...? (Как вы, возможно, помните, массивы numpy допускают абсолютно произвольные шаги, измеряемые в байтах.)

@njsmith Согласен ? Нет. Положительный? Я думаю, что мы сняли это ограничение, но оно не было тщательно проверено. Точное количество, кратное типу данных? Да еще.

Я ввожу в обсуждение

@homocomputeris Не запустить тест с другим значением для size ? Мне интересно, является ли значение, которое использовал автор (4096), степень двойки, особенно плохим вариантом использования для BLIS и в любом случае не особенно реалистичным для большинства приложений. Я предлагаю вместо этого попробовать 4000 (или 3000, или 2000).

@homocomputeris А вы сказали, что результаты BLIS однопоточные, а результаты MKL многопоточные?

FWIW, я некоторое время назад смотрел, как создавать BLIS для Windows. Основная проблема на данный момент - это система сборки. Возможно, удастся заставить mingw make использовать clang для создания двоичного файла, совместимого с MSVC. У меня так и не получилось с тем временем, которое я мог потратить на это, но это кажется возможным.

В собственном исходном коде ситуация не так уж плоха. Недавно они даже перешли на использование макросов для своих сборочных ядер, так что это еще один барьер для поддержки Windows. См. Https://github.com/flame/blis/issues/220#issuecomment -397842370 и https://github.com/flame/blis/pull/224. Похоже, что сами исходные файлы - это еще несколько макросов / ifdef, далеких от создания на MSVC, но это моя точка зрения как стороннего наблюдателя. Я также не знаю, как заставить существующие make-файлы BLIS работать с MSVC.

@insertinterestingnamehere Спасибо за участие, Ян. Вы правы в том, что переработанные ядра сборки на один шаг ближе к MSVC. Однако, как вы отметили, наша система сборки определенно не была разработана с учетом поддержки Windows. Кроме того, поддерживает ли MSVC C99? Если нет, то это еще одно препятствие. (Для BLIS требуется C99.)

Я привел приведенный выше пример только для того, чтобы показать, что BLIS сравним с другими, поэтому я не включил ничего более конкретного.

Но как вы спросите: smiley:

  • Процессор Intel Core i5-6260U с последней версией BIOS и любыми патчами для Spectre / Meltdown
  • Linux 4.17.3-1-ARCH
  • все скомпилировано с помощью gcc 8.1.1 20180531
  • NumPy 1.15.0rc1
  • Я выбрал простое число для размеров матрицы

Intel MKL 2018.3 ограничен двумя потоками (то есть моими физическими ядрами ЦП):

Dotted two 3851x3851 matrices in 1.62 s.
Dotted two vectors of length 492928 in 0.18 ms.
SVD of a 1925x962 matrix in 0.54 s.
Cholesky decomposition of a 1925x1925 matrix in 0.10 s.
Eigendecomposition of a 1925x1925 matrix in 4.38 s.

BLIS 0.3.2 скомпилирован с
CFLAGS+=" -fPIC" ./configure --enable-cblas --enable-threading=openmp --enable-shared x86_64

Dotted two 3851x3851 matrices in 3.82 s.
Dotted two vectors of length 492928 in 0.39 ms.
SVD of a 1925x962 matrix in 12.82 s.
Cholesky decomposition of a 1925x1925 matrix in 2.02 s.
Eigendecomposition of a 1925x1925 matrix in 67.80 s.

Итак, похоже, что BLIS определенно должен поддерживаться NumPy, по крайней мере, в Unix / POSIX / других подобных системах, поскольку я представляю вариант использования Windows как `` не трогайте его, если он работает ''
Единственное, чего я не знаю, так это связи между MKL / BLIS и LAPACK / libFLAME. Intel утверждает, что у них есть много оптимизированных вещей, помимо BLAS, таких как LAPACK, FFT и т. Д.

@fgvanzee Почему степень

Для Numpy и др., Было бы достаточно , чтобы управлять зданием в MinGW / MSYS2 --- это то, что мы делаем в данный момент с openblas на Windows , (хотя это своего рода хак сам по себе). Это ограничит использование «традиционных» API, которые не предполагают передачу ресурсов CRT, но это нормально для BLAS / LAPACK.

@fgvanzee хороший

@fgvanzee @njsmith Вот что нам нужно сделать для поддержки произвольных байтовых шагов:

1) Измените интерфейс. Самое целесообразное, что мне приходит в голову, - это добавить что-то вроде флага stride_units в интерфейс объекта.
2) Выполните рефакторинг всех внутренних компонентов, чтобы использовать только байтовые шаги. В любом случае это может быть неплохой идеей.
3) При упаковке проверьте соответствие типов данных, а если нет, используйте общее ядро ​​упаковки.
a) Общее ядро ​​упаковки также необходимо обновить, чтобы использовать memcpy . Если мы сможем заставить его использовать параметр буквального размера, это не должно быть ужасно отстойным.
4) Когда C не выровнен, также используйте виртуальное микроядро, которое обращается к C с помощью memcpy .

Это только для матриц ввода и вывода. Если alpha и beta могут быть произвольными указателями, тогда возникает больше проблем. Обратите внимание, что на x86 вы можете нормально читать / писать невыровненные данные, но другие архитектуры (особенно ARM) будут проблемой. Компилятор также может создавать дополнительные проблемы с выравниванием при автоматической векторизации .

@homocomputeris :

  1. Я не имел в виду, что степень двойки никогда не возникает «в дикой природе», а просто потому, что она слишком широко представлена ​​в тестах, вероятно, потому, что мы, компьютерно-ориентированные люди, любим считать в степени двойки. :)
  2. Результаты тестов действительно похожи. Мне бы очень понравилось, если ответ на следующий вопрос будет «нет», но возможно ли, что вы случайно запустили оба теста с привязкой к MKL (или BLIS)?
  3. Я полностью согласен с тем, что степени двойки возникают в приложениях, связанных с БПФ. Раньше я занимался обработкой сигналов, поэтому понимаю. :)
  4. Мое беспокойство по поводу того, что BLIS не очень хорошо работает с степенями двойки, на самом деле касается не только BLIS. Однако может случиться так, что явление, которое мы наблюдаем, более выражено с BLIS, и, следовательно, чистый «штраф» для BLIS по сравнению с смехотворно оптимизированным решением, таким как MKL. Проблема заключается в следующем: когда матрицы имеют размерность, равную степени двойки, вполне вероятно, что их «ведущее измерение» также является степенью двойки. (Ведущее измерение соответствует шагу столбца, когда матрица хранится в столбце, или шагу строки, когда сохраняется строка.) Давайте на мгновение предположим, что строка сохраняется. Когда ведущее измерение является степенью двойки, строка кэша, в которой находится элемент (i, j), живет с той же ассоциативностью, что и строка кеша, в которой элементы (i + 1, j), (i + 2, j) , (i + 3, j) etc live - то есть одинаковые элементы последующих строк. Это означает, что когда операция gemm обновляет, скажем, реальный микротайл C двойной точности 6x8, все эти 6 строк сопоставляются с одной и той же ассоциативностью, установленной в кэше L1, и неизбежно некоторые из них будут исключены, прежде чем будут повторно используется. Эти так называемые конфликтные пропуски будут отображаться на наших графиках производительности как случайные скачки производительности. Насколько я знаю, нет простого способа обойти это снижение производительности. Мы уже упаковываем / копируем матрицы A и B, так что это не влияет на них так сильно, но мы не можем упаковать матрицу C в какое-то более подходящее ведущее измерение, не получив огромного количества копий в памяти. (Лекарство будет хуже, чем болезнь.) Теперь, возможно, у MKL есть способ смягчить это, возможно, переключившись на микроядро другой формы, которое минимизирует количество конфликтных ошибок. Или, может быть, нет, но я знаю, что BLIS не пытается ничего сделать, чтобы смягчить это. Надеюсь, это ответ на ваш вопрос.
  5. Вы правы, что MKL - это больше, чем просто функциональность BLAS + LAPACK. Однако имейте в виду, что MKL - это коммерческое решение с закрытым исходным кодом. Хотя он доступен для некоммерческих целей «бесплатно», но нет никакой гарантии, что Intel не сделает MKL недоступным для общественности в будущем или не начнет взимать плату за него снова. Кроме того, это не очень хорошо для нас, компьютерных ученых, которые хотят понять реализацию, или настроить или изменить реализацию, или построить наши исследования на хорошо понятных строительных блоках. Тем не менее, если все, что вы хотите сделать, это решить свою проблему и продолжить свой день, и вы можете выразить это через BLAS, это здорово. :)

@fgvanzee Обновлено. Моя проблема, почему-то он компилировался с MKL, хотя я поставил BLIS в config.
Реализованы ли в LAPACK SVD и EVD?

@homocomputeris Да, LAPACK реализует SVD и EVD. И я ожидаю, что реализация MKL будет чрезвычайно быстрой.

Однако EVD немного неоднозначен: есть обобщенная проблема собственных значений и эрмитова (или симметричная) EVD. Существует также трехдиагональный EVD, но обычно это мало кого интересует, за исключением людей, которые внедряют эрмитовский EVD.

NumPy откажется от поддержки Python 2.7 и 3.4 в конце 2018 года, что позволит использовать более свежие компиляторы MSVC, совместимые с C99, что является одной из причин, по которой мы делаем этот шаг до того, как закончится официальная поддержка 2.7. См. Http://tinyurl.com/yazmczel для получения дополнительной информации о поддержке C99 в последних компиляторах MSVC. Поддержка не полная (есть ли кто-нибудь полный?), Но может быть достаточной. В какой-то момент мы переместим сам NumPy на C99, поскольку некоторые (Intel) запросили это для улучшенной числовой поддержки.

@charris Спасибо за эту информацию, Чарльз. Вероятно , все, что будет реализовано, будет достаточно, но мы не узнаем наверняка, пока не попробуем.

В любом случае, BLIS не нужно побеждать MKL, чтобы получить широкое распространение; его непосредственный конкурент - OpenBLAS.

BLIS не нужно побеждать MKL, чтобы получить широкое распространение; его непосредственный конкурент - OpenBLAS.

Не могу с этим согласиться. MKL находится в особой лиге.

@njsmith Просто любопытно: насколько заинтересовано сообщество numpy в новых API / функциях, которые позволяют выполнять, скажем, gemm в смешанной точности и / или смешанном домене? То есть каждый операнд может иметь разный тип данных, при этом вычисление выполняется с (потенциально) точностью, отличной от точности одного или обоих из A и B?

@fgvanzee Интересно, да, хотя возможное количество комбинаций поражает. Но на данный момент мы выполняем преобразование типов, что требует времени и съедает память. Я также играл с идеей оптимизировать подпрограммы для целочисленных типов, хотя логическое значение может быть единственным типом, который не будет непоправимо страдать от переполнения.

И всегда есть float16. Люди, занимающиеся машинным обучением, могут быть больше заинтересованы в этих функциях. @GaelVaroquaux Мысли?

@charris Да, количество случаев может быть устрашающим. Имеется 128 комбинаций типов для gemm , предполагая четыре традиционных типа данных с плавающей запятой, и это число исключает комбинаторное расширение из параметров транспонирования / сопряжения, а также возможности хранения матрицы (в этом случае оно будет увеличиваться до 55 296).

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

Если вас это интересует, пожалуйста, посмотрите наш проект в ближайшие дни / недели. :)

Вилка BLIS безопасна?

В любом случае, BLIS не нужно побеждать MKL, чтобы получить широкое распространение; его непосредственный конкурент - OpenBLAS.

Конечно. Честно говоря, я не смог заставить NumPy + BLAS работать в моей системе, но, судя по статье, которую я цитировал ранее, они, похоже, имеют очень похожую производительность.
Возможно, libFLAME может ускорить операции LAPACK, если они связаны с NumPy.

Еще один интересный вопрос - протестировать форк AMD BLIS / libFLAME на их последних процессорах Zen, чтобы увидеть, есть ли у них какие-либо улучшения. Еще интереснее становится в свете проблемных процессоров Intel.

В Linux и Windows в настоящее время официальные колеса numpy включают предварительно созданную копию OpenBLAS, поэтому на этих платформах самый быстрый способ получить копию - pip install numpy . (Конечно, это не совсем справедливо, потому что, например, эта версия построена с другим компилятором, чем тот, который использовался для локальной сборки BLIS, но это может дать некоторое представление.)

Вилка BLIS безопасна?

@jakirkham Я говорил об этом с @devinamatthews , и он, кажется, думает, что ответ - да. BLIS порождает потоки по запросу (например, когда вызывается gemm ), а не поддерживает пул потоков, как это делает OpenBLAS.

Однако нам любопытно: от какой модели потоковой передачи ожидает / предпочитает / зависит numpy, если таковая имеется? Вам нужен пул потоков, чтобы уменьшить накладные расходы в случае использования последовательного вызова множества очень мелких проблем? (Потоковая обработка по требованию BLIS не очень подходит для такого использования.)

РЕДАКТИРОВАТЬ: В аналогичном ключе вам нужны pthreads или вы поддаетесь OpenMP?

Спасибо за информацию.

Так он использует оба pthreads и OpenMP? FWIW есть известные проблемы с GCC OpenMP и разветвление, что AFAIK не решены (cc @ogrisel ). Разветвление через модуль multiprocessing в Python - одна из наиболее распространенных стратегий параллелизма, возможность использовать потоки (предпочтительно pthreads по указанной выше причине) безопасным для вилки способом очень важна в Python в целом. Хотя в наши дни есть больше возможностей с такими вещами, как loky, например, которые используют fork-exec.

По общему признанию, Натаниэль знал бы об этом гораздо больше, чем я, но поскольку я уже начал писать этот комментарий, IIUC NumPy не использует потоки, кроме как через внешние библиотеки (например, BLAS). Хотя NumPy выпускает GIL достаточно часто, что позволяет многопоточности в Python быть в некоторой степени эффективным. Такие вещи, как Joblib, Dask и т. Д., Используют эту стратегию.

Что касается пулов потоков, интересно, что вы спросите об этом, поскольку на днях я только что профилировал технику машинного обучения, чтобы максимизировать производительность, которая выполняет серию подпрограмм BLAS на Python с NumPy / SciPy. Используя OpenBLAS и разумное количество ядер, эта процедура способна насыщать ядра на всю длину процедуры, даже если в Python не используется явная потоковая передача (включая NumPy и SciPy) просто потому, что OpenBLAS использовал пул потоков для длины рутина. Так что да, пулы потоков невероятно ценны.

С другой стороны, обрабатывает ли BLIS обнаружение динамической архитектуры? Это очень важно для создания артефактов, которые можно создать один раз и развернуть в различных системах.

@jakirkham Спасибо за ваши комментарии. Я полагаю, что наблюдаемая стоимость отсутствия пула потоков будет зависеть от размера передаваемых матричных операндов и выполняемой операции. Я предполагаю, что gemm - это типичная операция, на которую вы нацелены (исправьте, если я ошибаюсь), но какой размер проблемы вы считаете типичным? Я предполагаю, что стоимость модели создания / объединения BLIS проявится, если вы многократно выполняете матричное умножение, где A, B и C были 40x40, но, возможно, не так много для 400x400 или больше. (TBH, вам, вероятно, не следует ожидать большого ускорения от распараллеливания задач 40x40 с самого начала.)

С другой стороны, обрабатывает ли BLIS обнаружение динамической архитектуры? Это очень важно для создания артефактов, которые можно создать один раз и развернуть в различных системах.

Да. Эта функция была реализована не так давно, во второй половине 2017 года. Теперь BLIS может нацеливаться на так называемые «семейства конфигураций», при этом конкретная подконфигурация, которая будет использоваться во время выполнения, выбирается с помощью некоторой эвристики (например, cpuid инструкция). Примеры поддерживаемых семейств: intel64 , amd64 , x86_64 .

@fgvanzee Я на самом деле считаю производительность на небольших матрицах одним из слабых мест OpenBLAS, поэтому немного беспокоит, если BLIS снова станет медленнее ... Люди определенно используют numpy во всех ситуациях, поэтому мы действительно ценим библиотеки, которые «просто работать» без необходимости настройки для конкретных случаев. (Я предполагаю, что это немного отличается от классической настройки плотной линейной алгебры, где двумя наиболее важными случаями являются автор алгоритма, выполняющий тесты производительности, и эксперты, выполняющие недельные задания на суперкомпьютере со специализированными системными администраторами.) Например, люди часто используют numpy с Матрицы 3x3.

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

Хотя такой вид настройки (или даже принцип «пул потоков против пула потоков») может быть чем-то, что сообщество может вмешаться и сделать, если BLIS когда-либо начнет широко распространяться.

На самом деле это напомнило мне: на прошлой неделе я разговаривал с кем-то, кто знал, что в своей библиотеке он хотел вызвать однопоточный gemm, потому что он управлял потоками на более высоком уровне, и он был разочарован тем, что стандартные библиотеки blas были единственным способом контролировать это через глобальные настройки, которые довольно удобно вызывать из случайной библиотеки. Позволяет ли собственный API BLIS пользователю управлять конфигурацией потока для каждого вызова? Я предполагаю, что да, потому что IIRC у вас вообще нет никакой глобальной конфигурации, кроме уровня совместимости BLAS?

Я на самом деле считаю производительность на небольших матрицах одним из слабых мест OpenBLAS, поэтому будет немного тревожно, если BLIS снова станет медленнее ... Люди определенно используют numpy во всех ситуациях, поэтому мы действительно ценим библиотеки, которые "просто работать "без настройки для конкретных случаев.

Я понимаю, что хочу, чтобы это «просто работало», но я стараюсь быть реалистичным и честным с вами. Если у вас проблема 3x3, и производительность действительно важна для вас, вам, вероятно, даже не стоит использовать BLIS. Я не говорю, что BLIS будет работать в 10 раз медленнее, чем наивный тройной цикл для 3x3, просто это не тот размер проблемы, с которым преуспевают реализации BLIS.

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

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

Я согласен, что отрезок идеален. Но определение того, где должно быть это отсечение, нетривиально и, безусловно, зависит от архитектуры (и от операций уровня 3). Так что это очень возможно, но еще не реализовано (или хорошо продумано).

Хотя такой вид настройки (или даже принцип «пул потоков против пула потоков») может быть чем-то, что сообщество может вмешаться и сделать, если BLIS когда-либо начнет широко распространяться.

Да.

На самом деле это напомнило мне: на прошлой неделе я разговаривал с кем-то, кто знал, что в своей библиотеке он хотел вызвать однопоточный gemm, потому что он управлял потоками на более высоком уровне, и он был разочарован тем, что стандартные библиотеки blas были единственным способом контролировать это через глобальные настройки, которые довольно удобно вызывать из случайной библиотеки.

Последовательный gemm из многопоточного приложения - один из наших любимых вариантов использования. (Напоминание: если его единственный вариант использования - последовательный gemm , он может просто настроить BLIS с отключенной многопоточностью. Но давайте предположим, что он хочет построить один раз, а затем решить многопоточность.)

Позволяет ли собственный API BLIS пользователю управлять конфигурацией потока для каждого вызова? Я предполагаю, что да, потому что IIRC у вас вообще нет никакой глобальной конфигурации, кроме уровня совместимости BLAS?

Даже если вы настроили BLIS с включенной многопоточностью, параллелизм по умолчанию отключен (один поток). Так что это второй способ решить свою проблему.

Но предположим, что он хочет изменить степень параллелизма во время выполнения. Параллелизм в BLIS можно настроить во время выполнения. Однако он по-прежнему включает внутреннюю настройку и чтение переменных среды BLIS через setenv() и getenv() . (См. Вики по OMP_NUM_THREADS и т. Д.) Указывать одно число при распараллеливании, и это грубое упрощение информации, которую предпочитает BLIS; В нашем алгоритме gemm пять циклов, четыре из которых могут быть распараллелены (пять в будущем). Мы можем угадать одно число, но обычно оно не идеально, поскольку зависит от топологии оборудования. Так что это часть того, что тормозит прогресс на этом фронте.

@devinamatthews Есть ли шанс добавить TBLIS в том же духе?

Если у вас проблема 3x3, и производительность действительно важна для вас, вам, вероятно, даже не стоит использовать BLIS. Я не говорю, что BLIS будет работать в 10 раз медленнее, чем наивный тройной цикл для 3x3, просто это не тот размер проблемы, с которым преуспевают реализации BLIS.

Я не особо беспокоюсь о достижении оптимальной производительности для задач 3x3 (хотя, очевидно, хорошо, если мы сможем это сделать!). Но чтобы привести крайний пример: если numpy скомпилирован с BLIS в качестве базовой библиотеки линейной алгебры, и какой-то пользователь пишет a @ b в своем коде с помощью numpy, я определенно надеюсь, что он не будет работать в 10 раз медленнее чем наивная реализация. Ожидать, что пользователи перестроят numpy перед умножением матриц 3x3, - это слишком много, чтобы просить :-). Тем более, что та же программа, которая умножает матрицы 3x3 в одном месте, может также умножать матрицы 1000x1000 в другом месте.

Напоминание: если последовательный гемм - его единственный вариант использования, он может просто настроить BLIS с отключенной многопоточностью. Но предположим, что он хочет построить один раз, а потом решит использовать потоки.

Он поставляет библиотеку Python. Он ожидает, что его пользователи возьмут его библиотеку и объединят ее с другими библиотеками и своим собственным кодом, и все они будут работать вместе в одном процессе. Его библиотека использует GEMM, и вполне вероятно, что часть другого кода, который он не контролирует или о котором он даже не знает, также захочет использовать GEMM. Он хочет иметь возможность управлять потоками для своих вызовов GEMM, не затрагивая случайно другие несвязанные вызовы GEMM, которые могут происходить в том же процессе. И в идеале он мог бы сделать это, не отправляя свою собственную копию GEMM, поскольку это очень раздражает, если делать это правильно, а также концептуально оскорбительно, что программа должна включать две копии большой библиотеки просто так что вы можете получить две копии одной целочисленной переменной number_of_threads . В этом больше смысла?

Я определенно надеюсь, что он не будет работать в 10 раз медленнее, чем наивная реализация.

Теперь, когда я думаю об этом, я бы не удивился, если бы он был в несколько раз медленнее, чем наивный для чрезвычайно маленьких (3x3) задач, но точка кроссовера была бы низкой, может быть, всего 16x16. И это проблема, на которую несложно надеть пластырь. (Я бы хотел сделать это для всех операций уровня 3.)

Он поставляет библиотеку Python. Он ожидает, что его пользователи возьмут его библиотеку и объединят ее с другими библиотеками и своим собственным кодом, и все они будут работать вместе в одном процессе. Его библиотека использует GEMM, и вполне вероятно, что часть другого кода, который он не контролирует или о котором он даже не знает, также захочет использовать GEMM. Он хочет иметь возможность контролировать потоки для своих вызовов GEMM, случайно не затрагивая другие несвязанные вызовы GEMM, которые могут происходить в том же процессе. И в идеале он мог бы сделать это, не отправляя свою собственную копию GEMM, поскольку это очень раздражает, если делать это правильно, а также концептуально оскорбительно, что программа должна включать две копии большой библиотеки просто так что вы можете получить две копии одной целочисленной переменной number_of_threads. В этом больше смысла?

Да, это было полезно - спасибо за подробности. Похоже, он был бы идеальным кандидатом на роль нашего пока еще несуществующего API потоковой передачи. (Лично я считаю , что условность переменная окружения пограничное безумия и выражали свои чувства моих сотрудников на более чем один раз. Тем не менее, это довольно удобно красоваться ГПЦ пользователей / benchmarkers, поэтому мы должны прийти с надлежащим API среды выполнения, который не исключает использования переменных среды, чтобы все остались довольны.)

Не могли бы вы передать ему, что это определенно на нашем радаре? Я буду болтаться с Робертом. Он может быть достаточно заинтересован, чтобы одобрить мое время, потраченное на это раньше, чем позже. (API потоковой передачи уже некоторое время находится в его списке желаний.)

Создание BLIS в Windows с помощью clang.exe, ориентированного на MSVC ABI, безусловно, возможно. Я потратил несколько часов, и вот изменения. Количество требуемых изменений было на удивление низким по сравнению с OpenBLAS.
Журнал здесь . Все тесты BLIS и BLAS проходят.

@isuruf Спасибо, что

Что касается запроса на перенос, у меня есть несколько комментариев / запросов (все довольно незначительные), но я начну разговор о самом запросе на перенос.

PS: Рад, что вы смогли выяснить ошибки f2c . Я импортировал достаточно частей libf2c в исходный код тестового драйвера BLAS (скомпилированный как libf2c.a ), чтобы все было связано, хотя это потребовало некоторого взлома в файлах заголовков. (Мне не нравится поддерживать код Fortran - до такой степени, что я предпочитаю смотреть на f2c'ed C, чем на Fortran - на случай, если вы не можете сказать.)

Intel MKL 2018.3:

Dotted two 4096x4096 matrices in 2.09 s.
Dotted two vectors of length 524288 in 0.23 ms.
SVD of a 2048x1024 matrix in 1.11 s.
Cholesky decomposition of a 2048x2048 matrix in 0.19 s.
Eigendecomposition of a 2048x2048 matrix in 7.83 s.

Несколько слов о тесте, использованном выше. Роберт напомнил мне, что, если мы не знаем, как этот тест (или numpy?) Реализует Cholesky, EVD и SVD, результаты этих операций не будут иметь большого значения. Например, если для факторизации Холецкого используется код netlib LAPACK, алгоритмический размер блока будет неправильным (далеко не идеальным). Кроме того, в зависимости от того, как numpy ссылается на MKL, это может еще больше исказить ситуацию, потому что MKL имеет собственную реализацию факторизации Холецкого в дополнение к быстрому gemm , поэтому он может даже не быть яблоком к яблоку по сравнению с MKL.

Я знаю, что вы, возможно, использовали его просто, чтобы получить общее представление о производительности, и это нормально. Просто поймите, что в деталях могут прятаться черти, которые не позволяют использовать его ни для чего, кроме очень приблизительных сравнений.

Обычно numpy откладывает операции, подобные этим, любой доступной библиотеке LAPACK или LAPACK. Сборки MKL почти наверняка используют настроенный форк LAPACK, который Intel поставляет внутри MKL. Сборки BLIS, вероятно, используют эталонный LAPACK или что-то в этом роде.

В некотором смысле это справедливо: если вы пытаетесь выбрать конфигурацию numpy для быстрого выполнения SVD, то важно, чтобы у MKL была настроенная версия, а у BLIS - нет. (Я считаю, что OpenBLAS находится посередине: они поставляют модифицированную версию LAPACK с библиотекой, но она намного ближе к эталонной реализации, чем версия MKL.) Но да, конечно, если вы пытаетесь понять, что такое BLIS и почему результаты выглядят так, важно помнить, что в SVD и т. д. входит гораздо больше, чем просто эффективность GEMM.

В каком-то смысле это справедливо:

Я понимаю и согласен. Это осложняется тем фактом, что некоторые люди используют тесты, подобные предложенному ранее, для измерения абсолютной производительности на одном оборудовании (то есть для оценки того, насколько хороша реализация по сравнению с пиковой производительностью), а некоторые используют ее для сравнения реализаций. Я думаю, что комментарии Роберта о Холецком и др. больше ориентированы на первое, чем на второе.

@njsmith @insertinterestingnamehere @rgommers @charris Благодаря быстрой работе Исуру мы смогли усовершенствовать и объединить его поддержку Windows на основе приложений в ветку BLIS master . Пожалуйста, взгляните и дайте нам знать, решает ли это проблему поддержки numpy в Windows и в какой степени.

@fgvanzee Можно ссылку на пиар?

Можно ссылку на пиар?

Конечно, вот оно.

Эта первоначальная сборка великолепна! По крайней мере, теоретически этого должно быть достаточно для создания numpy с BLIS в Windows. Такие вещи, как создание dll или поддержка MinGW, могут быть добавлены позже.

Одно замечание о зависимостях, используемых в этом случае: pthreads-win32 - это LGPL. IDK, что, если что, нужно с этим делать.

Лицензирование - сложный вопрос. Я знаю несколько крупных компаний, чья правовая политика затрудняет использование лицензий с «авторским левом», таких как LGPL, в то время как «разрешительные» лицензии не являются проблемой. Добавление кода LGPL в базу кода NumPy / SciPy или колеса определенно будет причиной для беспокойства, оправданно или нет.

Мы не добавляем его в базу кода здесь. Доставка компонента LGPL в колесах не проблема. Сейчас мы поставляем dll gfortran под лицензией GPL с исключением времени выполнения. См. Gh-8689

Конечно. К тому же не удивлюсь, если такие компании также предпочтут MKL.

Нам просто нужно убедиться, что компонент LGPL не связан статически, вероятно.

@jakirkham Спасибо за ваши комментарии. Я полагаю, что наблюдаемая стоимость отсутствия пула потоков будет зависеть от размера передаваемых матричных операндов и выполняемой операции. Я предполагаю, что gemm - это типичная операция, на которую вы нацелены (поправьте, если я ошибаюсь), но какой размер проблемы вы считаете типичным? Я предполагаю, что стоимость модели создания / объединения BLIS проявится, если вы многократно выполняете матричное умножение, где A, B и C были 40x40, но, возможно, не так много для 400x400 или больше. (TBH, вам, вероятно, не следует ожидать большого ускорения от распараллеливания задач 40x40 с самого начала.)

В основном типичны GEMM и SYRK. Иногда появляется GEMV, но часто по возможности он превращается в большую операцию GEMM.

Для нас нетипично иметь по крайней мере одно измерение порядка 10 ^ 6 элементов. Другой может быть от 10 ^ 3 до 10 ^ 6. Так что это немного варьируется. Что нам нужно сделать, чтобы использовать пул потоков, или это происходит автоматически? Также как ведет себя пул потоков, если процесс разветвляется?

С другой стороны, обрабатывает ли BLIS обнаружение динамической архитектуры? Это очень важно для создания артефактов, которые можно создать один раз и развернуть в различных системах.

Да. Эта функция была реализована не так давно, во второй половине 2017 года. Теперь BLIS может нацеливаться на так называемые «семейства конфигураций», при этом конкретная подконфигурация, которая будет использоваться во время выполнения, выбирается с помощью некоторой эвристики (например, инструкции cpuid). Примеры поддерживаемых семейств: intel64, amd64, x86_64.

В качестве предисловия скажу, что у нас есть старые компьютеры, использующие Nehalem вплоть до Broadwell. Возможно, в некоторых более новых персональных машинах есть Kaby Lake. Таким образом, важно иметь возможность максимально использовать предоставляемую нам архитектуру и одновременно избежать сбоев, если мы работаем на древней машине с использованием неподдерживаемых встроенных функций. Поддерживают ли субконфигурации Flame этот диапазон, есть ли дополнительные встроенные коды для отправки правильного ядра на разные архитектуры? Насколько это детализировано? Что нам нужно сделать во время сборки, чтобы гарантировать наличие этих ядер?

Здание @jakirkham с использованием конфигурации x86_64 дает вам:

  • Penryn (включая Nehalem и любой другой 64-битный чип SSSE3 Intel)
  • Sandy Bridge (включая Ivy Bridge)
  • Haswell (+ Broadwell, Skylake, Kaby Lake и Coffee Lake, хотя он может быть не совсем оптимальным для последних трех)
  • Xeon Phi (1-е и 2-е поколение, при необходимости мы можем сделать и 3-е поколение)
  • Skylake SP / X / W (вероятно, будет очень близок к оптимальному и на Cannon Lake)
  • Бульдозер / Пиледривер / Каток / Экскаватор
  • Ryzen / EPYC

Обратите внимание, что для некоторых из них требуется достаточно новый компилятор и / или версия binutils, но только на машине сборки.

AFAIK libflame не имеет специализированных ядер для какой-либо архитектуры, это все зависит от BLIS.

@jakirkham нам пришлось бы добавить реализацию пула потоков. На данный момент это базовая реализация fork-join, которая должна быть безопасна для fork.

AFAIK libflame не имеет специализированных ядер для какой-либо архитектуры, это все зависит от BLIS.

Это в основном правильно. Хотя в libflame есть несколько встроенных (в настоящее время только SSE) ядер для применения ротаций Givens, эти ядра не принадлежат libflame и существуют только потому, что BLIS не существовало на момент их написания, и поэтому не было другого места для разместите их. В конце концов, эти ядра будут переписаны, обновлены и перенесены в BLIS.

Поддерживают ли субконфигурации Flame этот диапазон, есть ли дополнительные встроенные коды для отправки правильного ядра на разные архитектуры? Насколько это детализировано? Что нам нужно сделать во время сборки, чтобы гарантировать наличие этих ядер?

@jakirkham Я не говоря "Пламя" в данном случае. У нас есть два продукта: libflame и BLIS. Хотя libflame требует внимания и, вероятно, его переписывания, практически все мое внимание за последние несколько лет было сосредоточено на BLIS.

Если вы текстуально замените «Flame» на «BLIS», ответ будет «да, в основном».

Насколько это гранулировано?

Не уверен, что вы имеете в виду, но поддержка ядра в BLIS не так обширна, как в OpenBLAS. Например, мы часто не оптимизируем сложный домен trsm . Ядра в некоторой комбинации используются субконфигурациями. Подконфигурации выбираются через cpuid . Вы получаете именно те ядра, которые "зарегистрированы" в субконфигурации. Подробную информацию о гайках и болтах см. В вики-странице конфигурации.

Что нам нужно сделать во время сборки, чтобы гарантировать наличие этих ядер?

Если вам нужно обнаружение оборудования во время выполнения, вы нацеливаетесь на семейство конфигурации (например, intel64 , x86_64 ) во время настройки вместо конкретной подконфигурации (или auto , которая выбирает конкретная подконфигурация). Вот и все.

Что нам нужно сделать, чтобы использовать пул потоков, или это происходит автоматически? Также как ведет себя пул потоков, если процесс разветвляется?

Как я уже говорил ранее в этом потоке, BLIS не использует пул потоков. И я полагаюсь на Девина по поводу безопасности вилки (и он, кажется, думает, что вилка не будет проблемой).

FYI, пакеты BLIS conda доступны для linux, osx, windows на conda-forge. (В настоящее время создается ветка разработки, ожидает релиза). Пакеты были собраны с включенными потоками pthreads и конфигурацией x86_64.

Он поставляет библиотеку Python. Он ожидает, что его пользователи возьмут его библиотеку и объединят ее с другими библиотеками и своим собственным кодом, и все они будут работать вместе в одном процессе. Его библиотека использует GEMM, и вполне вероятно, что часть другого кода, который он не контролирует или о котором он даже не знает, также захочет использовать GEMM. Он хочет иметь возможность контролировать потоки для своих вызовов GEMM, случайно не затрагивая другие несвязанные вызовы GEMM, которые могут происходить в том же процессе.

@njsmith Я разговаривал с Робертом, и он

@jakirkham Один из наших контактов в Intel, @jeffhammond , сообщает нам, что реализации OpenMP обычно используют внутреннюю модель пула потоков. Таким образом, он отговаривает нас от реализации избыточных пулов потоков в BLIS.

Теперь, как вы предполагаете, может оказаться, что numpy требует / предпочитает pthreads, и в этом случае, возможно, мы вернемся к фактическому форк / соединению, происходящему под API pthreads в стиле fork / join.

Так он использует оба pthreads и OpenMP?

Кроме того, я понял, что забыл ответить на этот вопрос. Многопоточный параллелизм BLIS настраивается: он может использовать pthreads или OpenMP. (Однако это не следует путать с безусловной зависимостью времени выполнения BLIS от pthreads из-за нашей зависимости от pthread_once() , который используется для инициализации библиотеки.)

К сожалению, если реализации OpenMP (например, GOMP) не станут более устойчивыми к разветвлению, это не совсем безопасный вариант в Python. В частности, не в таком низком стеке, как NumPy.

К сожалению, если реализации OpenMP (например, GOMP) не станут более устойчивыми к разветвлению, это не совсем безопасный вариант в Python. В частности, не в таком низком стеке, как NumPy.

Справедливо. Итак, похоже, что numpy будет полагаться на параметр конфигурации --enable-threading=pthreads для многопоточности через BLIS.

При компиляции с использованием pthreads существует ли какой-либо общедоступный API для получения некоторого программного управления, чтобы избежать проблем с переподпиской при выполнении вложенного параллелизма с процессами / пулами потоков Python, которые вызывают numpy?

В частности, я бы хотел найти следующие общедоступные символы:

https://github.com/tomMoral/loky/pull/135/files#diff -e49a2eb30dd7db1ee9023b8f7306b9deR111

Аналогично тому, что сделано в https://github.com/IntelPython/smp для MKL и OpenMP.

При компиляции с использованием pthreads существует ли какой-либо общедоступный API для получения некоторого программного управления, чтобы избежать проблем с переподпиской при выполнении вложенного параллелизма с процессами / пулами потоков Python, которые вызывают numpy?

@ogrisel Отличный вопрос, Оливье. Есть способ установить параметры потоковой передачи во время выполнения, но в настоящее время это делается с глобальной семантикой. Это неоптимально, потому что (помимо того, что он является глобальным, а не для каждого вызова gemm или чего-то еще), он питается через переменные среды.

В настоящее время я работаю над незначительным изменением базового кода, который позволит кому-либо использовать один из так называемых «экспертных» суб-API-интерфейсов BLIS для передачи дополнительной структуры данных в gemm что позволит вызывающей стороне, чтобы указать для каждого вызова, какой должна быть стратегия распараллеливания. Вышеупомянутая структура данных позволит людям указать единственное количество потоков, а BLIS автоматически сделает все возможное, чтобы выяснить, где получить параллелизм или уровень параллелизма для каждого цикла в алгоритме умножения матриц (как предпочитает BLIS).

В любом случае, я думаю, этот новый подход удовлетворит ваши потребности. Я уже наполовину закончил с изменениями, поэтому думаю, что у меня всего неделя или около того, чтобы добавить новую функцию в github. Пожалуйста, дайте мне знать, подходит ли вам этот план и / или есть ли у вас какие-либо вопросы или пожелания по этой теме.

PS: Я взглянул на предоставленную вами локальную ссылку. Все эти символы настроены для глобальной настройки потоков. Предлагаемое мной решение не исключает возможности настройки глобально, но оно спроектировано таким образом, что это не единственный вариант. Таким образом, тот, кто хочет использовать максимум 8 ядер / потоков, может иметь приложение, порождающее 2 потока, и каждый из них вызывает экземпляр gemm который получает 4-сторонний параллелизм. Или 3 потока приложений, два из которых вызывают gemm с двусторонним параллелизмом, а третий - с четырехсторонним параллелизмом. (Вы уловили идею.)

он питается через переменные среды.

Что это означает? Можно ли использовать ctypes из Python для перенастройки размера пула потоков BLIS по умолчанию / глобального, если он уже был инициализирован в конкретном процессе Python?

он питается через переменные среды.

Что это означает?

@ogrisel Я имею в виду, что у нас есть небольшой API в BLIS для установки или получения количества потоков, аналогичный omp_get_num_threads() и omp_set_num_threads() в OpenMP. Однако эти функции API BLIS реализованы как вызовы getenv() и setenv() . Это просто исторический артефакт того факта, что одним из первоначальных вариантов использования BLIS было задание одной или нескольких переменных среды в оболочке (например, bash ), а затем выполнение приложения, связанного с BLIS, поэтому время, когда было удобно просто использовать подход с использованием переменных среды; он никогда не задумывался как окончательный и идеальный способ определения количества потоков для параллелизма.

Можно ли использовать ctypes из Python для перенастройки размера пула потоков BLIS, если он уже был инициализирован в конкретном процессе Python?

BLIS явно не использует пул потоков. Скорее, мы используем модель создания / соединения для извлечения параллелизма через pthreads. Но да, после инициализации BLIS приложение (или вызывающая библиотека) может изменить количество потоков во время выполнения посредством вызова функции, по крайней мере, в принципе. (Однако я не знаю, будет ли это работать на практике , потому что я не знаю, как Python будет обрабатывать BLIS, пытаясь установить / получить переменные среды.) Но, как я упоминал ранее, после того, как я внесу некоторые изменения, приложение / библиотека сможет указать настраиваемую схему распараллеливания для каждого вызова во время вызова операции уровня 3. Это можно сделать, сначала инициализировав небольшой тип данных struct а затем передав этот struct в расширенную «экспертную» версию BLIS API, например, для gemm .) сделает переменные среды ненужными для тех, кому они не нужны / не нужны. Надеюсь, это ответит на ваш вопрос.

Большое спасибо за разъяснения. API-интерфейс эксперта по вызовам интересен, но потребует numpy для поддержки определенного API, чтобы предоставить его вызывающим объектам уровня Python. Я не уверен, что мы этого хотим. Я думаю, что для numpy пользователей достаточно иметь способ изменить текущее значение глобального (уровня процесса) уровня параллелизма. Я лично не возражаю, если это достигается путем изменения текущего значения переменной env, если это изменение учитывается при последующих вызовах BLAS-3.

@charris float16 действительно может быть интересен для некоторых рабочих нагрузок машинного обучения, по крайней мере, во время прогнозирования, хотя у меня нет личного опыта с этим.

Я думаю, что для numpy пользователей достаточно иметь способ изменить текущее значение глобального (уровня процесса) уровня параллелизма. Я лично не возражаю, если это достигается путем изменения текущего значения переменной env, если это изменение учитывается при последующих вызовах BLAS-3.

Хорошо знать. Если это так, то BLIS намного ближе к тому, чтобы быть готовым к использованию numpy. (Я просто хотел бы сначала внести эти незавершенные изменения, которые также позволяют исправить ранее незамеченное состояние гонки.)

Вероятно, лучше не зависеть от того, что переменные среды повторно проверяются при каждом вызове, потому что getenv не очень быстро, поэтому может иметь смысл удалить его позже. (Кроме того, я не думаю, что это даже гарантированно поточно-ориентированное?) Но вызовы bli_thread_set_num_threads API должны быть нормальными, поскольку даже если BLIS перестает вызывать getenv все время, тогда API вызывает можно настроить, чтобы продолжать работать независимо.

В долгосрочной перспективе, я думаю, имеет смысл начать раскрывать некоторые API-интерфейсы, отличные от BLAS, в numpy. Одна из вещей, которая делает BLIS привлекательным, в первую очередь, заключается в том, что он предоставляет функции, которых нет в других библиотеках BLAS, например, возможность умножать матрицы со штрихами, и идет работа по расширению API BLAS несколькими способами .

Мы не хотели бы жестко кодировать специфические для библиотеки детали в numpy API (например, мы не хотели бы, чтобы np.matmul начинал принимать аргументы, соответствующие параметрам BLIS JC, IC, JR и IR ), но это вполне может иметь смысл предоставить общий аргумент «сколько потоков для этого вызова», который работает только на бэкэндах, которые предоставляют эту функциональность.

Одна вещь, о которой я не упоминал, - это точность индекса. Большинство системных библиотек, похоже, используют 32-битные целые числа, что в наши дни является ограничением для некоторых приложений. В какой-то момент было бы хорошо, если бы все индексы были 64-битными, что, вероятно, потребует предоставления библиотеки. Я не знаю, что мы сейчас делаем в отношении размера индекса. @ matthew-brett Мы все еще компилируем с 32-битными целыми числами?

@charris Целочисленный размер в BLIS настраивается во время настройки: 32 или 64 бита. Более того, вы можете настроить целочисленный размер, используемый в BLAS API, независимо от внутреннего целочисленного размера.

На самом деле это напомнило мне: на прошлой неделе я разговаривал с кем-то, кто знал, что в своей библиотеке он хотел вызвать однопоточный gemm, потому что он управлял потоками на более высоком уровне, и он был разочарован тем, что стандартные библиотеки blas были единственным способом контролировать это через глобальные настройки, которые довольно удобно вызывать из случайной библиотеки.

@njsmith Я исправил состояние гонки, о котором упоминал ранее, а также реализовал поточно-ориентированный API многопоточности для каждого вызова. Пожалуйста, укажите своему другу на fa08e5e (или на любого потомка этого коммита). Документация по многопоточности также была обновлена ​​и знакомит читателя с его выбором с базовыми примерами. На данный момент фиксация находится в ветке dev , но я планирую вскоре объединить ее с master . (Я уже проделал большую часть кода кода.)

РЕДАКТИРОВАТЬ: ссылки обновлены, чтобы отразить незначительное исправление.

Как возможное дополнение к поддерживаемым типам, как насчет long double ? Я отмечаю, что некоторые архитектуры начинают поддерживать четверную точность (все еще в программном обеспечении), и я ожидаю, что в какой-то момент расширенная точность будет заменена четырехъядерной точностью на Intel. Я не думаю, что это срочно актуально, но я думаю, что по прошествии стольких лет дела начинают идти таким образом.

@charris Мы находимся на ранних этапах рассмотрения поддержки для bfloat16 и / или float16 в частности, из-за их приложений машинного обучения / ИИ, но мы также знаем о спросе на double double и четверная точность. Нам нужно будет заложить некоторый фундамент, чтобы это было осуществимо для всей системы, но это определенно находится на нашем радаре в средне- и долгосрочной перспективе.

@charris Согласно https://en.wikipedia.org/wiki/Long_double , long double может означать разные вещи:

  • 80-битный тип, реализованный в x87, с использованием хранилища 12 или 16 байт
  • двойная точность с MSVC
  • двойная двойная точность
  • учетверенная точность
    Поскольку значение неоднозначно и зависит не только от оборудования, но и от используемого компилятора, это полная катастрофа для библиотек, поскольку ABI не определен четко.

С точки зрения производительности я не вижу никаких преимуществ для float80 (т.е. x87 long double), потому что версии SIMD не существует. Если можно написать SIMD-версию double double в BLIS, это должно работать лучше.

Программная реализация float128 как минимум на порядок медленнее, чем аппаратная реализация float64. Было бы благоразумно написать новую реализацию float128, которая пропускает всю обработку FPE и поддается SIMD. Реализация в libquadmath, хотя и верна, не заслуживает внимания высокопроизводительной реализации BLAS, такой как BLIS.

Ага, это проблема. Я не думаю, что повышенная точность стоит затраченных усилий, а необходимость в четырехугольной точности неоднородна, двойная точность подходит для большинства вещей, но когда она вам нужна, она вам нужна. Меня не волнует производительность, нужна не скорость, а точность. Обратите внимание, что мы только что расширили поддержку ARM64 с четырехъядерной точностью long double, программной реализацией, конечно, но я ожидаю, что в какой-то момент последует и оборудование, и было бы неплохо иметь что-то протестированное и готовое к работе.

В предложении BLAS G2 есть некоторые соображения о двойных двойных и «воспроизводимых» вычислениях в BLAS. (Воспроизводимость здесь означает детерминированность во всех реализациях, но IIUC также предполагает использование промежуточных значений более высокой точности.)

Рад видеть, как это движется вперед!

Для записи, я тот, кого @njsmith имел в виду, кто был заинтересован в управлении потоками из программного обеспечения. Мои рабочие нагрузки невероятно параллельны во время прогнозирования, и мои умножения матриц относительно небольшие. Так что я бы предпочел распараллеливать большие единицы работы.

Около года назад я немного поработал над упаковкой Blis для PyPi и добавлением привязок Cython: https://github.com/explosion/cython-blis

Я обнаружил, что Blis довольно легко упаковать как расширение C. Основным камнем преткновения для меня была поддержка Windows. По памяти это были проблемы с C99, но, возможно, я неправильно вспомнил.

Добавленный мной интерфейс Cython может быть интересен. В частности, я использую объединенные типы Cython, так что существует единственная функция nogil которая может быть вызвана либо с представлением памяти, либо с необработанным указателем, как для типов float, так и для типов double. Добавление дополнительных веток для большего количества типов тоже не проблема. Слитые типы в основном являются шаблонами: они позволяют выполнять условное выполнение во время компиляции без дополнительных затрат.

Я был бы очень рад поддерживать автономный пакет Blis, держать колеса собранными, поддерживать приятный интерфейс Cython и т.д. Я думаю, было бы очень хорошо иметь его как отдельный пакет, а не что-то интегрированное в numpy. Тогда мы могли бы раскрыть больше API Blis, не ограничиваясь тем, что поддерживают другие библиотеки BLAS.

@honnibal Мэтью, извини за задержку с ответом на эту тему.

Спасибо за твое сообщение. Мы всегда рады видеть других в восторге от BLIS. Конечно, мы будем рады сообщить, когда это необходимо, если вы решили продолжить интеграцию в экосистему Python (приложение, библиотеку, модуль и т. Д.).

Что касается поддержки Windows, ознакомьтесь с поддержкой clang / appveyor для Windows ABI, которую недавно добавил

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

Начиная с версии BLIS 0.5.2 , у нас есть документ о производительности, который демонстрирует однопоточную и многопоточную производительность BLIS и других реализаций BLAS для репрезентативного набора типов данных и операций уровня 3 на множестве многоядерных архитектур, включая Marvell ThunderX2. , Intel Skylake-X, Intel Haswell и AMD Epyc.

Так что, если сообщество numpy задается вопросом, как BLIS сочетается с другими ведущими решениями BLAS, я предлагаю вам быстро взглянуть!

Очень интересно, спасибо @fgvanzee.

Мне пришлось искать Epyc - похоже, это торговая марка, основанная на архитектуре Zen (возможно, обновленная до Zen + в какой-то момент?). Может, лучше переименовать в Дзен? Для нашей пользовательской базы более интересными брендами являются Ryzen / Threadripper, они могут узнавать Zen, но не Epyc.

Epyc - это название линейки серверов AMD. Это преемник продуктов AMD Opteron прошлого.

К сожалению, у BLIS нет уникального способа обозначить свои архитектурные цели, потому что код зависит от вектора ISA (например, AVX2), микроархитектуры ядра процессора (например, Ice Lake) и интеграции SOC / платформы (например, процессора Intel Xeon Platinum ). BLIS в некоторых случаях использует кодовые имена микроархитектуры (например, Dunnington), но это не лучше для всех.

@fgvanzee Вы можете подумать о добавлении псевдонимов, которые соответствуют именам march / mtune / mcpu GCC ...

@rgommers Подконфигурация в BLIS, которая охватывает Ryzen и Epyc, на самом деле уже названа zen , поскольку она захватывает оба продукта.

Что касается того, являются ли Ryzen / Threadripper или Epyc более интересными брендами (даже для большого количества пользователей), я скажу следующее: если бы я мог протестировать только одну систему AMD Zen, это был бы Epyc высшего класса, потому что: (а) это использует аналогичную микроархитектуру, что и у Ryzen; (б) он дает мне максимум 64 физических ядра (и, в качестве бонуса, эти ядра расположены в несколько новой, похожей на NUMA конфигурации); который (c) придает максимальное значение BLIS и другим реализациям. И это в основном то, что мы здесь сделали.

К счастью, нет правила, согласно которому я могу протестировать только одну систему Zen. :) Однако есть и другие препятствия, особенно в том, что касается получения доступа в первую очередь. На данный момент у меня нет доступа к системам Ryzen / Threadripper. Если / когда я получу доступ, я буду счастлив повторить эксперименты и опубликовать результаты соответственно.

Джефф указывает на некоторые подводные камни, с которыми мы сталкиваемся. Обычно мы называем наши подконфигурации и наборы ядер в терминах микроархитектуры, но есть еще один нюанс. Например, мы используем нашу подконфигурацию haswell в Haswell, Broadwell, Skylake, Kaby Lake и Coffee Lake. Это потому, что все они в основном используют один и тот же векторный ISA, что почти все заботит код ядра BLIS. Но это деталь реализации, о которой почти никто не должен беспокоиться. Если вы используете ./configure auto , вы почти всегда получите лучшую подконфигурацию и набор ядра для вашей системы, независимо от того, называются ли они zen или haswell или что-то еще. На данный момент вам все еще нужно использовать более практический подход, когда дело доходит до оптимального выбора схемы потоковой передачи, и именно здесь вступает в силу интеграция SoC / платформы, о которой упоминает Джефф.

@jeffhammond Спасибо за ваше предложение. Я рассматривал возможность добавления этих псевдонимов в прошлом. Однако я не уверен, что оно того стоит. Это добавит значительного беспорядка в реестр конфигурации, и люди, которые будут смотреть на него в первую очередь, вероятно, уже знают о нашей схеме именования для подконфигураций и наборов ядер, и, следовательно, их не смущает отсутствие определенной микроархитектурной ревизии. имена в этом файле (или в каталоге config ). Теперь, если BLIS требует ручного определения подконфигурации, например, с помощью ./configure haswell , то я думаю, что чаша весов определенно склонится в пользу вашего предложения. Но ./configure auto работает довольно хорошо, поэтому в настоящее время я не вижу необходимости. (Если хотите, вы можете открыть вопрос по этой теме, чтобы мы могли начать более широкое обсуждение среди членов сообщества. Я всегда готов передумать, если будет достаточный спрос.)

да, именовать всегда сложно :) спасибо за ответы @fgvanzee и @jeffhammond

# 13132 и # 13158 связаны

Дискуссия немного увлеклась; Какие еще проблемы необходимо решить, чтобы официально поддерживать BLIS в numpy?

Наивно, я попытался запустить numpy-тесты с BLIS из conda-forge (см. Https://github.com/numpy/numpy/issues/14180#issuecomment-525292558), и для меня в Linux все тесты прошли (но, возможно, я что-то упустил).

Также попытался запустить тестовый набор тестов scipy в том же env, и есть несколько сбоев в scipy.linalg (cf https://github.com/scipy/scipy/issues/10744) на случай, если у кого-то есть комментарии к тот.

Обратите внимание, что BLIS от conda-forge использует ReferenceLAPACK (netlib) в качестве реализации LAPACK, которая использует BLIS как реализацию BLAS, а не libflame .

Что касается опции BLIS на conda-forge, я прав, что она однопоточная из коробки (в отличие от опции OpenBLAS)?

Наверное, лучше перенести обсуждения conda-forge в conda-forge 🙂

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