1. 程式人生 > >Android Studio編譯C++程式碼——Release與Debug版本的記錄

Android Studio編譯C++程式碼——Release與Debug版本的記錄

問題

部分C++程式碼庫,Release版本與Debug版本速度差異非常大,拿之前的Dlib的人臉檢測來說,Debug版本在手機上跑速度基本上是15秒1幀,而Release版本差不多是1秒2幀,這個速度差異非常的大。

AS上始終編譯不出Release版本的庫檔案,又不會在Linux下去編譯SO檔案(技術不行,,,加上Android執行環境和Linux也有一點區別)

經過多天的摸索,大概有了思路,記錄回顧一下。

Android Studio使用Cmake時的版本設定問題

  1. 使用CmakeLists.txt指定,無效。。。
  2. 使用build.gradle的arguments指定-DBUILD_TYPE=Release,無效。。。
  3. cppFlags指定-O2或-O3,同樣無效。。。

探查目錄結構

發現一:AS為了加速使用Cmake的編譯效率,Configure模組後,會生成.ExternalNativeBuild資料夾,發現該目錄下有cmake/debug和cmake/release兩種版本的配置檔案。

繼續往裡面看,發現有cmake_build_command.txtandroid_gradle_build.json
android_gradle_build.json內容為如下形式:
這裡寫圖片描述
其中的flags為編譯此檔案的引數。向後拖動,會發現帶有-g、-O0(-O2)等。

Debug版本和Release版本的最大區別就是有沒有加入除錯資訊和有沒有進行優化。優化選項極大的影響了程式的執行速度和效率。

下面的思路是找到這些引數的出處。

再來看cmake_build_command.txt內容為如下:

Executable : F:\sdk\android-sdk-windows\cmake\3.6.4111459\bin\cmake.exe
arguments : 
-HE:\AS_Workspace\TestDlibModule0103\dlibtool
-BE:\AS_Workspace\TestDlibModule0103\dlibtool\.externalNativeBuild\cmake\debug\arm64-v8a
-DANDROID_ABI=arm64-v8a
-DANDROID_PLATFORM=android-21
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=E:\AS
_Workspace\TestDlibModule0103\dlibtool\build\intermediates\cmake\debug\obj\arm64-v8a -DCMAKE_BUILD_TYPE=Debug -DANDROID_NDK=F:\sdk\android-sdk-windows\ndk-bundle -DCMAKE_CXX_FLAGS=-std=c++11 -DCMAKE_TOOLCHAIN_FILE=F:\sdk\android-sdk-windows\ndk-bundle\build\cmake\android.toolchain.cmake -DCMAKE_MAKE_PROGRAM=F:\sdk\android-sdk-windows\cmake\3.6.4111459\bin\ninja.exe -GAndroid Gradle - Ninja -DANDROID_PLATFORM=android-19 -DANDROID_TOOLCHAIN=clang -DANDROID_STL=c++_shared jvmArgs :

裡面定義了一些引數,發現有-DCMAKE_TOOLCHAIN_FILE=F:\sdk\android-sdk-windows\ndk-bundle\build\cmake\android.toolchain.cmake這樣的引數,開啟該檔案。

可看到如下內容:
這裡寫圖片描述
找到了一些預設配置,其實都是NDK為了方便我們編譯,使用了該工具進行編譯引數上的簡化。

向下翻動,發現目標-g
這裡寫圖片描述
上面是通用的flags,不知道為什麼預設使用了-g,帶有除錯資訊的標誌。

下面是Debug和Release的標誌資訊。此時的疑問:該處有Dubug和Release版本的區分,為什麼在外面進行修改引數,不起作用呢?

猜測、嘗試

.ExternalNativeBuild/cmake/debug.ExternalNativeBuild/cmake/release中的android_gradle_build.json的flags其實是不一樣的。

Debug的flags是-O0,不進行任何優化。
Release的flags是-Os,進行了不縮小程式碼體積的O3優化(相當於-O2.5)。

Build/intermediates/下是一些中間檔案,找到cmake資料夾,發現只有debug資料夾,說明只生成了debug版本的庫檔案。

發現了build variants這麼個東西,欣喜若狂的發現了Release,修改成Release,立馬Make 模組名。
從中間檔案那裡看到,生成了需要的Release版本的庫檔案,大小明顯比Debug版本的要小。

驗證

拷貝到主工程的Jnilibs下,編譯APK,安裝,執行,速度果然相比Debug版本提升很多很多。

使用預編譯的庫出現的問題

發現主工程與匯入的module(用到了JNI)關聯後,即使在build.gradle設定了sourceSets.main.jniLib.srcDirs,貼上了生成好的庫檔案,主工程編譯時,還是會先去檢查被關聯的module的intermediates資料夾中的cmake的debug下的debug版本的so庫是否存在以及是否被替換(猜測應該在哪裡配置了的),若不存在或被替換,則重新去編譯module,生成最新的so庫檔案。
然後自動拷貝到生成APK中去。

進一步發現

主工程的libs中的檔案優先於module的中間檔案的,也就是說APK中的SO庫是來自於libs,但還是會進行上面的檢查和編譯。
除非刪掉module下的C++程式碼和取消CmakeLists.txt,讓Module變成純的JAVA庫。

小問題

由於module中設定了STL=c++_shared,一開始只將我自己的庫拷貝到了另外的工程的libs去,發現執行時報錯,說找不到libc++_shared.so庫檔案,但是在自己的工程中,就不會出現這樣的問題。
自己的工程中,發現在生成的APK中的libs下是有這個庫檔案的,猜想也是預設將中間檔案新增到了主工程的庫檔案當中去(就像上面的那樣,自己的工程中,即使在libs中不加自己的庫,但是有依賴關係的話,主工程就會自動加依賴模組的庫檔案進去,而在別人的工程中,沒有關聯的module或新增的是純JAVA庫不編譯C++程式碼)。

解決辦法

手動拷貝libc++_shared.so到主工程的libs下,當作預編譯的庫使用,該檔案可在android-sdk-windows\ndk-bundle\sources\cxx-stl\llvm-libc++\libs找到不同架構的庫檔案。