跨编译Armadillo线性代数库

7

我很喜欢使用Armadillo线性代数库。当将Octave .m文件移植到C++时,特别是在使用eigen方法时,它变得非常好用。

然而,当我需要将程序从我的本地vanilla G++转移到ARM处理器上时,我遇到了问题。因为我花了几个小时来摸索它,所以我想分享一下,以便其他人可以避免一些挫败感。

如果还有其他人能够添加任何东西,我会非常感激。这是我用来解决问题的过程,肯定不是唯一或最好的方法。

2个回答

13

首先,我使用Code Sourcery作为我的交叉编译器。我知道还有其他的编译器,但我还没有开始为其他编译器重新构建,不过这个方法应该适用于任何编译器。

信息:

Armadillo库需要LAPACK和BLAS,但是Code Sourcery没有Fortran编译器。因此我使用了f2c转换后的版本。

1. 获取源文件:

首先获取源文件。

2. 交叉编译CLAPACK

一旦我们获得了源代码,下一步就是将它们交叉编译到我们的目标硬件上。在我的情况下,是在ARM7上使用CodeSourcery。这里非常建议阅读README文件,您可以通过花些时间仔细阅读来完成所有操作。

  1. 首先要做的是更改make.inc文件,让它指向我们的交叉编译器,而不是普通的GCC。通常你可以使用$export命令,但我发现修改makefiles更容易跟踪。

    从以下位置编辑clapack-3.2.1-CMAKE/make.inc:

    CC = GCC
    LOADER = GCC
    

    致:

    CC = [CROSS-COMPILER-GCC location]
    LOADER = [CROSS-COMPILER-GCC location]
    

    请编辑clapack-3.2.1-CMAKE/F2CLIBS/libf2c/Makefile:

    ld -r -x -o $*.xxx $*.o
    

    至:

    [CROSS-COMPILER-LD location] -r -x -o $*.xxx $*.o
    
  2. 编译f2c库:

    $make f2clib
    

    当我构建f2c库时,最后出现了一个错误:

    ./a.out > arith.h
    /bin/sh: ./a.out: cannot execute binary file
    make[1]: *** [arith.h] Error 126
    make[1]: Leaving directory `/home/matt/clapack-3.2.1-CMAKE/F2CLIBS/libf2c'
    make: *** [f2clib] Error 2
    

    这里实际上没有问题。当然它会有执行问题,因为它是经过交叉编译的!

  3. 编译BLAS:

  4. $make blaslib
    

    完成后,您会注意到有一个新的“blas_XXXXX.a”文件。这是您交叉编译的BLAS库。

  5. 编译LAPACK:

    make.inc将指向使用$make lapacklib,但这会导致它尝试更多交叉编译项目的执行。相反,切换到SRC目录并执行以下命令:

  6. $make
    

    这应该会生成您的新的“lapack_XXXXX.a”文件。既然我们拥有了F2C、LAPACK和BLAS,我建议将它们放在一个合适的位置,以便以后可以找到它们。在我的情况下,我把它们放在了Code Sourcery编译器 /CodeSourcery/arm-none-linux-gnueabi/usr/lib所在的位置。记得重新命名这些文件:

    $cp libf2c.a [CROSS-COMPILE LIBRARY PATH]/libf2c.a
    $cp blas_XXXXX.a [CROSS-COMPILE LIBRARY PATH]/libblas.a
    $cp lapack_XXXXX.a [CROSS-COMPILE LIBRARY PATH]/liblapack.a
    

    记住它们必须具备“lib”才能在以后被识别。 接着将其存储在交叉编译库位置中。我已经设置了我的工具链使其更容易与普通的gcc / g ++分离。

    3. 交叉编译ARMADILLO

    首先阅读README,这总是最好的起点。
    接着运行:

        $cmake .
    

    这将准备好所有内容并生成cmake创建我们的共享armadillo库所需的一切。 我在这里的操作方式并不是我认为你应该使用的方式,但由于我对通常的makefile不是很熟练,所以我认为展示我所做的工作有助于跨编译。

    我修改了以下生成的CMakeCache.txt行:

        //CXX compiler.
        CMAKE_CXX_COMPILER:FILEPATH=[CROSS-COMPILER-G++ location]
    

    我知道在CMakeCache.txt文件中有一个地方可以指定我们的BLAS和LAPACK所在的路径,但我很难弄清楚。而不是在这个问题上费神,我只是修改了"CMakeFiles/armadillo.dir/link.txt"并手动添加了"-L [交叉编译的BLAS/LAPACK目录]". 熟悉如何做这件事的人可以在评论中说明。
    接下来,由于我们希望在以后编译程序时手动链接BLAS和LAPACK库(就像README中说的那样),请修改"include/armadillo_bits/config.hpp"并确保注释掉定义使用arma包装器的行:

        //  #define ARMA_USE_WRAPPER
    

    唯一要做的事情就是$cd回到犰狳目录的根目录,然后

        $make
    

    完成编译后,您就可以在程序中使用Armadillo。

    4. 在您的程序中使用ARMADILLO

    要在程序中使用Armadillo,请添加以下头文件:#include <armadillo>和命名空间using namespace arma;。现在,您应该能够使用所有需要的vecmat。 通常情况下,在使用arma时,您只需要在编译时链接libarmadillo.so库即可,但如前所述,我们需要直接链接BLAS和LAPACK。下面是GCC C++编译器的语法:

        [CROSS-COMPILER-G++] -I [CROSS-COMPILED ARMADILLO DIRECTORY]/include ...
    

    和我的链接器:

        [CROSS-COMPILER-G++] -L [CROSS-COMPILED LIBRARY] -o ... -llapack -lf2c -lblas
    

    还要注意链接库的顺序很重要!lapack必须排在第一位,然后是f2c和blas。

    确保跨编译的armadillo目录包含在编译中,并按上述方式正确设置链接即可。

    再次强调,提供更多信息会更好,请随意添加更多评论。你的解决方法与我的不同之处,我做错了什么,可以做些什么来改进。

    谢谢。


2
在第三点中,你是不是想说 cmake . 而不是 $make . (末尾有一个点)? - mtall
你说得对,已经编辑将 $make . 改为 $cmake .。谢谢! - Matt
@Matt 在使用 CMake 进行交叉编译的正确方法是使用 工具链文件。如果您正在使用 Buildroot,则已经设置好了工具链文件。 - yegorich
作为另一个数据点,我已经成功地使用Fortran交叉编译器构建了libopenblas,并将Armadillo链接到它上面,然后使用https://github.com/taka-no-me/android-cmake中的工具链文件进行了构建。在我看来,这种方法要稍微简单一些,希望有一天我能够整理并发布说明。但至少使用工具链文件应该比手动更改编译器路径要容易一些。 - Ibrahim

1
我的具体设置是在OSX上(使用Eclipse IDE)交叉编译到Beaglebone Black。但是,这些说明应该适用于类似的设置。

可选:

为了编译,我使用了Mac OS X ARM GNU Linux G++ Lite 2013.11-33 Toolchain。具体来说,是ARM GNU/Linux G++ Lite 2013.11-33 Advanced Binary。

1. 下载:

正如Matt所述,GCC交叉编译器不支持Fortran,因此如果您想编译LAPACK和BLAS,请使用找到的修改版本here。我正在使用clapack-3.2.1-CMAKE.tgz

2. 制作交叉编译cmake文件:

您可以使用工具链构建器或者自己写一个。我写了一个。

示例:

# http://www.cmake.org/Wiki/CMake_Cross_Compiling#The_toolchain_file

# REQUIRED
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 1)
SET(CMAKE_SYSTEM_PROCESSOR arm)

# Added for the beaglebone
SET(FLOAT_ABI_SUFFIX "")

# specify the cross compiler
SET(CMAKE_C_COMPILER   /usr/local/arm-2013.11/bin/arm-none-linux-gnueabi-gcc)
SET(CMAKE_CXX_COMPILER /usr/local/arm-2013.11/bin/arm-none-linux-gnueabi-c++)

# where is the target environment 
SET(CMAKE_FIND_ROOT_PATH  /usr/local/arm-2013.11)

# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

注意:这是我第一次制作cmake文件,不能保证其正确性。
你需要将/usr/local/arm-2013.11/bin/arm-none-linux-gnueabi-gcc替换为你选择的编译器的路径,以及/usr/local/arm-2013.11/bin/arm-none-linux-gnueabi-c++和/usr/local/arm-2013.11。
我选择将此cmake文件保存为beaglebone.cmake。
3. 编译:
解压clapack-3.2.1-CMAKE.tgz并cd到该目录。
编译:cmake -DCMAKE_TOOLCHAIN_FILE=~/Dropbox/workspaces/beaglebone/beaglebone.cmake 其中~/Dropbox/workspaces/beaglebone/beaglebone.cmake是你的cmake文件的路径。
make 由于某种原因,我得到了:
ds-mac-pro:clapack-3.2.1-CMAKE bunny$ make
Scanning dependencies of target arithchk
[  0%] Building C object F2CLIBS/libf2c/CMakeFiles/arithchk.dir/arithchk.c.o
Linking C executable arithchk
[  0%] Built target arithchk
[  0%] Generating arith.h
/bin/sh: arithchk: command not found
make[2]: *** [F2CLIBS/libf2c/arith.h] Error 127
make[1]: *** [F2CLIBS/libf2c/CMakeFiles/f2c.dir/all] Error 2
make: *** [all] Error 2

奇怪的是,再次运行make可以正常编译:

ds-mac-pro:clapack-3.2.1-CMAKE bunny$ make
[  0%] Built target arithchk
Scanning dependencies of target f2c
[  0%] Building C object F2CLIBS/libf2c/CMakeFiles/f2c.dir/f77vers.c.o
[  0%] Building C object F2CLIBS/libf2c/CMakeFiles/f2c.dir/i77vers.c.o
[  0%] Building C object F2CLIBS/libf2c/CMakeFiles/f2c.dir/main.c.o
[  0%] Building C object F2CLIBS/libf2c/CMakeFiles/f2c.dir/s_rnge.c.o

...

[100%] Building C object TESTING/EIG/CMakeFiles/xeigtstz.dir/xerbla.c.o
[100%] Building C object TESTING/EIG/CMakeFiles/xeigtstz.dir/xlaenv.c.o
[100%] Building C object TESTING/EIG/CMakeFiles/xeigtstz.dir/chkxer.c.o
[100%] Building C object TESTING/EIG/CMakeFiles/xeigtstz.dir/__/__/INSTALL/dsecnd.c.o
Linking C executable xeigtstz
[100%] Built target xeigtstz

4. 复制(安装):

find . | grep \.a$

返回
./BLAS/SRC/libblas.a
./F2CLIBS/libf2c/libf2c.a
./SRC/liblapack.a
./TESTING/MATGEN/libtmglib.a

将libblas.a、libf2c.a和liblapack.a复制到您喜欢的库文件夹中。我选择了/usr/local/armadillo-4.300.3/lib

所需:

1. 复制包含文件:

Armadillo不需要编译。我没有运行任何基准测试来验证,但似乎使用LAPACK和BLAS与未编译的Armadillo副本通过在代码中使用#define可以正常工作。稍后有代码示例。

提取armadillo-4.300.3.tar.gz

(可选) cp armadillo-4.300.3/include/ /usr/local/armadillo-4.300.3/include
当然,将/usr/local/armadillo-4.300.3/include替换为您选择的路径

2. 设置GCC以使用Armadillo:

我正在使用带有C/C++交叉编译器支持插件(帮助菜单 -> 安装新软件...)的Eclipse,但是说明很容易转换为cli或其他IDE。

在项目属性窗口中:C/C++构建 -> 设置

交叉G++编译器 -> 包含 -> 包含路径(-I)

点击“+”添加一个包含文件。我的包含文件路径是:/usr/local/armadillo-4.300.3/include

3. 可选 - 设置GCC使用已编译的库:

交叉G++链接器 -> 库 ->

库(-l)

lapack
f2c
blas

库搜索路径(-L)

点击“+”添加一个路径。我的路径是:/usr/local/armadillo-4.300.3/lib

3. 代码示例:

在cpp文件中尝试:

#include <armadillo>
using namespace arma;

mat A = randu<mat>(4,5);
mat B = randu<mat>(4,5);

std::cout << A*B.t() << std::endl;

成功! :D

一些功能 Armadillo 并不直接支持,需要使用编译的库才能实现。您可以通过运行类似以下简单测试来检查库是否已被编译并正常工作:

#define ARMA_DONT_USE_WRAPPER
#define ARMA_USE_LAPACK
#define ARMA_USE_BLAS
#include <armadillo>
using namespace arma;

mat    A = randu<mat>(5,5);
double x = det(A);

std::cout << x << std::endl;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接