2.1- Build OpenBLAS sur windows 64

2.1.1- Méthodes de build identifiées

Différentes méthodes de build d’OpenBLAS sont proposées :

Après de très nombreux essais infructueux, la procédure de build d’OpenBLAS adoptée s’inspire largement de M1 et emprunte à M2 la création (probablement optionnelle) de librairie d’import pour Visual Studio.

Remarque:

La méthode M4 est une alternative intéressante. La configuration de l’environnement conda est simple. Les solutions de build de librairies OpenBLAS sont bien décrites. L’exemple par défaut produit le fichier libopenblas.lib (70 Mo) en 3h, quand notre procédure en demande 4h30. Par contre, la compilation d’une librairie python (numpy) avec le résultat du build n’est pas abordée dans cet article (cf utilisation des mêmes compilateurs pour OpenBLAS et numpy).

A tester:

  • Adapter la procédure de build d’OpenBLAS de M4 pour produire une dll (-DBUILD_SHARED_LIBS=ON dans la commande cmake)
  • Décliner la procédure de build de numpy proposée plus loin en pointant via les variables CC et FC vers les compilateurs clang-cl et flang utilisés pour OpenBLAS dans M4.

2.1.2- Configuration

Sur une première machine (Conf_win_1):

Configuration initiale : Windows 7 pro 64 bits SP1 (avec windows update complet)

Installation de python:

  • Installation standard de python 3.7.5: via l’installeur
  • Pré requis supplémentaires:
    >>> python –m pip install wheel setuptools Cython>=0.29.13
    

Remarque:

L’utilisation de Visual Studio avec python peut nécessiter la mise à jour de setuptools:

>>> python –m pip install –U pip setuptools

Ressources:

Installation de git: installation de git 2.24 depuis https ://git-scm.com/download/win

Outils de compilation GNU pour windows: Installation de Cygwin, MinGW et MinGW-w64 d’après l’article de référence.

Remarque importante:

Lors de l’installation de Cygwin sélectionner le package Devel dans son intégralité pour s’assurer d’avoir tous les compilateurs nécessaires.

System path: à l’issue des installations standards de ces outils, la variable d’environnement système Path comprend notamment les chemins suivants:

  • C:\MinGW;
  • C:\MinGW\bin;
  • C:\cygwin64\bin;
  • %systemroot%\System32\WindowsPowerShell\v1.0\;
  • C:\Program Files\Git\cmd;

2.1.3- Procedure de build d’OpenBLAS adoptée

Dans le terminal Cygwin64 (en mode administrateur).

Etape 1: clonage

Clonage d’OpenBLAS dans un dossier de travail:

$ cd "C:\Workdir"
$ git clone https://github.com/xianyi/OpenBLAS.git
$ cd OpenBLAS

Structure de fichiers partielle à l’issue du clonage :

C:/Workdir
└── OpenBLAS
    ├── …
    └── Makefile.rule

Etape 2: param.

Paramètres de build spécifiés dans le fichier Makefile.rule à la racine du dossier OpenBLAS (seuls les paramètres actifs sont reportés ci-dessous):

#
#  Beginning of user configuration
#
[…]
VERSION = 0.3.8.dev
DYNAMIC_ARCH = 1
DYNAMIC_OLDER = 1
CC = x86_64-w64-mingw32-gcc
FC = x86_64-w64-mingw32-gfortran
HOSTCC = gcc
BINARY=64
USE_THREAD = 0
USE_LOCKING = 1
BUILD_LAPACK_DEPRECATED = 1
NO_WARMUP = 1
NO_AFFINITY = 1
COMMON_PROF = -pg
[…]
#
#  End of user configuration
#

Remarque:

OpenBLAS pouvant poser quelques problème sur Windows 64 bits en version multi threaded, la version single threaded (soit USE_THREAD = 0) semble conseillée (source).

Etape 3: MSVC

Activation des outils Visual Studio:

$ cmd /k C:\\Program\ Files\ \(x86\)\\Microsoft\ Visual\ Studio\\2019\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat

Le terminal affiche les informations suivantes :

**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.3.10
** Copyright (c) 2019 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

Etape 4: compilation

On lance la compilation:

$ make

4h30 plus tard (la machine est ancienne), le terminal conclue sur ces mots :

[…]
OpenBLAS build complete. (BLAS CBLAS LAPACK LAPACKE)
  OS               ... WINNT
  Architecture     ... x86_64
  BINARY           ... 64bit
  C compiler       ... GCC  (command line : x86_64-w64-mingw32-gcc)
  Fortran compiler ... GFORTRAN  (command line : x86_64-w64-mingw32-gfortran)
  Library Name     ... libopenblas-r0.3.8.dev.a (Single threaded)
To install the library, you can run "make PREFIX=/path/to/your/installation install".
install :

Etape 5: installation

Création du dossier d’installation d’OpenBLAS.

On crée un sous répertoire dédié :

$ mkdir -p package

Puis on lance le processus d’installation :

$ make PREFIX=./package install

Le terminal affiche les informations suivantes :

make PREFIX=./package install
make -j 2 -f Makefile.install install
make[1] : on entre dans le répertoire « /cygdrive/c/Workdir/OpenBLAS »
Generating openblas_config.h in ./package/include
Generating f77blas.h in ./package/include
Generating cblas.h in ./package/include
Copying LAPACKE header files to ./package/include
Copying the static library to ./package/lib
Copying the shared library to ./package/lib
Generating openblas.pc in ./package/lib/pkgconfig
Generating OpenBLASConfig.cmake in ./package/lib/cmake/openblas
Generating OpenBLASConfigVersion.cmake in ./package/lib/cmake/openblas
Install OK!
make[1] : on quitte le répertoire « /cygdrive/c/Workdir/OpenBLAS »

A l’issue de cette étape la structure de fichiers est la suivante :

C:/Workdir 
└── OpenBLAS
    ├── …
    └── package
        ├── bin
        │   └── libopenblas.dll
        ├── include
        │   └── headers c (*.h)
        └── lib
            ├── libopenblas.a
            ├── libopenblas.dll.a
            ├── libopenblas-r0.3.8.dev.a
            ├── cmake
            └── pkgconfig

Etape 6: lib d’import

Création (optionnelle) de la librairie d’import pour Visual Studio (source).

Dans le terminal x64 Native Tools Command Prompt for VS2019:

On se déplace dans le sous dossier exports d’ OpenBLAS :

> cd C:\Workdir\OpenBLAS\exports

On lance l’outil lib.exe de la suite d’outils Visual Studio :

> lib.exe /machine:x64 /def:libopenblas.def

Le terminal affiche les informations suivantes :

Microsoft (R) Library Manager Version 14.23.28107.0
Copyright (C) Microsoft Corporation.  All rights reserved.
   Création de la bibliothèque libopenblas.lib et de l'objet libopenblas.exp

Deux fichiers ont été créés dans le dossier exports :

C:/Workdir 
└── OpenBLAS
    ├── …
    └── exports
        ├── libopenblas.exp
        ├── libopenblas.lib
        └── …

On copie ces deux fichiers dans le sous dossier package/lib :

C:/Workdir 
└── OpenBLAS
    ├── …
    └── package
        ├── bin
        │   └── libopenblas.dll
        ├── include
        │   └── headers c (*.h)
        └── lib
            ├── libopenblas.a
            ├── libopenblas.dll.a
            ├── libopenblas-r0.3.8.dev.a
            ├── libopenblas.exp
            ├── libopenblas.lib
            └── …

A présent OpenBLAS /package constitue notre dossier d’installation OpenBLAS.

Remarque:

Taille des fichiers :

  • libopenblas.dll (41 314 ko)
  • libopenblas.a (1 ko)
  • libopenblas.dll.a (5 290 ko)
  • libopenblas-r0.3.8.dev.a (49 786 ko)
  • libopenblas.lib (1 595 ko)

Nature des fichiers (source):

  • libopenblas.a : est la librairie statique pour MinGW sur windows
  • libopenblas.dll.a est la librairie d’import pour Visual Studio, équivalente à libopenblas.lib (pourtant de tailles différentes)

Le fait que la librairie statique ne fasse que 1 ko interroge pourtant. Ne s’agirait-il pas plutôt de libopenblas-r0.3.8.dev.a qui fait près de 50 Mo ?

2.2- Compilation de Numpy avec OpenBLAS

Comme pour la compilation d’OpenBLAS, la procédure de build de Numpy + OpenBLAS proposée ci-dessous est le fruit d’un grand nombre d’essais/erreurs.

Les ressources suivantes ont été utiles :

2.2.1- Mise à jour de la configuration

Pour éviter des échecs de chargement de dlls lors du build de Numpy (en particulier pour l’objet multiaaray_umath) il s’avère nécessaire d’étendre le PATH système.

L’outil Dependancy Walker indique que libopenblas.dll dépend de deux autres dlls « manquantes »: libgcc_s_seh-1.dll et LIBGFORTRAN-4.DLL. Ces deux dlls (présentes dans différents dossiers de C:) partagent un emplacement commun: C:\cygwin64\usr\x86_64-w64-mingw32\sys-root\mingw\bin.

On ajoute ce chemin à la variable d’environnement système PATH, à présent, cette variable comprend en particulier:

  • C:\MinGW;
  • C:\MinGW\bin;
  • C:\cygwin64\bin;
  • %systemroot%\System32\WindowsPowerShell\v1.0\;
  • C:\Program Files\Git\cmd;
  • C:\cygwin64\usr\x86_64-w64-mingw32\sys-root\mingw\bin

2.2.2- Procédure de build de Numpy avec OpenBLAS

Dans le terminal Cygwin64 (en mode administrateur).

Etape 1: ENV VARS

Variables d’environnement:

$ export DNDEBUG=1 
$ export OPT=1
$ export CC=c:/cygwin64/bin/x86_64-w64-mingw32-gcc.exe
$ export FC=c:/cygwin64/bin/x86_64-w64-mingw32-gfortran.exe

Remarques:

  • La première ligne limite les traces affichées dans la console
  • La seconde active l’optimisation pour le compilateur C. Attention l’optimisation pour le compilateur Fortran (FOPT=1) crée une erreur
  • Les deux dernières lignes indiquent explicitement les compilateurs C et Fortran à utiliser (les mêmes que ceux utilisés pour OpenBLAS)

Etape 2: clonage

Clonage des sources numpy dans le répertoire de travail:

$ cd c:
$ cd "c:/Workdir"
$ git clone https://github.com/numpy/numpy.git
$ cd numpy

Etape 3: insertion

Insertion d’élément de OpenBLAS/package dans les sources numpy:

C:/Workdir 
├── OpenBLAS
└── numpy
    ├── …
    └── numpy
        ├── …
        └── core
            ├── …
            ├── libopenblas.dll # (1)
            ├── include
            │   └── numpy
            │       └── libopenblas 
            │           └── headers c (*.h) # (2)
            └── libopenblas
                └── lib # (3)

Références:

  • (1) La dll est copiée depuis OpenBLAS/package/bin. On la copie dans numpy/numpy/core car le plugin Numpy de Nuitka la recherchera ici ou dans un éventuels dossier numpy/.libs.
  • (2) numpy/numpy/core/include/numpy/libopenblas contient les copies des hearders c contenus dans OpenBLAS/package/include. Le dossier numpy/numpy/core/include/numpy existant déjà dans les sources on y ajoute les include OpenBLAS dans un sous dossier dédié.
  • (3) numpy/numpy/core/libopenblas/lib contient les copies des fichiers contenus dans OpenBLAS/package/lib.

Etape 4: config.

Configuration du build.

Repérage des trois fichiers à modifier dans les sources numpy :

C:/Workdir 
├── OpenBLAS
└── numpy
    ├── …
    ├── setup.py
    ├── site.cfg.example # -> site.cfg après modif
    └── numpy
        ├── …
        └── setup.py

Remarque:

Dans cette solution on ne modifie pas le fichier MANIFEST.in.

Modifications apportées à numpy/setup.py

Avant la fonction setup_package(), ligne 370 on insère le code suivant:

#************************************************************************
# MODIF 1 :
from setuptools import setup, Distribution
class BinaryDistribution(Distribution):
    def has_ext_modules(foo):
        return True
#************************************************************************

Puis au sein de la fonction setup_package(), on insère ligne 448 le code suivant:

    #************************************************************************
    # MODIF 2 :
    metadata["distclass"]=BinaryDistribution
    #metadata["include_package_data"]=True
    #************************************************************************

Qui précède à présent le dernier bloc try/finally/return de la fonction.

Modifications apportées à numpy/numpy/setup.py

Le fichier étant court, voici sa version modifiée :

#!/usr/bin/env python
from __future__ import division, print_function

def configuration(parent_package='',top_path=None):
    from numpy.distutils.misc_util import Configuration
    config = Configuration('numpy', parent_package, top_path)
    config.add_subpackage('compat')
    config.add_subpackage('core')
    config.add_subpackage('distutils')
    config.add_subpackage('doc')
    config.add_subpackage('f2py')
    config.add_subpackage('fft')
    config.add_subpackage('lib')
    config.add_subpackage('linalg')
    config.add_subpackage('ma')
    config.add_subpackage('matrixlib')
    config.add_subpackage('polynomial')
    config.add_subpackage('random')
    config.add_subpackage('testing')
    config.add_data_dir('doc')
    config.add_data_dir('tests')
    #************************************************************************
    # MODIF : openblas et ses dépendances
    config.add_data_dir(('core/include/numpy/libopenblas', 'core/include/numpy/libopenblas'))
    config.add_data_files(('core', 'core/libopenblas.dll'))
    #************************************************************************
    config.make_config_py() # installs __config__.py
    return config

if __name__ == '__main__':
    print('This is the wrong setup.py file to run')

Cette modification assure la copie de la dll et des headers dans le résultat du build.

Création de numpy/site.cfg

Ce fichier est créé par enregistrement de numpy/site.cfg.example en numpy/site.cfg.

Les seules lignes actives de ce fichiers (ie non précédées de #) sont les suivantes :

[openblas]
libraries = openblas
library_dirs = C:\Workdir\numpy\numpy\core\libopenblas\lib
include_dirs =  C:\Workdir\numpy\numpy\core\include\numpy\libopenblas
src_dirs=C:\Workdir\OpenBLAS, C:\Workdir\OpenBLAS\lapack-netlib\BLAS\SRC , C:\Workdir\OpenBLAS\lapack-netlib\CBLAS\src, C:\Workdir\OpenBLAS\lapack-netlib\LAPACKE\src, C:\Workdir\OpenBLAS\lapack-netlib\SRC, C:\Workdir\OpenBLAS\relapack\src, C:\Workdir\OpenBLAS\reference, C:\Workdir\OpenBLAS\interface

Les variables library_dirs et include_dirs pointent explicitement vers les fichiers copiés dans les sources Numpy. A contrario, src_dirs pointe vers tous les répertoires originaux d’OpenBLAS contenant du code source.

Etape 5: MSVC

Activation des outils Visual Studio:

$ cmd /k C:\\Program\ Files\ \(x86\)\\Microsoft\ Visual\ Studio\\2019\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat

Le terminal affiche les informations suivantes :

**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.3.10
** Copyright (c) 2019 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

Etape 6: compilation

On lance la compilation:

$ C:/Users/admin/AppData/Local/Programs/Python/Python37/python.exe setup.py build

Extraits des logs affichés dans la console :

Processing numpy/random\_bounded_integers.pxd.in
[...]
Cythonizing sources
[...]
  FOUND:
    libraries = ['libopenblas', 'libopenblas']
    library_dirs = ['C:\\Workdir\\numpy\\numpy\\core\\openblas\\lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
[...]
running build
[...]
customize MSVCCompiler
[...]
compiling C sources
[...]
Running from numpy source directory.
C:\Users\admin\AppData\Local\Programs\Python\Python37\lib\distutils\dist.py:274: UserWarning: Unknown distribution option: 'define_macros'
  warnings.warn(msg)

Deux choses à noter:

  • Les 4 lignes suivant FOUND indiquent que des composantes d’OpenBLAS ont été reconnues. Ce type de message devrait survenir à plusieurs reprises dans les logs.
  • Le traitement s’achève sur un warning concernant l’option « define_macros », avertissement que l’on ignore.

Etape 7: wheel

Création de la wheel d’installation de Numpy:

$ C:/Users/admin/AppData/Local/Programs/Python/Python37/python.exe setup.py bdist_wheel

Extraits des logs affichés dans la console :

numpy/random\_bounded_integers.pxd.in has not changed
[...]
Cythonizing sources
[...]
  FOUND:
    libraries = ['libopenblas', 'libopenblas']
    library_dirs = ['C:\\Workdir\\numpy\\numpy\\core\\openblas\\lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
[...]
running bdist_wheel
running build
[...]
customize MSVCCompiler
[...]
compiling C sources
[...]
installing to build\bdist.win-amd64\wheel
running install
[...]
Running from numpy source directory.
C:\Users\admin\AppData\Local\Programs\Python\Python37\lib\distutils\dist.py:274: UserWarning: Unknown distribution option: 'define_macros'
  warnings.warn(msg)
C:\Users\admin\AppData\Local\Programs\Python\Python37\lib\site-packages\wheel\pep425tags.py:83: RuntimeWarning: Config variable 'Py_DEBUG' is unset, Python ABI tag may be incorrect
  warn=(impl == 'cp')):
C:\Users\admin\AppData\Local\Programs\Python\Python37\lib\site-packages\wheel\pep425tags.py:88: RuntimeWarning: Config variable 'WITH_PYMALLOC' is unset, Python ABI tag may be incorrect
  sys.version_info < (3, 8))) \

Les traitements s’achèvent sur trois avertissements que l’on ignore. La Wheel d’installation de Numpy a été créée dans numpy/dist:

C:/Workdir 
├── OpenBLAS
└── numpy
    ├── …
    ├── build
    ├── dist
    │   └── numpy-1.19.0.dev0+d4b495c-cp37-cp37m-win_amd64.whl
    └── numpy.egg-info

Le fichier pèse 14 182 ko.

Etape 8: installation

Installation de la wheel Numpy.

Dans le terminal x64 Native Tools Command Prompt for VS2019:

> python -m pip install C:\Workdir\numpy\dist\numpy-1.19.0.dev0+d4b495c-cp37-cp37m-win_amd64.whl

Remarque:

Pour désinstaller la wheel:

> python -m pip uninstall numpy

Vérification

>>> import numpy
>>> numpy.__config__.show()
blas_mkl_info:
  NOT AVAILABLE
blis_info:
  NOT AVAILABLE
openblas_info:
    libraries = ['libopenblas', 'libopenblas']
    library_dirs = ['C:\\Workdir\\numpy\\numpy\\core\\libopenblas\\lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
blas_opt_info:
    libraries = ['libopenblas', 'libopenblas']
    library_dirs = ['C:\\Workdir\\numpy\\numpy\\core\\libopenblas\\lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
lapack_mkl_info:
  NOT AVAILABLE
openblas_lapack_info:
    libraries = ['libopenblas', 'libopenblas']
    library_dirs = ['C:\\Workdir\\numpy\\numpy\\core\\libopenblas\\lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
lapack_opt_info:
    libraries = ['libopenblas', 'libopenblas']
    library_dirs = ['C:\\Workdir\\numpy\\numpy\\core\\libopenblas\\lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
>>>

Les librairies relatives à BLAS et LAPACK sont bien définies.

Résultat

Une fois cette version accélérée de Numpy installée le jeu tourne de manière beaucoup plus fluide. De plus notre wheel est portable, elle fonctionne parfaitement sur une autre machine Windows 64 bits (Windows 7 Ed Familiale 64 bits).

Wheel Numpy 1.19 compilée avec OpenBLAS pour Windows 64: Télécharger