TVM编译遇到的那些坑

2022-04-14  本文已影响0人  耗子JF

由于工作需要,初次接触TVM,工作用的电脑是windows系统的,要从头搭建整个编译环境,嗯,真的硬核从头,从安装ubuntu子系统开始,中间遇坑无数,死磕各种报错很久,终于经过苦难的半个月折磨顺利按官方文档完成了tvm库到android_rpc客户端的编译工作!撒花完结!

目前找到的比较友好的大佬文档:
用TVM在Android上部署模型
tvm系列-Android TVM RPC
TVM系列 - 终于在手机成功部署Auto-TVM

源码是在2022年4月初下载的最新代码,高于V0.8版本,懒人包:
tvm/build目录覆盖:TVM share-lib build 目录完全内容下载
tvm/jvm目录覆盖:TVM jvmpkg 编译完成包下载
tvm/apps/android_rpc/app/src/main/libs目录覆盖:tvm android_rpc so完整库
tvm/apps/android_rpc/app/src/libs目录覆盖:tvm4j-core-0.0.1-SNAPSHOT.jar

宝宝心里苦啊,不知道是不是大佬们玩linux环境比较溜,基本上编译都是一笔带过环境问题,主角光环加身既视感,嗯,就像看小说里的那句“很多年过去了,男主神功大成”一毛一样!

废话不多说,我们开始爬坑之路:

安装windows10自带unbuntu子系统:
公司破电脑权限问题只能绕过微软应用商店,用shell装ubuntu16.4的系统,正常用应用商店应该是装18.4(推荐)或者20.4系统,最好18.4的,因为网上很多环境下载这个版本支持比较全,20.4实在没心情去折腾了,有兴趣的自己去搞一下试试,应该也差不多……吧?

新系统先跑一下下面这个命令,官方给的环境,16.4默认好些个版本都太低,cmake,gcc python3都低于官方要求版本,需要单独升级一下

sudo apt-get update
sudo apt-get install -y python3 python3-dev python3-setuptools gcc libtinfo-dev zlib1g-dev build-essential cmake libedit-dev libxml2-dev

step 1:TVM4J Installation Guide

官方文档(下载,环境配置,编译)
基本上先按文档走一遍,那么大部分环境就算OK了:

1.tvm源码下载地址

git clone --recursive https://github.com/apache/tvm tvm

For windows users who use github tools, you can open the git shell, and type the following command.

git submodule init
git submodule update

上面是官方原文建议,但是个人建议还是老老实实用ubuntu下敲命令下载吧,因为这里也会有点点坑,看人品,别问,问就是遇到过,对于像我这种触及到知识盲区的新入坑人员来说还是太深了:源码内 tvm/3rdparty/目录下会有子工程,直接git clone tvm这部分代码是不会下载的,要么按上面submodule操作自动下载,要么就手动点进github子模块链接下载,推荐手动点进去下载,因为这里也有坑

各个子系统git地址:
tvm/3rdparty/cutlass: https://github.com/NVIDIA/cutlass.git
tvm/3rdparty/dlpack: https://github.com/dmlc/dlpack.git
tvm/3rdparty/dmlc-core: https://github.com/dmlc/dmlc-core.git
tvm/3rdparty/libbacktrace: https://github.com/tlc-pack/libbacktrace.git
tvm/3rdparty/rang: https://github.com/agauniyal/rang.git
tvm/3rdparty/vta-hw:https://github.com/apache/tvm-vta.git

好了,源码下载完了,按官方文档更新下编译环境:

Requirements

android_rpc工程依赖tvm4j的jar包,因此首先需要编译生成tvm4j包,编译的环境要求为:

编译开始:

mkdir build
cp cmake/config.cmake build

打开config.cmake找到下面这些参数,修改为打开

set(USE_GRAPH_EXECUTOR ON) 
set(USE_PROFILER ON)

有需要的话可以开启debug:

set(USE_RELAY_DEBUG ON)

//命令行需要执行一下这一句命令

export TVM_LOG_DEBUG="ir/transform.cc=1;relay/ir/transform.cc=1"

打开LLVM功能

set(USE_LLVM /path/to/your/llvm/bin/llvm-config) 
#我的实际路径是:set(USE_LLVM /usr/lib/llvm-6.0/bin/llvm-config)

然后切换到build目录进行编译:

cd build
cmake ..
make -j4

顺利的话,我们应该能喝一喝茶或者咖啡等待编译完成拿到我们需要的so库:
TVM share-lib build 目录完全内容下载
实在搞不出来又急着用,可以直接下这个包,整个build完整编译文件都在里面,包括要用到的libtvm.so和libtvm_runtime.so文件,解压覆盖就可以用

但是!有问题很正常,容易采坑的地方:

如果遇到问题,首先先对照一下编译环境是不是OK,回过头去耐心对照一下我上面加粗标注的那些环境版本,起码上面那些版本我趟过雷了,编译是没问题的,这个编译很磨人的地方就在这里,如果你环境对不上可能就会报各种莫名其妙的错误:


In file included from /mnt/d/ubuntu/tvm/include/tvm/runtime/object.h:26:0,
                 from /mnt/d/ubuntu/tvm/src/support/libinfo.cc:19:
/mnt/d/ubuntu/tvm/include/tvm/runtime/c_runtime_api.h:89:16: error: redeclaration of ‘kDLHexagon’
   kDLHexagon = 14,
                ^~
In file included from /mnt/d/ubuntu/tvm/include/tvm/runtime/c_runtime_api.h:72:0,
                 from /mnt/d/ubuntu/tvm/include/tvm/runtime/object.h:26,
                 from /mnt/d/ubuntu/tvm/src/support/libinfo.cc:19:
/mnt/d/ubuntu/tvm/3rdparty/dlpack/include/dlpack/dlpack.h:64:3: note: previous declaration ‘DLDeviceType kDLHexagon’
   kDLHexagon = 15,
   ^~~~~~~~~~
In file included from /mnt/d/ubuntu/tvm/include/tvm/runtime/object.h:26:0,
                 from /mnt/d/ubuntu/tvm/src/support/libinfo.cc:19:
/mnt/d/ubuntu/tvm/include/tvm/runtime/c_runtime_api.h:90:15: error: redeclaration of ‘kDLWebGPU’
   kDLWebGPU = 15
               ^~
In file included from /mnt/d/ubuntu/tvm/include/tvm/runtime/c_runtime_api.h:72:0,
                 from /mnt/d/ubuntu/tvm/include/tvm/runtime/object.h:26,
                 from /mnt/d/ubuntu/tvm/src/support/libinfo.cc:19:
/mnt/d/ubuntu/tvm/3rdparty/dlpack/include/dlpack/dlpack.h:65:3: note: previous declaration ‘DLDeviceType kDLWebGPU’
   kDLWebGPU = 16
   ^~~~~~~~~
^CCMakeFiles/tvm_runtime_objs.dir/build.make:89: recipe for target 'CMakeFiles/tvm_runtime_objs.dir/src/runtime/c_runtime_api.cc.o' failed

同时这个问题还会导致android so 的jni编译脚本无法通过,报错与kDLWebGPU,kDLHexagon重复定义有关,这个就是子模块自动下载可能带来的问题:git submodule update拉取的代码是⽗项⽬中记录的那个submodule版本,但不⼀定是submoudle远程仓库⾥最新的版本,实际上最新的版本dlpack.h相关重复定义字段已经删除了,泪流满面,因为大佬的文章都是一笔带过,所以坚信源码没问题没问题没问题!浪费了大量时间有木有有木有!!

踩过坑以后,我们对于TVM相关share-lib so库的编译工作就算完成了,我们可以继续按流程往下走,编译jar库,在上面编译通过的基础上,环境问题的坑基本我们都补好了,就不太可能出啥问题了,直接就能顺利编译过了:

First please refer to Installation Guide and build runtime shared library from the C++ codes (libtvm_runtime.so for Linux and libtvm_runtime.dylib for OSX).
Then you can compile tvm4j by

#source-shell tvm目录运行shell脚本
make jvmpkg

(Optional) run unit test by 这个其实不跑也没关系……吧?

#source-shell tvm目录运行shell脚本
sh tests/scripts/task_java_unittest.sh

After it is compiled and packaged, you can install tvm4j in your local maven repository,

#source-shell tvm目录运行shell脚本
make jvminstall

顺利完成tvm4j jar库的编译,我们可以在tvm/jvm/core/target里找到编译成功的jar库
tvm4j-core-0.0.1-SNAPSHOT.jar 这个jar包我们在后面的android 编译的时候需要用到,附上编译好的jvm目录,懒人可以直接覆盖tvm/jvm文件夹跳过编译过程:
TVM jvmpkg 编译完成包下载
文件包括所有编译出的文件,包括tvm4j-core-0.0.1-SNAPSHOT.jar,以及后续要用的编译文件

Step2. Android TVM RPC

You will need JDK(我的版本是openJDK8,尽量安装这个版本,因为我在论坛查找各种问题的issue的时候看到有人因为jdk版本问题无法正确编译的), Android NDK(务必使用NDK 16 rb版本我测试过10e, 12b, 23b统统卡死在ndk-build编译JNI那一步,no zuo no die!!!) and an Android device to use this.

Build APK

We use Gradle (请使用相对高一些的版本,推荐gradle 6 或者 7 这两个都试过,没问题,有人在官方论坛说的,gradle版本低了无法正常编译,不信可以试试嗷)to build. Please follow the installation instruction for your operating system.

Before you build the Android application, please refer to TVM4J Installation Guide and install tvm4j-core to your local maven repository. You can find tvm4j dependency declare in app/build.gradle. Modify it if it is necessary.

我们前面编辑出来jar包了,所以用简单点的方法,直接换成本地libs的jar引用方式吧,maven库的方式是在有点麻烦,附上懒人包(其中jar包也可以在jvm那个包里的jvm/core/target下面找到),使用so懒人包的时候,记得把buildJni注释掉:

tasks.withType(JavaCompile) {
    //像这样,注释掉,很重要!!!
    //compileTask -> compileTask.dependsOn buildJni
}

这样就不用走buildJni去重复编译so库了,否则会直接就删掉我们懒人包的so重新编译了:
tvm/apps/android_rpc/app/src/main/libs目录覆盖:tvm android_rpc so完整库
tvm/apps/android_rpc/app/src/libs目录覆盖:tvm4j-core-0.0.1-SNAPSHOT.jar

dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    implementation 'com.android.support:appcompat-v7:26.0.1'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:design:26.0.1'
    //划重点,这里替换成我们编译出来的jar包放到工程目录src/libs里
    implementation files('src/libs/tvm4j-core-0.0.1-SNAPSHOT.jar')
    //implementation 'org.apache.tvm:tvm4j-core:0.0.1-SNAPSHOT'
    testImplementation 'junit:junit:4.12'
}

Now use Gradle to compile JNI, resolve Java dependencies and build the Android application together with tvm4j. Run following script to generate the apk file.
如果一切正常,我们就能得到编译出来的的apk用于连接服务器测试了,如果要加入opencl库的APK请参考官方文档进行设置打包,好了,暂时更新到这里,后面继续测试连接服务器运算以及opencl库启用踩坑!

坑来了

NDK在这里,版本很重要我测试过(android-ndk-r10e,android-ndk-r12b,android-ndk-r16b,android-ndk-r23b),太高的版本(android-ndk-r23b)会报错,各种错,一个是找不到tools-chain里面的编译器工具错误,因为新版已经把这些工具包去掉了,只有llvm,renderscript文件夹,后续用到的Cross Compile也会需要,高版本是没法正常使用的:

cd /opt/android-ndk/build/tools/
//高版本NDK这条命令没法正常执行
./make-standalone-toolchain.sh --platform=android-24 --use-llvm --arch=arm64 --install-dir=/opt/android-toolchain-arm64

更新了编译机制,就算你从低版本复制对应的chain包进去,依然会有其他错误,错误是没有具体提醒的,具体姿势是这样的:

make: Entering directory '/mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni'
[armeabi-v7a] Compile++ arm  : tvm4j_runtime_packed <= org_apache_tvm_native_c_api.cc
clang: error: no input files

//gradle执行脚本的话会报这个错误
Process 'command 'sh'' finished with non-zero exit value 2

NDK版本太低的版本(10e,12b)会报错误,而且ubuntu16.4默认的clang是3.8的版本,最好手动更新下默认clang版本,同理还有gcc 和 g++版本一定要检查(我用的是gcc g++ 7.5),否者会遇到下面错误,这两个错误主要能在jni 对应的Application.mk配置里找到对应的命令行,但本质上就是编译版本对不上的问题:

//错误1( gcc最好升级到7)
arm-linux-androideabi-g++: error: unrecognized command line option '-std=c++14'
//错误2
cc1plus: error: argument to '-O' should be a non-negative integer, 'g', 's' or 'fast'

然后正确的NDK版本,如果遇到之前编译so的时候没有处理tvm/3rdparty下面dlpack库同步问题,会有这样的的错误:

In file included from /mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni/org_apache_tvm_native_c_api.cc:25:
In file included from /mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni/tvm_runtime.h:36:
In file included from /mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni/../../../../../../include/../src/runtime/c_runtime_api.cc:25:
In file included from /mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni/../../../../../../include/tvm/runtime/c_backend_api.h:31:
/mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni/../../../../../../include/tvm/runtime/c_runtime_api.h:90:3: error:
      redefinition of enumerator 'kDLWebGPU'
  kDLWebGPU = 15
  ^
/mnt/d/ubuntu/tvm/apps/android_rpc/app/src/main/jni/../../../../../../3rdparty/dlpack/include/dlpack/dlpack.h:65:3: note:
      previous definition is here
  kDLWebGPU = 16

另外,我在参考有大佬文档的时候,说自己运行gradle clean build 进行打包的时候会报错,但是我后面把各种坑踩完以后,倒是没遇到这个问题,这里提供一种担心gradle版本过高(反正我用最新的gradle 7没遇到),可能引起错误的规避方法,使用gradlew脚本进行指定版本编译,操作如下:

//先切换到android_rpc根目录,执行下面命令,会自动生成gradlew脚本
//且会自动下载对应的gradle插件包,避免版本不匹配的问题
//我这里设置的4.4版本,工程里3.1版本下不下来
gradle wrapper --gradle-version 4.4
//执行完毕以后就生成好gradlew脚本文件了,执行工程编译
./gradlew clean build
上一篇 下一篇

猜你喜欢

热点阅读