Я делаю некоторые тесты с CUDA, C ++, C #, Java и использую MATLAB для проверки и генерации матрицы. Когда я выполняю умножение матриц с помощью MATLAB, 2048x2048
и даже большие матрицы умножаются практически мгновенно.
1024x1024 2048x2048 4096x4096
--------- --------- ---------
CUDA C (ms) 43.11 391.05 3407.99
C++ (ms) 6137.10 64369.29 551390.93
C# (ms) 10509.00 300684.00 2527250.00
Java (ms) 9149.90 92562.28 838357.94
MATLAB (ms) 75.01 423.10 3133.90
Только CUDA конкурентоспособна, но я думал, что по крайней мере C ++ будет несколько ближе, а не в 60 раз медленнее. Я также не знаю, что думать о результатах C #. Алгоритм такой же , как C ++ и Java, но гигантский скачок 2048
от 1024
.
Как MATLAB так быстро выполняет умножение матриц?
Код C ++:
float temp = 0;
timer.start();
for(int j = 0; j < rozmer; j++)
{
for (int k = 0; k < rozmer; k++)
{
temp = 0;
for (int m = 0; m < rozmer; m++)
{
temp = temp + matice1[j][m] * matice2[m][k];
}
matice3[j][k] = temp;
}
}
timer.stop();
Ответы:
Вот мои результаты с использованием MATLAB R2011a + Parallel Computing Toolbox на машине с Tesla C2070:
MATLAB использует высоко оптимизированные библиотеки для умножения матриц, поэтому умножение матриц в MATLAB так быстро.
gpuArray
Версия использует МАГМА .Обновление с использованием R2014a на машине с Tesla K20c, а также новые
timeit
иgputimeit
функции:Обновление с использованием R2018b на машине WIN64 с 16 физическими ядрами и Tesla V100:
(Примечание: в какой-то момент (я забыл, когда именно)
gpuArray
переключился с MAGMA на cuBLAS - MAGMA все еще используется для некоторыхgpuArray
операций)источник
Этот вопрос повторяется и на него следует ответить более четко, чем «MATLAB использует высоко оптимизированные библиотеки» или «MATLAB использует MKL» один раз в переполнении стека.
История:
Матричное умножение (вместе с матрично-векторным, векторно-векторным умножением и многими матричными разложениями) является (являются) наиболее важными проблемами линейной алгебры. Инженеры решали эти проблемы с компьютерами с первых дней.
Я не специалист по истории, но, видимо, тогда все просто переписали его версию на Фортране с помощью простых циклов. Затем пришла некоторая стандартизация с идентификацией «ядер» (базовых процедур), которые необходимы для решения большинства задач линейной алгебры. Эти основные операции были затем стандартизированы в спецификации под названием: «Основные подпрограммы линейной алгебры (BLAS)». Инженеры могли бы тогда назвать эти стандартные, хорошо проверенные подпрограммы BLAS в своем коде, делая их работу намного проще.
BLAS:
BLAS эволюционировал от уровня 1 (первая версия, которая определяла скалярно-векторные и векторно-векторные операции) до уровня 2 (операции вектор-матрица) до уровня 3 (операции матрица-матрица) и предоставлял все больше и больше «ядер», настолько стандартизированных и другие основные операции линейной алгебры. Оригинальные реализации FORTRAN 77 по-прежнему доступны на веб-сайте Netlib .
На пути к лучшей производительности:
Таким образом, за прошедшие годы (особенно между выпусками уровня 1 и уровня 2: в начале 80-х годов) аппаратное обеспечение изменилось с появлением векторных операций и иерархий кэша. Эти изменения позволили существенно повысить производительность подпрограмм BLAS. Различные поставщики затем пришли с их реализацией подпрограмм BLAS, которые были все более и более эффективными.
Я не знаю всех исторических реализаций (тогда я не родился и не был ребенком), но в начале 2000-х вышли две наиболее заметные из них: Intel MKL и GotoBLAS. Ваш Matlab использует Intel MKL, который является очень хорошим, оптимизированным BLAS, и это объясняет великолепную производительность, которую вы видите.
Технические подробности по умножению матриц:
Так почему же Matlab (MKL) так быстр
dgemm
(общее умножение матрицы на матрицу с двойной точностью)? Проще говоря: потому что он использует векторизацию и хорошее кэширование данных. В более сложных терминах: см. Статью Джонатана Мура.По сути, когда вы выполняете умножение в предоставленном вами коде C ++, вы совсем не дружественны к кешу. Поскольку я подозреваю, что вы создали массив указателей на массивы строк, ваш доступ во внутреннем цикле к k-му столбцу "matice2":
matice2[m][k]
очень медленный. Действительно, когда выmatice2[0][k]
получаете доступ , вы должны получить k-й элемент массива 0 вашей матрицы. Затем на следующей итерации вы должны получить доступmatice2[1][k]
, который является k-м элементом другого массива (массив 1). Затем на следующей итерации вы получаете доступ к еще одному массиву и т. Д. Так как вся матрицаmatice2
не может поместиться в старшие кеши (это8*1024*1024
большие байты), программа должна извлечь нужный элемент из основной памяти, потеряв много время.Если вы просто транспонируете матрицу так, чтобы доступ осуществлялся по смежным адресам памяти, ваш код уже работал бы намного быстрее, потому что теперь компилятор может загружать целые строки в кэш одновременно. Просто попробуйте эту модифицированную версию:
Таким образом, вы можете видеть, как простота размещения кэша значительно увеличила производительность вашего кода. Теперь реальные
dgemm
реализации используют это на очень обширном уровне: они выполняют умножение на блоки матрицы, определяемые размером TLB (буфер с переводом в сторону, короче говоря: что может эффективно кэшироваться), чтобы они передавались в процессор именно тот объем данных, который он может обработать. Другой аспект - векторизация, они используют векторизованные инструкции процессора для оптимальной пропускной способности команд, чего вы не можете сделать из своего кроссплатформенного кода C ++.Наконец, люди, утверждающие, что это из-за алгоритма Штрассена или Копперсмита-Винограда, ошибочны, оба эти алгоритма не реализуемы на практике из-за аппаратных соображений, упомянутых выше.
источник
Вот почему . MATLAB не выполняет наивное матричное умножение, зацикливаясь на каждом отдельном элементе, как вы делали это в своем коде C ++.
Конечно, я предполагаю, что вы просто использовали
C=A*B
вместо того, чтобы писать функцию умножения самостоятельно.источник
Matlab включил LAPACK некоторое время назад, поэтому я предполагаю, что их умножение матриц использует что-то, по крайней мере, так быстро. LAPACK исходный код и документация легко доступны.
Вы также можете посмотреть статью Гото и Ван Де Гейна «Анатомия высокопроизводительного матричного умножения» по адресу http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.140.1785&rep=rep1&type=pdf.
источник
Ответ заключается в том, что библиотеки LAPACK и BLAS делают MATLAB ослепительно быстрым при матричных операциях, а не каким-либо частным кодом, созданным людьми в MATLAB.
Используйте библиотеки LAPACK и / или BLAS в своем коде C ++ для матричных операций, и вы должны получить производительность, аналогичную MATLAB. Эти библиотеки должны быть свободно доступны в любой современной системе, а их части были разработаны в течение десятилетий в научных кругах. Обратите внимание, что существует несколько реализаций, включая некоторые с закрытым исходным кодом, такие как Intel MKL .
Обсуждение того, как BLAS достигает высокой производительности , доступно здесь.
Кстати, я испытываю серьезную боль при вызове библиотек LAPACK непосредственно из c (но оно того стоит). Вы должны прочитать документацию ОЧЕНЬ точно.
источник
Делая матричное умножение, вы используете наивный метод умножения, который занимает время
O(n^3)
.Существует алгоритм умножения матриц, который принимает
O(n^2.4)
. Это означает, что дляn=2000
вашего алгоритма требуется ~ 100 раз больше вычислений, чем для лучшего алгоритма.Вы действительно должны проверить страницу википедии для умножения матриц для получения дополнительной информации об эффективных способах ее реализации.
источник
Я полагаю, что в зависимости от вашей версии Matlab она уже использует ваш графический процессор.
Еще одна вещь; Matlab отслеживает многие свойства вашей матрицы; по его диагонали, по Герметику и т. д., и специализирует свои алгоритмы на ее основе. Может быть, это специализация, основанная на нулевой матрице, которую вы передаете, или что-то в этом роде? Может быть, это кеширование повторных вызовов функций, которое портит ваши настройки? Возможно, это оптимизирует повторяющиеся неиспользованные матричные продукты?
Чтобы защититься от таких событий, используйте матрицу случайных чисел и убедитесь, что вы форсируете выполнение, распечатав результат на экране или на диске, или еще что-нибудь.
источник
A.*B
делает. Так что ОП почти наверняка что-то обманывает.MATLAB использует высоко оптимизированную реализацию LAPACK от Intel, известную как Intel Math Kernel Library (Intel MKL), в частности, функцию dgemm . Скорость Эта библиотека использует преимущества процессорных функций, включая инструкции SIMD и многоядерные процессоры. Они не документируют, какой конкретный алгоритм они используют. Если бы вы вызывали Intel MKL из C ++, вы должны увидеть аналогичную производительность.
Я не уверен, какую библиотеку MATLAB использует для умножения GPU, но, вероятно, что-то вроде nVidia CUBLAS .
источник
Общий ответ на вопрос «Почему Matlab работает быстрее, чем в других программах», заключается в том, что в Matlab много встроенных оптимизированных функций.
Другие используемые программы часто не имеют этих функций, поэтому люди применяют свои собственные творческие решения, которые удивительно медленнее, чем профессионально оптимизированный код.
Это можно интерпретировать двумя способами:
1) Обычный / теоретический способ: Matlab не значительно быстрее, вы просто делаете неверный тест
2) Реалистичный способ: для этого Matlab быстрее на практике, потому что языки как c ++ слишком легко используются неэффективными способами.
источник
Резкий контраст связан не только с удивительной оптимизацией Matlab (как уже говорилось во многих других ответах), но и с тем, как вы формулировали матрицу как объект.
Кажется, вы сделали матрицу списком списков? Список списков содержит указатели на списки, которые затем содержат ваши матричные элементы. Местоположения содержащихся списков назначаются произвольно. Поскольку вы перебираете свой первый индекс (номер строки?), Время доступа к памяти очень значительно. Для сравнения, почему бы вам не попробовать реализовать матрицу в виде одного списка / вектора, используя следующий метод?
И
Тот же алгоритм умножения следует использовать, чтобы число флопов было одинаковым. (n ^ 3 для квадратных матриц размера n)
Я прошу вас рассчитать время, чтобы результат был сопоставим с тем, что вы имели ранее (на той же машине). Сравнение покажет, насколько значительным может быть время доступа к памяти!
источник
Это медленно в C ++, потому что вы не используете многопоточность. По сути, если A = BC, где все они являются матрицами, первая строка A может быть вычислена независимо от 2-й строки и т. Д. Если A, B и C все n на n матриц, вы можете ускорить умножение на фактор n ^ 2, как
a_ {i, j} = sum_ {k} b_ {i, k} c_ {k, j}
Если вы используете, скажем, Eigen [ http://eigen.tuxfamily.org/dox/GettingStarted.html ], многопоточность встроена, а количество потоков настраивается.
источник
Потому что MATLAB - это язык программирования, изначально разработанный для числовой линейной алгебры (матричные манипуляции), в котором есть библиотеки, специально разработанные для умножения матриц. И теперь MATLAB также может использовать для этого графические процессоры (графические процессоры) .
И если мы посмотрим на ваши результаты вычислений:
тогда мы можем видеть, что не только MATLAB так быстр в умножении матриц: CUDA C (язык программирования от NVIDIA) дает лучшие результаты, чем MATLAB. CUDA C также имеет библиотеки, специально разработанные для умножения матриц, и использует графические процессоры.
Краткая история MATLAB
Что такое CUDA C
CUDA C также использует библиотеки, специально разработанные для умножения матриц, такие как OpenGL (Open Graphics Library). Он также использует GPU и Direct3D (на MS Windows).
Сравнение скорости выполнения CPU и GPU
Из введения в Руководство по программированию CUDA C:
Расширенное чтение
Основные подпрограммы линейной алгебры (BLAS)
Анатомия высокопроизводительного матричного умножения , от Казушиге Гото и Роберта А. Ван Де Гейна
Некоторые интересные лица
источник
"additionally"
. Это значит: это можно использовать. Это также означает, что для нормального умножения матриц все еще используются программные библиотеки. Вы думаете, что я должен изменить свой пост, чтобы быть более понятным? Спасибо за ваши комментарии!