由于工作需要,初次接觸TVM,工作用的電腦是windows系統(tǒng)的,要從頭搭建整個編譯環(huán)境,嗯,真的硬核從頭,從安裝ubuntu子系統(tǒng)開始,中間遇坑無數(shù),死磕各種報錯很久,終于經(jīng)過苦難的半個月折磨順利按官方文檔完成了tvm庫到android_rpc客戶端的編譯工作!撒花完結(jié)!
目前找到的比較友好的大佬文檔:
用TVM在Android上部署模型
tvm系列-Android TVM RPC
TVM系列 - 終于在手機(jī)成功部署Auto-TVM
源碼是在2022年4月初下載的最新代碼,高于V0.8版本,懶人包:
tvm/build目錄覆蓋:TVM share-lib build 目錄完全內(nèi)容下載
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環(huán)境比較溜,基本上編譯都是一筆帶過環(huán)境問題,主角光環(huán)加身既視感,嗯,就像看小說里的那句“很多年過去了,男主神功大成”一毛一樣!
廢話不多說,我們開始爬坑之路:
安裝windows10自帶unbuntu子系統(tǒng):
公司破電腦權(quán)限問題只能繞過微軟應(yīng)用商店,用shell裝ubuntu16.4的系統(tǒng),正常用應(yīng)用商店應(yīng)該是裝18.4(推薦)或者20.4系統(tǒng),最好18.4的,因為網(wǎng)上很多環(huán)境下載這個版本支持比較全,20.4實在沒心情去折騰了,有興趣的自己去搞一下試試,應(yīng)該也差不多……吧?
新系統(tǒng)先跑一下下面這個命令,官方給的環(huán)境,16.4默認(rèn)好些個版本都太低,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
官方文檔(下載,環(huán)境配置,編譯)
基本上先按文檔走一遍,那么大部分環(huán)境就算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下敲命令下載吧,因為這里也會有點點坑,看人品,別問,問就是遇到過,對于像我這種觸及到知識盲區(qū)的新入坑人員來說還是太深了:源碼內(nèi) tvm/3rdparty/目錄下會有子工程,直接git clone tvm這部分代碼是不會下載的,要么按上面submodule操作自動下載,要么就手動點進(jìn)github子模塊鏈接下載,推薦手動點進(jìn)去下載,因為這里也有坑
各個子系統(tǒng)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
好了,源碼下載完了,按官方文檔更新下編譯環(huán)境:
Requirements
- JDK 1.6+. Oracle JDK and OpenJDK are well tested.
我的版本是openJDK8 - Maven 3 for build.
我的版本是maven3.3.9 - LLVM (TVM4J need LLVM support. Please refer to build-the-shared-library for how to enable LLVM support.) LLVM一定要安裝,推薦LLVM6.0版本(ubuntu16.4 apt支持的最高版本,手動安裝太麻煩了),當(dāng)然LLVM7應(yīng)該也可以(這個是ubuntu18.4apt支持的最低版本,LLVM10也OK,官方推薦),如果config.mk不把LLVM打開容易遇到報錯不通過編譯,嗯,有一定概率,玄學(xué)玄學(xué)
android_rpc工程依賴tvm4j的jar包,因此首先需要編譯生成tvm4j包,編譯的環(huán)境要求為:
- A recent c++ compiler supporting C++ 14 (g++-5 or higher) :
我使用的是gcc g++ 7.5的版本(5.4的版本也能過),unbuntu16.4自帶版本太低,需要手動升級一下,ubuntu18.4默認(rèn)就是7的版本起步省心??! - CMake 3.10 or higher:
我使用的是3.22.3以及3.23.xx版本都能過,ubuntu16.4 apt庫的版本過低要手動升級 - We highly recommend to build with LLVM to enable all the features.
Python is also required. Avoid using Python 3.9.X+ which is not supported. 3.7.X+ and 3.8.X+ should be well supported however:
這個后續(xù)部署服務(wù)聯(lián)調(diào)會用,但是前面階段編譯TVM用不到還,盡量按要求來吧
編譯開始:
- First, check the cmake in your system. If you do not have cmake, you can obtain the latest version from official website
我的cmake版本3.22.3(3.23.xx也可以,家里電腦用的最新的沒問題) - First create a build directory, copy the
cmake/config.cmaketo the directory.
在TVM目錄創(chuàng)建一個build文件夾,復(fù)制cmake/config.cmake到build目錄
mkdir build
cp cmake/config.cmake build
打開config.cmake找到下面這些參數(shù),修改為打開
set(USE_GRAPH_EXECUTOR ON)
set(USE_PROFILER ON)
有需要的話可以開啟debug:
set(USE_RELAY_DEBUG ON)
//命令行需要執(zhí)行一下這一句命令
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目錄進(jìn)行編譯:
cd build
cmake ..
make -j4
順利的話,我們應(yīng)該能喝一喝茶或者咖啡等待編譯完成拿到我們需要的so庫:
TVM share-lib build 目錄完全內(nèi)容下載
實在搞不出來又急著用,可以直接下這個包,整個build完整編譯文件都在里面,包括要用到的libtvm.so和libtvm_runtime.so文件,解壓覆蓋就可以用
但是!有問題很正常,容易采坑的地方:
如果遇到問題,首先先對照一下編譯環(huán)境是不是OK,回過頭去耐心對照一下我上面加粗標(biāo)注的那些環(huán)境版本,起碼上面那些版本我趟過雷了,編譯是沒問題的,這個編譯很磨人的地方就在這里,如果你環(huán)境對不上可能就會報各種莫名其妙的錯誤:

1.一定要ubuntu子系統(tǒng)內(nèi)下載,不要windows下面下載,否則會莫名其妙遇到編碼問題,比如里面加一個^M啊這種啥的,編譯的時候會報錯:
unrecognized command line xxx
...3rdparty/libbacktrace/configure: Syntax error: newline unexpected (expecting ")")
等類似錯誤,因為編譯的時候會先運行各個子模塊的configure進(jìn)行驗證,大概率這個地方會報語法錯誤,但是如果普通文本工具打開看是看不到這些編碼的,如果你遇到查看configure內(nèi)容明明語法沒問題,但是死活告訴你語法有錯,編譯不過
解決方法:如果是這個錯誤,大概率就是下載的源碼被污染了,可以通過命令:vim -b xxxx 來查看是否有奇怪編碼,如果有亂碼,那就重新在unbuntu里下一遍吧,如果只是單個配置文件出問題了,可以用這個命令清除整理一下:sed -i 's/\r//g' xxxx.sh 然后再用vim -b查看,非法字符就消失了,上面的configure可以通過運行 sh configure --help 驗證是否正常,如果執(zhí)行了清除非法字符的命令,應(yīng)該是會正常運行,并提示configure文件內(nèi)可用參數(shù)的相關(guān)help信息的2.就是前面下載說明的地方說的,要手動下載tvm/3rdparty目錄下各個子模塊編碼放到對應(yīng)的文件夾里去,否則編譯的時候會報錯:各種找不到文件之類的,目錄指向tvm/3rdparty下面,那么就是這個問題了
3.編譯遇到tvm/3rdparty下某些重復(fù)定義錯誤,對就是kDLHexagon,kDLWebGPU這些重復(fù)定義,指向的是c_runtime_api.h 以及dlpack.h
編譯build so庫的時候,給你的錯誤提醒是這樣的:
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
同時這個問題還會導(dǎo)致android so 的jni編譯腳本無法通過,報錯與kDLWebGPU,kDLHexagon重復(fù)定義有關(guān),這個就是子模塊自動下載可能帶來的問題:git submodule update拉取的代碼是?項?中記錄的那個submodule版本,但不?定是submoudle遠(yuǎn)程倉庫?最新的版本,實際上最新的版本dlpack.h相關(guān)重復(fù)定義字段已經(jīng)刪除了,淚流滿面,因為大佬的文章都是一筆帶過,所以堅信源碼沒問題沒問題沒問題!浪費了大量時間有木有有木有??!
踩過坑以后,我們對于TVM相關(guān)share-lib so庫的編譯工作就算完成了,我們可以繼續(xù)按流程往下走,編譯jar庫,在上面編譯通過的基礎(chǔ)上,環(huán)境問題的坑基本我們都補(bǔ)好了,就不太可能出啥問題了,直接就能順利編譯過了:
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 這個其實不跑也沒關(guān)系……吧?
#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,以及后續(xù)要用的編譯文件
Step2. Android TVM RPC
You will need JDK(我的版本是openJDK8,盡量安裝這個版本,因為我在論壇查找各種問題的issue的時候看到有人因為jdk版本問題無法正確編譯的), Android NDK(務(wù)必使用NDK 16 rb版本我測試過10e, 12b, 23b統(tǒng)統(tǒng)卡死在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去重復(fù)編譯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用于連接服務(wù)器測試了,如果要加入opencl庫的APK請參考官方文檔進(jìn)行設(shè)置打包,好了,暫時更新到這里,后面繼續(xù)測試連接服務(wù)器運算以及opencl庫啟用踩坑!
坑來了
NDK在這里,版本很重要我測試過(android-ndk-r10e,android-ndk-r12b,android-ndk-r16b,android-ndk-r23b),太高的版本(android-ndk-r23b)會報錯,各種錯,一個是找不到tools-chain里面的編譯器工具錯誤,因為新版已經(jīng)把這些工具包去掉了,只有l(wèi)lvm,renderscript文件夾,后續(xù)用到的Cross Compile也會需要,高版本是沒法正常使用的:
cd /opt/android-ndk/build/tools/
//高版本NDK這條命令沒法正常執(zhí)行
./make-standalone-toolchain.sh --platform=android-24 --use-llvm --arch=arm64 --install-dir=/opt/android-toolchain-arm64
更新了編譯機(jī)制,就算你從低版本復(fù)制對應(yīng)的chain包進(jìn)去,依然會有其他錯誤,錯誤是沒有具體提醒的,具體姿勢是這樣的:
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執(zhí)行腳本的話會報這個錯誤
Process 'command 'sh'' finished with non-zero exit value 2
NDK版本太低的版本(10e,12b)會報錯誤,而且ubuntu16.4默認(rèn)的clang是3.8的版本,最好手動更新下默認(rèn)clang版本,同理還有g(shù)cc 和 g++版本一定要檢查(我用的是gcc g++ 7.5),否者會遇到下面錯誤,這兩個錯誤主要能在jni 對應(yīng)的Application.mk配置里找到對應(yīng)的命令行,但本質(zhì)上就是編譯版本對不上的問題:
//錯誤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
另外,我在參考有大佬文檔的時候,說自己運行g(shù)radle clean build 進(jìn)行打包的時候會報錯,但是我后面把各種坑踩完以后,倒是沒遇到這個問題,這里提供一種擔(dān)心gradle版本過高(反正我用最新的gradle 7沒遇到),可能引起錯誤的規(guī)避方法,使用gradlew腳本進(jìn)行指定版本編譯,操作如下:
//先切換到android_rpc根目錄,執(zhí)行下面命令,會自動生成gradlew腳本
//且會自動下載對應(yīng)的gradle插件包,避免版本不匹配的問題
//我這里設(shè)置的4.4版本,工程里3.1版本下不下來
gradle wrapper --gradle-version 4.4
//執(zhí)行完畢以后就生成好gradlew腳本文件了,執(zhí)行工程編譯
./gradlew clean build