2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3, 4, 5  След.
 
 Перемножение матриц в С++
Сообщение14.06.2017, 06:37 


07/10/15

2400
В моё приложении на С++ основное время затрачивается на перемножение квадратных матриц,
которое реализуется следующим образом
  1. ....................................... 
  2. for (ii=0; ii<NStr; ii++) 
  3.       for (j=0; j<NStr; j++) 
  4.       {   ind=j*NStr; 
  5.           *(D+ii+ind)=-*(A+ii)**(B+ind); 
  6.           for (k=1; k<NStr; k++) 
  7.               *(D+ii+ind)-=*(A+ii+k*NStr)**(B+k+ind); 
  8.       } 
  9. ...................................... 


Матрицы размерами 1000х1000 перемножаются примерно за 10 секунд (проверял с помощью функции clock()),
однако проведя аналогичную операцию в системе Matlab , я был мягко говоря озадачен

  1. >> A=randn(1000,1000); 
  2. >> B=randn(1000,1000); 
  3. >> tic;D=A*B;toc; 
  4. Elapsed time is 0.122922 seconds. 
  5. >>  


Matlab выполняет перемножение примерно в 100 раз быстрее.
Не могу понять в чём дело? как можно оптимизировать алгоритм?
вообще программы на С++ обычно работают быстрее чем в Matlab,
кроме того, насколько я понял никаких быстрых алгоритмов перемножения матриц такого размера не существует.
Подскажите пожалуйста как лучше организовать вычисления?

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 08:49 
Заслуженный участник


16/02/13
4112
Владивосток
А почему там -= вместо +=?
А ** — это умножить на число по адресу? Или ++ понимает как возведение в степень?
Можно попробовать ввести ещё несколько указателей для исключения умножения при взятии элементов матриц по индексам.
Возможно, функция Matlab написана на ассемблере, а это ещё быстрее.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 08:51 


11/12/14
893
Посмотрите в википедии - в статье "умножение матриц" целый раздел выделен про "быстрое умножение матриц".
P.S.
Скорее всего вы неправильно произвели оценку. Между 1000 в степени 3 и 1000 в степени 2,3727 значительная разница.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 09:06 
Заслуженный участник
Аватара пользователя


16/07/14
8456
Цюрих
Поменяйте порядок циклов так, чтобы самый внутренний шел по памяти подряд (если я правильно понял, это будет цикл по ii). Это потребует предварительно инициализировать D нулями, но не страшно.

Потом посмотрите, справляется ли компилятор с векторизацией и попробуйте скомпилировать с флагами -Ofast -march=native -mtune=native

Алгоритм Копперсмита-Винограда на практике интереса не представляет.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 10:27 
Заслуженный участник
Аватара пользователя


06/10/08
6422
Andrey_Kireew в сообщении #1225268 писал(а):
В моё приложении на С++ основное время затрачивается на перемножение квадратных матриц,
Возьмите BLAS.

aa_dav в сообщении #1225284 писал(а):
Посмотрите в википедии - в статье "умножение матриц" целый раздел выделен про "быстрое умножение матриц".
P.S.
Скорее всего вы неправильно произвели оценку. Между 1000 в степени 3 и 1000 в степени 2,3727 значительная разница.
Это не то - Вы забыли константу, обычно хорошо написанной стандартное умножение лучше Штрассена до размера где-то несколько тысяч.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 12:16 


08/10/10
50
Andrey_Kireew в сообщении #1225268 писал(а):
Матрицы размерами 1000х1000 перемножаются примерно за 10 секунд (проверял с помощью функции clock()),

А у меня получилось 8 сек. Без особых ухищрений.
Visual Studio 2015, Debug x86 (все по умолчанию); Тип данных double.
Если немножко пошаманить, то меньше одной сек - но до матлаба не дотягивает.
"Пошаманить" значит: включить Release конфигурацию, включить оптимизацию Full и favor for speed. А также использовать тип данных float.
Как ни странно, если все это сделать, то
mihaild в сообщении #1225288 писал(а):
Поменяйте порядок циклов так, чтобы самый внутренний шел по памяти подряд

приводит к замедлению.
Код:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <stdlib.h>
#include <chrono>
#include <iostream>

void main()
{
        typedef float elemtype;
        static elemtype a[1000][1000];
        static elemtype b[1000][1000];
        static elemtype c[1000][1000];
        for (int j1 = 0; j1 < 1000; j1++)
        {
                for (int j2 = 0; j2 < 1000; j2++)
                {
                        a[j1][j2] = elemtype(rand());
                        b[j1][j2] = elemtype(rand());
                        c[j1][j2] = 0;
                }
        }
        std::cout << "Multiplication started" << std::endl;
        auto startTime = std::chrono::system_clock::now();
        for (int j1 = 0; j1 < 1000; j1++)
        {
                for (int j2 = 0; j2 < 1000; j2++)
                {
                        for (int j3 = 0; j3 < 1000; j3++)
                        {
                                c[j1][j2] += a[j1][j3] * b[j3][j2];
                        }
                }
        }
        std::chrono::duration<double> delay = std::chrono::system_clock::now() - startTime;
        std::cout << "Multiplication ended: delay = " << delay.count() << std::endl;
}
 

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 12:38 
Заслуженный участник


09/05/12
25179
Xaositect в сообщении #1225303 писал(а):
Это не то - Вы забыли константу, обычно хорошо написанной стандартное умножение лучше Штрассена до размера где-то несколько тысяч.
Так $O(n^{\approx 2.37})$ - это не Штрассен, это Копперсмит-Виноград (который и при любых реальных размерах проиграет из-за константы).

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 13:03 
Заслуженный участник
Аватара пользователя


16/07/14
8456
Цюрих
iakovk в сообщении #1225338 писал(а):
Visual Studio 2015, Debug x86
Смотреть на производительность в отладочной сборке - бесполезно, там получается случайное число.
iakovk в сообщении #1225338 писал(а):
приводит к замедлению.
Ну у вас же не подряд идет. Внутренний цикл у вас по j3, который идет старшим индексом у b. В вашем коде правильный порядок - j1, j3, j2. На моей машине ваш код отрабатывает за 6.5с, после перестановки циклов - за 0.22с.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 15:03 


07/10/15

2400
Спасибо за многочисленные комментарии,
отвечаю на вопросы по порядку:
iifat в сообщении #1225282 писал(а):
А почему там -= вместо +=?

просто матрица должна быть со знаком минус и это сделано чтобы не заводить новых циклов, вряд ли это как то влияет на производительность
iifat в сообщении #1225282 писал(а):
А ** — это умножить на число по адресу?

совершенно верно
iifat в сообщении #1225282 писал(а):
Можно попробовать ввести ещё несколько указателей для исключения умножения при взятии элементов матриц по индексам.

я пробовал это делать - но производительность не меняется, переменная ind введена по тем же соображениям - однако если её убрать и в скобках написать
  1.  *(D+ii+j*NStr)-=*(A+ii+k*NStr)**(B+k+j*NStr);  

производительность остаётся такой же, так что видимо это не самое узкое место
iifat в сообщении #1225282 писал(а):
Возможно, функция Matlab написана на ассемблере, а это ещё быстрее

возможно это и так, можно будет потом попробовать и написать ассемблерную вставку, только даст ли это существенный выигрыш? если ускорение будет 1.5 - 2 раза, то лучше и не связываться

aa_dav в сообщении #1225284 писал(а):
Посмотрите в википедии

это я всё смотрел, где то в сети написано, что в Matlab реализован эффективный алгоритм перемножения, но что за алгоритм конкретно - не сказано.
Цитата:
Скорее всего вы неправильно произвели оценку. Между 1000 в степени 3 и 1000 в степени 2,3727 значительная разница.

что я 0.1 секунду от 10 неправильно отличил? показатель степени как таковой меня интересует мало, хотя согласен с последующими комментариями 2,3727 - это вряд ли, так как это практически бесполезный алгоритм. Скорее всего там алгоритм Штрассена с логарифмом 2.8 и при этом маленькая константа, но это всё мои догадки ...
Xaositect в сообщении #1225303 писал(а):
Возьмите BLAS

спасибо, попробую
iakovk в сообщении #1225338 писал(а):
А у меня получилось 8 сек. Без особых ухищрений

Это только подтверждает, что никаких особых ошибок у меня нет.
Замедление на пару сек. может быть из за изменения загрузки компьютера, у меня каждый раз получаются разные цифры, так что на отклонения менее 10-15% думаю ориентироваться не стоит. Прирост должен быть в разы, а в идеале - на порядки.
mihaild в сообщении #1225352 писал(а):
На моей машине ваш код отрабатывает за 6.5с, после перестановки циклов - за 0.22с.

спасибо, попробую, это уже кое что

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 15:14 
Заслуженный участник


05/08/14
1564
Вы можете скомпилировать м-файл и потом посмотреть С++ коды, какой получится алгоритм.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 15:33 
Заслуженный участник
Аватара пользователя


16/07/14
8456
Цюрих
Еще можно спросить компилятор, векторизует ли он код (это тоже влияет на производительность):
Код:
$ clang++ -std=c++14 -Ofast -march=native -mtune=native me.cpp -Rpass=loop-vectorize -Rpass-missed=loop-vectorize  -Rpass-analysis=loop-vectorize
me.cpp:24:4: remark: vectorized loop (vectorization width: 8, interleaved count: 4) [-Rpass=loop-vectorize]
                        for (int j2 = 0; j2 < 1000; j2++) {
                        ^

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

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 15:47 


10/04/12
704
А MATLAB в один поток считает?

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 15:53 
Заслуженный участник


20/08/14
11172
Россия, Москва
По моему у Вас сильно недооптимизированы вычисления (по вине компилятора), за то же время можно выполнить в тысячу раз больше операций, если использовать современный процессор и FMA расширения системы команд:
SY1234 в сообщении #1043753 писал(а):
Haswell - очень удачная архитектура: позволяет, например, на 6-ти ядерном процессоре с частотой 3.3 GHz перемножить две квадратные матрицы double с n=10000 всего за 7.0 с для оси x64.
Матрицы вдесятеро больше, а время примерно то же! Я тогда прикидывал по тактам, выходило практически пиковая теоретическая производительность процессора по умножениям достигалась. На немного более старом процессоре (лишь с AVX расширением) скорость умножений упадёт ровно вдвое. Без даже AVX совсем грустно (ещё ровно вдвое медленнее).

(Прикидка теоретической скорости)

Можно и снова прикинуть, учитываем только умножения (сложения выполняются бесплатно, в параллельном потоке на том же ядре процессора), скорость работы с памятью считаем уже оптимизированной и не тормозящей процесс (именно для этого циклы переставляются). FMA команда выполняет 4 умножения за полтакта в потоке, т.е. 8 умножений за такт. AVX на более старом процессоре позволяет умножать 4 числа за такт.
Для перемножения матриц 1000х1000 надо выполнить суммарно $10^9$ умножений. Время перемножения будет не меньше $T=\dfrac{10^9}{8|4 \cdot N_\text{core} \cdot \text{Freq}}= 83|42 \text{мс}$ на каждый поток для 3ГГц процессора (83 для более старого процессора без FMA). На четырёх потоках время может быть 10 (FMA) - 20 (AVX) мс.
Повторю, с использованием оптимизированных библиотек (от той же Intel) или с оптимизацией на асме вполне достигается до 90% этой теоретической скорости.
Разумеется это лишь чистое перемножение матриц, без дополнительных операций над ними.

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 16:15 


07/10/15

2400
mustitz в сообщении #1225403 писал(а):
А MATLAB в один поток считает?

да в один поток,
кстати экспонента алгоритма перемножения matlab примерно 2.69, это лучше, чем алгоритм Штрассена

 Профиль  
                  
 
 Re: Перемножение матриц в С++
Сообщение14.06.2017, 20:06 


07/10/15

2400
mihaild в сообщении #1225288 писал(а):
Поменяйте порядок циклов так, чтобы самый внутренний шел по памяти подряд (если я правильно понял, это будет цикл по ii). Это потребует предварительно инициализировать D нулями, но не страшно.


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

код: [ скачать ] [ спрятать ]
  1. indD=D; 
  2.    for (j=0; j<NStr; j++) 
  3.    { indB=B; 
  4.       for (ii=0; ii<NStr; ii++) 
  5.       { 
  6.          *indD=*indB**(M+j*NStr); 
  7.            indD++; indB++; 
  8.       } 
  9.    } 
  10.     
  11.   indB=B; 
  12.   for (k=1; k<NStr; k++) 
  13.   {   indD=D; 
  14.       for (j=0; j<NStr; j++) 
  15.       { val=*(M+k+j*NStr);   
  16.           for (ii=0; ii<NStr; ii++) 
  17.           { 
  18.               *indD+=*indB*val; 
  19.                indD++; indB++; 
  20.           } 
  21.                indB-=NStr; 
  22.       } 
  23.       indB++; 
  24.   } 


теперь цикл выполняется за 1.5 секунды (вместо 10), ускорение в 6 с лишним раз

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 64 ]  На страницу 1, 2, 3, 4, 5  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: YandexBot [bot]


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group