Комментарии 49
Интересно было бы сравнить по скорости с библиотекой MKT от того же Intel,
и например с Eigen еще.
Быстрое гугление говорит, что разница между _mm_load_p и _mm_loadu_p на выравненных данных исчезла начиная с Nehalem (2008).
Я уже достаточно давно разработываю проект по оптимизации различных алгоритмов (в основном по компьютерному зрению) при помощи SIMD инструкций.
Обёртку на python ещё не завезли? :)
Очень информативно, а вы пробовали сравнить результаты для реализации "в лоб" у gcc и clang (-O3 -march=haswell -ffast-math
)?
Я по правде говоря ожидал что оптимизации из первого шага и так должны быть сделаны компилятором (loop unswitch/rotate, const propogation?)
Зачем пытаться их обогнать на CPU? Ведь все равно не выйдет.
Это можно использовать либо явно (tiled версия aka блочное перемножение матриц) либо в рекурсивных схемах (где возникают всякие вкусные субкубические варианты типа алгоритма Штрассена). У Вас как я понимаю по сути tiled версия сильно заточенная под AVX — ширина блоков выбирается под ширину регистров, длина — под размер L1.
Для мелких лучше использовать libxsmm. Он делает JIT компиляцию микрокернела под вашу платформу и бесплатный.
а) Если выставлен MKL_NUM_THREADS — то количество потоков ограничено этой переменной;
б) Если выставлен OMP_NUM_THREADS и не выставлена а) то ограничивается этой переменной;
По умолчанию считается что количество потоков = количеству ядер. Т.е если ОБЕ переменные не выставлены — будет работать на всем до чего дотянется.
MKL библиотека от производителя процессора, я сильно сомневаюсь чтобы они специально занижали производительность своих продуктов.
А в случае когда два потока работают на одном физическом ядре они разделяют как блоки FPU, так и кеш, что скажется на производительности скорее негативно, чем позитивно.
50% загрузка видимо видна в диспетчере процессов Windows, там да. Логическое ядро отображается как нормальное, в связи с чем получается такое недопонимание.
Но все-таки умножение матриц это достаточно простая вещь. Вот сделать то же самое для обращения матрицы (LUP разложения) и распараллелить обращение было посложнее.
Однако: максимальный размер микроядра получится 3x4, что дает нам (3 + 4)/(3*4) = ~0.58 загрузок на одну fma. Напомню, что при классической схеме с окном 6x16 получается (6 + 16)/(6*16) = ~0.23 загрузок на одну fma. Т.е. предложенная вами схема почти в 2.5 раза более требовательна к пропускной способности памяти. В принципе мои внутренние тесты это подтверждают.
В начале статьи Вы критикуете малопонятность опенсурсных реализаций, ссылаясь в большей степени на ассемблер. Могу покритиковать Ваш код, так как он использует захардкоженные значения :) Думаю, именованные константы мне бы были более понятны, чем всякие 6, 24 и т. д…
Ещё раньше использовал Athlon II X3 445, но для немного других задач. Там можно было явно записать решение задачи
dE/dt = f(E)*E + a*(laplas)E
в форме
dE/E = [f(E) — a*k2] dt.
То есть это обычный метод с функцией fft. При этом 3-ядерник грузился процентов на 60, но брат думает, что это тратилось второе ядро только на вывод графики.
Почему-то в сети мало информации по умножению матриц, искал варианты реализации на Swift.
AlphaZero, говорят, вычислила новые алгоритмы умножения матриц, еще быстрее чем ранее, но подробностей не гуглится, только новость.
https://www.nature.com/articles/s41586-022-05172-4
Оригинальная статья.
Если коротко: они представили алгоритм умножения матриц как тензор (а):
На b изображен алгоритм Штрассена.
На с изображено разложение тензора для алгоритма Штрассена.
Далее с помощью нейросети стали искать разложения для различных размерностей матриц. Сам поиск представлен в виде игры:
На нулевом шаге есть тензор из примера а.
На каждом шаге нейросеть генерирует возможное разложение для текущего тензора, которое собирается в тензор и вычитается из текущего тензора.
Алгоритм работает пока не получится нулевой тензор, либо пока не будет достигнут лимит ходов.
Из всех предложенных нейросетью разложений собирается финальное разложение оригинального тензора.
Так, например, для умножения 4х4 на 4х4 в алгоритме Штрассена нужно 49 умножений, а они нашли вариант за 47 умножений.
Умножение матриц: эффективная реализация шаг за шагом