Умножение матриц, серия 2: MKL против компилятора, single/double и int
C=alpha*A*B+beta*C
Нас интересует, естественно, самый быстрый способ из изученных ранее, а вопрос заключается в разнице в скорости между float и double и разницы в скорости между простым кодом, написанным вручную, и библиотечной реализацией.
Оборудование
Все тесты делались на Dual Opteron 275 под управлением Windows XP x64. Размер кэша составляет 1 мегабайт на ядро, памяти достаточно.Ручное написание кода
Перемножающий код практически тот же самый, добавляется половинка строчки:C(i,j)=alpha*sum+beta*D(i,j)
.
Самодельный код компилировался Intel C++ Compiler 9.1.028 с включенной генерацией SSE3-кода и без распараллеливания.
В таблице времена исполнения в секундах для разных типов данных.
Тип данных | размер задачи | ||||
---|---|---|---|---|---|
256 | 512 | 1024 | 2048 | 4096 | |
SSE3 | |||||
int (32 bit) | 0.031 | 0.25 | 2.25 | 20.22 | 152.4 |
float (32 bit) | 0.015 | 0.078 | 1.89 | 14.78 | 123.1 |
double (64 bit) | 0.015 | 0.391 | 3.55 | 28.0 | 225 |
Без SSE | |||||
float (32 bit) | 0.047 | 0.281 | 2.58 | 20.2 | 163.9 |
double (64 bit) | 0.031 | 0.453 | 4.03 | 31.55 | 257.6 |
Intel Math Kernel Library
Возьмем библиотечную реализацию: Intel MKL, версия 9.0.17. Перемножаем квадратные матрицы, состоящие из случайных положительных элементов от 0 до 1.Мгновенно выяснилось, что задачи с размером 128-512 бенчмаркать нет смысла, не хватает разрешения таймера Windows. Столь же мгновенно выяснилось, что транспонирование не имеет значения, отклонения в пределах ошибки таймера. Для задач размером 1024-4096 времена исполнения (в секундах) приведены в таблице:
Тип данных | размер задачи | ||
---|---|---|---|
1024 | 2048 | 4096 | |
single (32 bit) | 0.375 | 2.89 | 23.25 |
double (64 bit) | 0.688 | 5.56 | 49.375 |
Независимо от помещаемости в кэш, MKL-версия впятеро быстрее самодельной. Пропорция от размера задачи и от используемой точности практически не зависит, я предполагаю, что сработала тут ручная SSE-оптимизация.
Параллельная версия
MKL позволяет использовать распараллеливание через OpenMP без перекомпиляции, достаточно установить переменную окружения OMP_NUM_THREADS в положительное значение.Результаты для параллельного варианта приведены в таблице (время в секундах):
Количество потоков | размер задачи | ||
---|---|---|---|
1024 | 2048 | 4096 | |
single precision | |||
2 threads | 0.187 | 1.453 | 11.797 |
4 threads | 0.094 | 0.734 | 5.95 |
double precision | |||
2 threads | 0.344 | 2.891 | 25.609 |
4 threads | 0.187 | 1.438 | 12.609 |
Comments
> Мгновенно выяснилось, что задачи с размером 128-512 бенчма
> Мгновенно выяснилось, что задачи с размером 128-512 бенчмаркать нет смысла, не хватает разрешения таймера Windows.
Отчего же не воспользоваться QueryPerformanceCounter или вариацией на тему RDTSC?
Да темные мы, как-то не по виндам. В любом случае, какой см
Да темные мы, как-то не по виндам.
В любом случае, какой смысл в маленьких задачах ? Прикидывать, когда выгодно переходить к Штрассену ? Так это уже давно померяли, начиная примерно с 1000x1000
Мне нужно было перемножить матрицу 400000x50 и вектор(длина
Мне нужно было перемножить матрицу 400000x50 и вектор(длина 50). Я использовал компилятор Visual Studio 2005
с включенными опциями Streaming SIMD Extensions 2 (/arch:SSE2) и Fast floating point model (/fp:fast).
Из Intel MKL я использовал функцию cblas_sgemv и сравнивал ее с простыми вложенными циклами, написанными на С.
Приятно, что получил примерно одинаковый числовой результат :)
НО по скорости cblas_sgemv оказалась чуть медленней, а не быстрее (я в цикле производил 400 умножений).
У меня машина Intel Core 2, ОЗУ 2 ГБ. Как думаете, почему Intel MKL ничего не дала?