当前位置: 首页 > 工具软件 > gcc_termux > 使用案例 >

在termux中利用安卓的vulkan库加速NCNN

陈项禹
2023-12-01

termux是安卓手机上的一款终端模拟软件,运行在安卓的linux内核之上。在termux中我们也可以利用proot构建完整的linux发行版(如Ubuntu, Debian等),这样就可以在上面运行支持ARM64架构的软件啦,比如常用的Chromium, VS Code, WPS Office,配合xfce等桌面,用pad代替电脑简单办公不是梦!但是,linux的发行版一般基于GNU/Linux ,其使用的标准库glibc也与安卓系统内核使用的Bionic Libc有所不同,因此安卓系统自带的库文件(libOpenCL.so, libvulkan.so...)在linux发行版中是无法使用的。ps. 最近有一个有意思的项目libhybris可以帮助我们在GNU/Linux中加载安卓的库文件(主要是厂商闭源的HAL库),虽然项目还不是很成熟,可能会面临大量的报错,但这是在GNU/Linux下复用安卓基础设施是很有意思的尝试。

言归正传,在ncnn的wiki中其实包括了“Build for termux on android”一章,只不过这里面用的也是利用proot安装Ubuntu的做法(安装完Ubuntu后就和Build for linux一样),由于运行环境的不同,在proot里是没有办法调用设备的GPU来加速计算的,只能使用CPU来进行计算。要想利用设备自带的OpenCL/Vulkan来加速模型的推理,我们只能尝试在termux中编译ncnn,利用安卓内核来调用GPU。

1 Preliminary

首先从termux项目的github主页(https://github.com/termux/termux-app)release中下载最新的apk并安装,对安卓版本的要求是最低7.0。打开后,首先可以用termux-change-repo命令换到清华源。termux中自带的包管理器是pkg,用法和apt基本一致(同时termux也有apt,pkg向下兼容apt)。如使用

pkg install openssh
passwd

安装ssh并设置密码后,就可以在电脑上使用终端连接手机,注意ssh默认的端口是8022,因为没有root权限手机是无法操作端口号<1024的常用端口的。

登录后可以看到自己的路径为“/data/data/com.termux/files/home”,没错,这里的绝对路径就是相对于安卓内核的路径,termux在这里模拟出了一个home地址,而使用pkg安装的包会在“/data/data/com.termux/files/usr”下。一般安卓系统库的位置为/system/lib64,不同手机厂商会有点不同,可以使用AIDA64软件查找自己OpenCL/Vulkan库的具体位置。找到后还需要对应的头文件,这里我们使用命令下载安装。

apt install vulkan-headers
pkg install vulkan-tools

这里vulkan-tools并不是必须的,但是安装后可以使用命令vulkaninfo来查看设备的vulkan信息,也可以用来验证vulkan环境是否已经完整~

2 下载编译NCNN

git clone https://gitee.com/Tencent/ncnn.git
git submodule update --init --recursive

如果因为网络问题连不上github,可以从gitee的镜像来克隆ncnn。仓库中另有依赖于glslang和pybind,也需要下载。

代码准备完毕,安装termux中的编译器:

pkg install clang

这里是基于谷歌Android toolchain llvm的编译器。前面提到GNU的库和编译器都不能在安卓上运行,因此需要Android toolchain来编译。安装完后,环境也贴心的把gcc也链接到了clang上,这样在编译时不用修改太多的cmake文件。

进入克隆的ncnn文件夹,在toolchain下面新建一个termux.toolchain.cmake用于指定编译器:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_C_FLAGS "-march=armv8-a")
set(CMAKE_CXX_FLAGS "-march=armv8-a")

# cache flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags")

完成后在ncnn目录下创建一个build文件夹,开始编译:

cmake -DCMAKE_BUILD_TYPE=Release -DNCNN_VULKAN=ON -DNCNN_SYSTEM_GLSLANG=OFF -DNCNN_BUILD_EXAMPLES=ON  -DCMAKE_TOOLCHAIN_FILE=../toolchains/termux.toolchain.cmake -DVulkan_LIBRARY=/system/lib64/libvulkan.so  -DNCNN_PLATFORM_API=OFF -DNCNN_SYSTEM_GLSLANG=OFF ..

其中TOOLCHAIN指定了上面的编译器,Vulkan_LIBRARY指定了安卓libvulkan.so的位置,NCNN_PLATFORM_API默认为ON,经测试这样ncnn会用到安卓的一些log功能(还是被你发现了我是安卓),由于没有链接相关库而报undefined symbol错误,设为OFF则编译成功。

3 验证ncnn和Vulkan

进入ncnn目录下的benchmark文件夹,运行

../build/benchmark/benchncnn 8 4 0 0 1

其中参数的含义可见官方wiki,简单来说第一个参数为循环次数,第二个为线程数,第四个为-1则为CPU模式,>=0则为GPU id,手机上则使用0。

作者这里使用的是一台2016年的老手机,麒麟655的Soc,8核A53的CPU+Mali T830MP2的GPU,性能上还远不如RK3399(A72大核+Mali T860MP4),但也可以运行vulkan模式和像vgg16这样的大网络。简单贴一下性能,在CPU4核模式下:

~/ncnn/benchmark $ ../build/benchmark/benchncnn 8 4 0 -1 1
loop_count = 8
num_threads = 4
powersave = 0
gpu_device = -1
cooling_down = 1
          squeezenet  min =   84.65  max =   86.07  avg =   85.24
     squeezenet_int8  min =   82.10  max =   83.10  avg =   82.55
           mobilenet  min =  113.70  max =  117.65  avg =  115.38
      mobilenet_int8  min =   86.20  max =   88.93  avg =   87.09
        mobilenet_v2  min =  138.55  max =  156.88  avg =  152.12
        mobilenet_v3  min =   88.80  max =   93.86  avg =   92.43
          shufflenet  min =  108.49  max =  112.89  avg =  109.60
       shufflenet_v2  min =   66.64  max =   67.58  avg =   67.19
             mnasnet  min =   92.42  max =  145.46  avg =  122.95
     proxylessnasnet  min =  110.89  max =  113.93  avg =  112.48
     efficientnet_b0  min =  180.27  max =  269.97  avg =  229.80
   efficientnetv2_b0  min =  211.38  max =  213.64  avg =  212.44
        regnety_400m  min =  189.46  max =  193.84  avg =  191.28
           blazeface  min =   23.03  max =   26.67  avg =   23.75
           googlenet  min =  259.65  max =  264.35  avg =  260.75
      googlenet_int8  min =  229.00  max =  251.55  avg =  234.80
            resnet18  min =  199.28  max =  208.02  avg =  202.51
       resnet18_int8  min =  158.06  max =  163.77  avg =  160.61
             alexnet  min =  269.18  max =  274.16  avg =  272.43
               vgg16  min = 1155.47  max = 1235.34  avg = 1184.81
          vgg16_int8  min =  747.65  max =  758.80  avg =  751.62
            resnet50  min =  505.84  max =  580.00  avg =  531.49
       resnet50_int8  min =  438.09  max =  440.49  avg =  438.78
      squeezenet_ssd  min =  225.10  max =  499.12  avg =  274.91
 squeezenet_ssd_int8  min =  197.45  max =  216.89  avg =  202.77
       mobilenet_ssd  min =  234.62  max =  240.82  avg =  238.23
  mobilenet_ssd_int8  min =  178.87  max =  180.79  avg =  179.63
      mobilenet_yolo  min =  489.46  max =  499.22  avg =  493.67
  mobilenetv2_yolov3  min =  310.20  max =  452.35  avg =  335.04
         yolov4-tiny  min =  447.58  max =  536.35  avg =  495.77
           nanodet_m  min =  160.12  max =  168.65  avg =  163.86
    yolo-fastest-1.1  min =   80.50  max =   89.89  avg =   82.72
      yolo-fastestv2  min =   68.25  max =   80.10  avg =   76.25

在vulkan模式下:

~/ncnn/benchmark $ ../build/benchmark/benchncnn 8 4 0 0 1
[0 Mali-T830]  queueC=0[2]  queueG=0[2]  queueT=0[2]
[0 Mali-T830]  bugsbn1=0  bugbilz=0  bugcopc=0  bugihfa=0
[0 Mali-T830]  fp16-p/s/a=1/0/0  int8-p/s/a=1/0/0
[0 Mali-T830]  subgroup=16  basic=0  vote=0  ballot=0  shuffle=0
loop_count = 8
num_threads = 4
powersave = 0
gpu_device = 0
cooling_down = 1
          squeezenet  min =   98.36  max =  101.33  avg =  100.14
     squeezenet_int8  min =   93.73  max =  149.93  avg =  127.12
           mobilenet  min =  148.93  max =  150.30  avg =  149.69
      mobilenet_int8  min =  134.03  max =  147.65  avg =  137.13
        mobilenet_v2  min =   99.72  max =  101.14  avg =  100.44
        mobilenet_v3  min =   90.05  max =   94.20  avg =   91.74
          shufflenet  min =   61.54  max =   65.67  avg =   62.83
       shufflenet_v2  min =   76.51  max =   80.57  avg =   78.73
             mnasnet  min =  100.05  max =  101.79  avg =  100.77
     proxylessnasnet  min =  104.45  max =  107.86  avg =  106.07
     efficientnet_b0  min =  147.30  max =  151.59  avg =  149.21
   efficientnetv2_b0  min =  303.89  max =  417.94  avg =  338.40
        regnety_400m  min =  119.83  max =  122.73  avg =  121.04
           blazeface  min =   55.65  max =   59.06  avg =   57.05
           googlenet  min =  324.63  max =  327.42  avg =  325.84
      googlenet_int8  min =  254.78  max =  399.30  avg =  329.25
            resnet18  min =  337.40  max =  347.44  avg =  342.30
       resnet18_int8  min =  178.98  max =  189.48  avg =  185.73
             alexnet  min =  277.58  max =  299.94  avg =  284.57
               vgg16  min = 2690.19  max = 3180.77  avg = 2957.29
          vgg16_int8  min =  748.23  max =  765.36  avg =  754.77
            resnet50  min =  857.16  max = 1004.09  avg =  936.61
       resnet50_int8  min =  435.07  max =  440.28  avg =  437.63
      squeezenet_ssd  min =  390.19  max =  456.46  avg =  409.24
 squeezenet_ssd_int8  min =  196.69  max =  202.96  avg =  198.65
       mobilenet_ssd  min =  340.55  max =  378.76  avg =  363.61
  mobilenet_ssd_int8  min =  179.10  max =  181.20  avg =  180.20
      mobilenet_yolo  min =  735.47  max =  819.14  avg =  778.76
  mobilenetv2_yolov3  min =  370.43  max =  407.27  avg =  392.68
         yolov4-tiny  min =  757.71  max =  844.02  avg =  789.01
           nanodet_m  min =  154.20  max =  193.19  avg =  168.09
    yolo-fastest-1.1  min =   67.22  max =   74.97  avg =   71.27
      yolo-fastestv2  min =   60.79  max =   76.87  avg =   68.21

这个故事告诉我们…如果你是弱鸡的GPU,那还不如老老实实的用CPU吧!

 类似资料: