【Python笔记】如何源码编译依赖LAPACK和ATLAS库的NumPy包

上篇笔记介绍了不依赖lapack和atlas库的NumPy包源码编译/安装方法,但“纯净版”的NumPy会损失性能,故本篇笔记说明如何源码编译安装依赖lapack和atlas库的NumPy包。

特别提醒:由于atlas编译时会根据当前机器配置生成编译参数,所以如果想把在一台机器上编译好的NumPy包打包拷贝到另一台配置不同的机器上,可能会在使用时(例如调用numpy.dot()计算两个向量的点积)出现导致Python解释器崩溃的core dump,出错提示类似于“Illegal instruction (core dumped)”。这意味着两台机器的CPU指令集存在不兼容。解决此类core dump的根本方法是在新机器上按照本文的步骤重新编译atlas库和NumPy包。

1. GCC版本要求
使用较新版本的GCC工具集(尽量不低于v4.7)且集成有gfortran编译器。
备注1:这里大写的"GCC"是指GNU Compiler Collection,它除包含C语言编译器gcc外,还包含很多其它语言的编译器(如g++/gfortran等)
备注2:3.x版的的C语言编译器gcc会由于某些头文件缺失导致编译atlas库报错
备注3:若GCC工具集中没有gfortran编译器,则编译lapack库时会遇到一些莫名其妙的错误(因为lapack是用fortran编写的),好在GCC4.7及以上版本中已经集成了gfortran编译器
在GCC版本符合要求的前提下,临时将其加入环境变量PATH并设置动态库查找路径:

$ export PATH=/home/slvher/tools/gcc48/bin/:$PATH
$ export LD_LIBRARY_PATH=/home/slvher/tools/gcc48/lib64:/home/slvher/tools/gcc48/lib
备注4:在当前shell会话中临时设置LD_LIBRARY_PATH可以保证编译过程中正确搜索到GCC库,但最好不要设置到.bash_profile中,因为那样会影响其它程序的查找路径,可能会踩到坑。
备注5:这里提到的GCC的版本要求及环境变量设置如果没有出差错,那么下面的编译会比较顺利,否则会遇到各种编译/链接问题,后续我会用一篇笔记来记录这些踩坑的过程及遇到这些诡异问题时的分析思路,这里不赘述。 2. 编译LAPACK和ATLAS库
lapack是用fortran开发的经过特别优化的线性代数计算库;atlas也是一个优化过的线性代数计算库,它提供了BLAS库的全部API(包括C接口和Fortran接口),还实现了lapack库中的部分函数,atlas在编译过程中会根据机器的配置参数来调整科学计算函数的参数,以便在该机器上达到更好的计算性能。
初看起来,需要分别编译lapack和atlas两个库,所幸的是,atlas库支持编译时自动编译lapack库,因此,只需正确完成atlas库的编译配置,编译atlas库就可以了。
下面是编译atlas/lapack库的主要步骤。
1)
分别从官网下载lapack源码包和atlas源码包,我下载的是目前的最新版lapack-3.5.0.tgz及atlas3.10.2.tar.bz2
2) 解压atlas源码压缩包:tar -jxvf atlas3.10.2.tar.bz2
3) cd ATLAS && mkdir BLDdir && cd BLDdir
4) 执行configure命令以配置编译参数
$ ../configure --shared -b 64 --prefix=/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs --with-netlib-lapack-tarfile=/home/slvher/tools/scikit-learn-virtualenv/dep-libs/lapack-3.5.0.tgz 
其中,--shared表明要编译atlas共享库(configure会自动在编译命令中插入"-fPIC"参数,无需在这里显式指定);--prefix指定编译结果的安装路径;--with-netlib-lapack-tarfile表明编译atlas库时会用相同的编译器及编译/链接参数自动编译lapack库,这里指定lapack源码包的路径后,configure运行后会自动解压lapack源码并将其拷贝至BLDdir/src/lapack/reference/这个目录下。
5) configure运行完后,BLDdir目录下生成了Make.inc文件,该文件中设置了众多编译参数(如查找路径、编译产出路径、编译器、传给编译器的参数,等等),BLDdir子目录下很多模块的Makefile都会include这个Make.inc,包括源码独立的lapack包,可见,这个Make.inc文件可以达到统一编译环境的目的。
6) make build
7) make check
8) make ptcheck
9) make install
如果上述一系列命令均执行成功,那么编译完成的*.a和*.so库会安装到--prefix参数指定的路径下,这些库的头文件也会被拷贝到安装路径下的include目录。
至此,ATLAS和LAPACK库均完成编译,其中LAPACK库是.a静态库,ATLAS库是.so动态库。事实上,ATLAS的动态库中已经包含了LAPACK静态库的所有符号和代码。
下面可以开始编译依赖LAPACK和ATLAS库的NumPy包了。

3. 编译优化版NumPy包
前提:官网下载NumPy源码包并解压,这里以目前最新版numpy-1.9.2.tar.gz为例进行说明。
1) cd至解压目录numpy-1.9.2
2) cp site.cfg.example site.cfg
3) 在site.cfg中配置atlas项,其中include_dirs和library_dirs是atlas库安装路径下的include和lib目录

[atlas]
atlas_libs   = lapack,f77blas,cblas,atlas
library_dirs = /home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/lib
include_dirs = /home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/include
4) python setup.py config
5) python setup.py build --fcompiler=gnu95 ## 指定Fortran编译器为GCC4.8工具集中的gfortran
6) python setup.py install
正常情况下,build成功后,install会把编译产出拷贝到当前python解释器安装路径下的lib/python2.7/site-packages目录中。
此时,可以通过下面的例子来查看NumPy包的配置情况:
>>> import numpy as np
>>> np.__config__.show()
atlas_3_10_blas_threads_info:
    libraries = ['lapack','f77blas','cblas','atlas']
    library_dirs = ['/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/lib']
    define_macros = [('HAVE_CBLAS',None),('ATLAS_INFO','"\\"3.10.2\\""')]
    language = c
    include_dirs = ['/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/include']
lapack_opt_info:
    libraries = ['tatlas','lapack','atlas']
    library_dirs = ['/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/lib']
    define_macros = [('ATLAS_INFO','"\\"3.10.2\\""')]
    language = f77
    include_dirs = ['/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/include']
blas_opt_info:
    libraries = ['lapack','"\\"3.10.2\\""')]
    language = c
    include_dirs = ['/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/include']
openblas_info:
  NOT AVAILABLE
openblas_lapack_info:
  NOT AVAILABLE
atlas_3_10_threads_info:
    libraries = ['tatlas','"\\"3.10.2\\""')]
    language = f77
    include_dirs = ['/home/slvher/tools/scikit-learn-virtualenv/dep-libs/sklearn-libs/include']
lapack_mkl_info:
  NOT AVAILABLE
blas_mkl_info:
  NOT AVAILABLE
mkl_info:
  NOT AVAILABLE
也可以用具体的例子来验证其功能是否正常:
>>> import numpy as np
>>> np.arange(15).reshape(3,5)
array([[ 0,1,2,3,4],[ 5,6,7,8,9],[10,11,12,13,14]])
>>> 
>>> a = np.arange(15).reshape(3,5)
>>> a
array([[ 0,14]])
>>> type(a)
<type 'numpy.ndarray'>
>>>
>>>
>>> from numpy.linalg import *
>>> b = np.array([[1.0,2.0],[3.0,4.0]])
>>> b
array([[ 1.,2.],[ 3.,4.]])
>>> b.transpose()
array([[ 1.,3.],[ 2.,4.]])
>>> inv(b)
array([[-2.,1. ],[ 1.5,-0.5]])
>>>
【参考资料】
1. Example: Installing ATLAS with full LAPACK on Linux/AMD64
2. SciPy.org - Building everything from source with gfortran on Ubuntu
3. SciPy.org - Building and installing NumPy

======================= EOF ======================

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结