четверг, 2 марта 2017 г.

Подключение библиотек Intel MKL или OpenBLAS к GFortrаn

Раньше я был уверен, что, возможно, самая крутая в мире :) математическая библиотека Intel MKL – полностью коммерческая и с чистой совестью пользоваться ей можно только купив лицензию у Intel. Однако, разбираясь со свободными (совершенно бесплатными) системами для научных вычислений, я обнаружил, что Intel MKL входит в состав некоторых из них и используется там как основной решатель для СЛАУ, а также других задач. Как минимум, она входит:

  • в библиотеки NumPy и SciPy в составе дистрибутива Anaconda Python;

  • в SciLAB (известная MatLAB-подобная свободная система компьютерной математики).

Пойдя на сайт Intel, я увидел, что и оттуда можно скачать MKL совершенно бесплатно. Скачав, я открыл прилагающийся файл о лицензии и прочитал:

Intel License for Intel® Math Kernel Library and Installer Program (version January 2017). Copyright (c) 2017 Intel Corporation.

Use and Redistribution. You may use and redistribute the software (the “Software”), without modification, provided the following conditions are met:

  • Redistributions must reproduce the above copyright notice and the following terms of use in the Software and in the documentation and/or other materials provided with the distribution.
  • Neither the name of Intel nor the names of its suppliers may be used to endorse or promote products derived from this Software without specific prior written permission.
  • No reverse engineering, decompilation, or disassembly of this Software is permitted.

. . .

Я не юрист, но из этого по-моему следует, что я имею полное право пользоваться MKL и даже ее распространять, если уведомлю в документации на свою программу, что копирайт MKL принадлежит Intel.

Видимо, разработчики Anaconda Python и SciLAB так и делают.

Конечно, если действительно на MKL придется завязать какие-то коммерческие проекты, этот вопрос потребует более глубокого юридического изучения. Для меня подобные юридические вопросы – полные дебри. Тут должны разбираться специальные люди. Но надеюсь, что простое экспериментирование с MKL, так же как и расчеты в NumPy и SciLAB, прав Intel не нарушают. :)

Мои «околонаучные» проекты в основном сделаны в GFortran. А Intel MKL для Windows, к сожалению, тесно интегрирована только с Intel Fortran, который уже точно коммерческий и весьма недешевый. (Под Linux MKL официально работает в том числе и с компиляторами GCC).

Подключить Intel MKL к GFortran в Windows на первый взгляд казалось нетривиальной задачей, но, после определенного разбирательства, это у меня получилось. Как – хочу зафиксировать ниже. Если потом все-таки окажется, что юридически это делать нельзя :), Intel MKL всегда можно заменить на OpenBLAS. Как он подключается – тоже запишу здесь для удобства. (Сказанное относится к библиотекам LAPACK и BLAS – больше мне из MKL пока не нужно).

Intel MKL в GFortrаn

Дистрибутив Intel MKL содержит скомпилированные библиотеки .dll, а также библиотеки .lib для компиляции и линковки в Intel Fortran и среде MS Visual Studio.

Мы не используем Visual Studio, а работаем с GFortrаn в составе компиляторов GCC. В принципе, GCC работает и с библиотеками .lib. Однако, корректно подключить все нужные для компиляции MKL .lib к GFortrаn – не получается. Судя по форумам, не у меня одного. Поэтому, чтобы вызывать из нашей программы MKL, попробуем подготовить библиотеки в родном для GCC формате .a и ограничимся динамическим связыванием. В принципе, .a содержит дистрибутив Intel MKL для Linux (я не поленился, установил MKL в Ubuntu), но .a в Ликунсе и в Виндовсе – несовместимы.

Нам надо получить из .dll и .lib библиотеки .a. Это возможно. Оказалось, что проще не использовать для этого .lib вообще. Библиотеки .a можно сделать просто из *.dll. Правда, это будут библиотеки только для динамической компоновки. Т.е. чтобы наша программа работала, в системе должно иметься несколько *.dll с Intel MKL. Кстати, если у нас инсталлирована, например, Anaconda Python, то все необходимые *.dll библиотеки с ней уже установлены.

Итак, у нас есть ряд .dll с Intel MKL. Их можно взять как из Anaconda Python, так и из официального дистрибутива Intel MKL, скачанного с их сайта. Первое, по-моему, даже проще т.к. на сайте Intel нужна регистрация.

Оказалось, что нужные нам функции LAPACK и BLAS в привычном формате вызова содержатся конкретно в файле mkl_rt.dll. Чтобы посмотреть, какие функции входят в *.dll есть разные утилиты. Проще всего воспользоваться утилитой dumpbin.exe из состава Visual Studio:

dumpbin /exports mkl_rt.dll

Она выдает следующее:


Ниже по списку упоминаются привычные функции LAPACK dgesv, dposv и другие.

Чтобы сделать библиотеку *.a из *.dll сначала надо сформировать текстовый файл .def со списком названий функций, т.е. из того, что обведено красным. В начале этого файла надо написать слово EXPORTS. Т.е. файл .def должен выглядеть так:

EXPORTS
CAXPBY
CAXPY
CAXPYI
CAXPY_DIRECT
CBBCSD
CBDSQR
CCOPY
CDOTC
CDOTCI
CDOTU
...

Файл .def можно сформировать из того, что вывел dumpbin.exe руками, а можно сделать скрипт, например, на питоне (привожу его ниже).

Получив .def, осталось сформировать библиотеку *.a (она должна иметь префикс lib и суффикс .dll) с помощью утилиты dlltool.exe из состава GCC:

dlltool -d mkl_rt.def -D mkl_rt.dll -l libmkl_rt.dll.a

Вот скрипт на Питоне, который делает все описанное. Чтобы он работал в системе должны быть доступны dumpbin.exe и dlltool.exe. Кстати, чтобы это проверить, в Windows можно набрать where dumpbin – система выведет путь, где он лежит, либо скажет, что не находит такого.

DllFile = "mkl_rt.dll"  # *.dll-файл, из которого будет сделан *.a-файл

import os

(FileBaseName, FileExt) = os.path.splitext(DllFile)
TmpFile = FileBaseName + ".tmp"
DefFile = FileBaseName + ".def"
AFile   = "lib" + FileBaseName + ".dll.a"

os.system("dumpbin /exports " + DllFile + " > " + TmpFile)

LineNo = 1
FirstLineNo = 0
LastLineNo = 0
TmpFile_ = open(TmpFile, 'r')
DefFile_ = open(DefFile, 'w')
DefFile_.write("EXPORTS\n")
for Line in TmpFile_:
    if Line.find('ordinal hint') >= 0:
        FirstLineNo = LineNo
    if Line.find('Summary') >= 0:
        LastLineNo = LineNo
    if FirstLineNo != 0 and LineNo > FirstLineNo and \
       (LastLineNo == 0 or  LineNo < LastLineNo):
        DefFile_.write(Line[26:]) # Названия функций – начиная с 26 символа
    LineNo += 1
TmpFile_.close()
DefFile_.close()

os.system("dlltool -d" + DefFile + " -D " + DllFile + " -l " + AFile)
os.system("DEL *.tmp")

Полученный файл *.a надо положить туда, где GCC хранит свои библиотеки, либо в папку с нашими собственными библиотеками, например, C:\gcc\mylibs.

В принципе, это все. Проверим как все работает.

Протестируем MKL на примере из прошлого поста с тестом решения СЛАУ.

Тестовая программа на Фортране (в скоростном варианте с \(LL^T\)-разложением) выглядит так (файл LinTest.f90):

program LinTest
    implicit none
    integer, parameter      :: n = 1000 ! Число уравнений
    integer, parameter      :: m = 100  ! Число шагов
    real(8), dimension(n,n) :: A, A0    ! Матрица коэффициентов
    real(8), dimension(n)   :: B        ! Вектор правых частей / результатов
    real(8)                 :: s        ! Контрольная сумма
    integer                 :: i, j, k  ! Вспомог. индексы
    integer                 :: info     ! Инфо об успешности решения
    integer                 :: t_0, t_1, t_rate, t_max ! Для измер. времени
    !------------------------------------------------------------------------ 
    forall(i=1:n, j=1:n) A0(i,j) = 1-(dble(i-j)/n)**2 
    forall(i=1:n, j=1:n, i==j) A0(i,j) = 10.
    s = 0.
    call system_clock(t_0, t_rate, t_max) 
    do k = 1, m
        A=A0
        B=k
        call dposv("L", n, 1, A, n, B, n, info)  ! Вызов процедуры LAPACK
        if (info/=0) stop ("Ошибка")
        s = s + sum(B)
    enddo
    call system_clock(t_1, t_rate, t_max)
    print *, "Время:", real(t_1-t_0)/t_rate
    print *, "Сумма:", s
endprogram

Условно статическая компиляция (пусть наша библиотека libmkl_rt.dll.a лежит в C:\gcc\mylibs):

gfortran LinTest.f90 -static -L C:\gcc\mylibs -lmkl_rt.dll -O2 -o LinTest.exe

Запуская программу, получаем:

LinTest.exe
 Время:  0.328000009
 Сумма:  22439.195088956156 

Время 0,33 с – точно такое же как и у лидера прошлого теста Intel Fortran + MKL.

Размер LinTest.exe получился 595 КБ (компилятор был из GCC 6.3.0 из дистрибутива MinGW64). В него статически прилинковались все библиотеки, кроме .dll-библиотек MKL.

Какие в принципе .dll нужны для работы нашей программы? Если мы хотим, чтобы наша программа работала на другой машине, не имеющей установленную Анаконду или другой вариант MKL, надо будет скопировать и обеспечить доступность 5-ти файлов:

Размер       Файл
----------------------------------
  1 328 896  libiomp5md.dll
 37 854 480  mkl_avx2.dll
 25 122 064  mkl_core.dll
 24 297 744  mkl_intel_thread.dll
 12 629 264  mkl_rt.dll
----------------------------------
101 232 448 байт

100 МБ – конечно, не мало. Это вариант с последним дистрибутивом MLK с сайта Intel, версия 2017.2.187. Из дистрибутива Анаконды набегает поменьше – «всего» 86 МБ. Современный софт, он такой. :)

Полностью динамическая компиляция (убираем ключ -static):

gfortran LinTest.f90 -L C:\gcc\mylibs -lmkl_rt.dll -O2 -o LinTest.exe

LinTest.exe получился крошечный – 57 КБ, но теперь для работы потребуются еще три .dll из GCC (данные для версии 6.3.0):

Размер      Файл
----------------------------------
    75 264  libgcc_s_seh-1.dll
 1 277 952  libgfortran-3.dll
   325 632  libquadmath-0.dll
----------------------------------
 1 678 848 байт

OpenBLAS в GFortrаn

Установить OpenBLAS – куда проще.

Скачиваем с официального сайта уже скомпилированные бинарные файлы в архиве OpenBLAS-v0.2.19-Win64-int32.zip (версия для 64-разрядных машин).

Переписываем файлы libopenblas.a и libopenblas.dll.a туда, где GCC хранит свои библиотеки, либо в папку с нашими собственными библиотеками, например, C:\gcc\mylibs.

Статическая компиляция (пусть наши библиотеки *.a лежат в C:\gcc\mylibs):

gfortran LinTest.f90 -static -L C:\gcc\mylibs -lopenblas -O2 -o LinTest.exe

Запускаем:

LinTest.exe
 Время:  0.483999997
 Сумма:   22439.195088956145 

Время на 48% больше, чем с Intel Fortran + MKL, но на самом деле тоже очень достойное.

Размер файла LinTest.exe , правда, громадный: 26 МБ.

Динамическая компиляция (убираем -static и добавляем к -lopenblas суффикс .dll):

gfortran LinTest.f90 -L C:\gcc\mylibs -lopenblas.dll -O2 -o LinTest.exe

Размер LinTest.exe – всего 57 КБ. Для работы потребуется libopenblas.dll из дистрибутива OpenBLAS и еще три DLL из GCC. Общий расклад (данные для версии 6.3.0):

Размер      Файл
----------------------------------
    75 264  libgcc_s_seh-1.dll
 1 277 952  libgfortran-3.dll
42 587 753  libopenblas.dll
   325 632  libquadmath-0.dll
----------------------------------
44 266 601 байт

«Всего» 44 МБ – не так и страшно. :)

Комментариев нет:

Отправить комментарий