TVM編譯遇到的那些坑

由于工作需要,初次接觸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.cmake to 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容