opencl.md 11.1 KB
Newer Older
J
up  
juncaipeng 已提交
1 2 3 4 5
---
layout: post
title: 基于OpenCL的ARM GPU预测
---

Y
Yuan Shuai 已提交
6
Lite支持在Android系统上运行基于OpenCL的程序,目前支持Ubuntu环境下armv8、armv7的交叉编译。
J
up  
juncaipeng 已提交
7 8 9

## 编译

Y
Yuan Shuai 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
### 编译环境

1. Docker 容器环境;
2. Linux(推荐 Ubuntu 16.04)环境。

详见[ **源码编译指南-环境准备** 章节](./source_compile.md)

### 编译选项

|参数|介绍|值|
|--------|--------|--------|
|--arm_os|代表目标操作系统|目前仅支持且默认为`android`|
|--arm_abi|代表体系结构类型,支持armv8和armv7|默认为`armv8`即arm64-v8a;`armv7`即armeabi-v7a|
|--arm_lang|代表编译目标文件所使用的编译器|默认为gcc,支持 gcc和clang两种|

25
### 编译Paddle-Lite OpenCL库范例
Y
Yuan Shuai 已提交
26

27
注:以android-armv8-opencl的目标、Docker容器的编译开发环境为例,CMake3.10,android-ndk-r17c位于`/opt/`目录下。
J
up  
juncaipeng 已提交
28 29

```bash
Y
Yuan Shuai 已提交
30
# 假设当前位于处于Lite源码根目录下
J
up  
juncaipeng 已提交
31

Y
Yuan Shuai 已提交
32 33
# 导入NDK_ROOT变量,注意检查您的安装目录若与本示例不同
export NDK_ROOT=/opt/android-ndk-r17c
J
up  
juncaipeng 已提交
34

Y
Yuan Shuai 已提交
35 36 37
# 删除上一次CMake自动生成的.h文件
rm ./lite/api/paddle_use_kernels.h
rm ./lite/api/paddle_use_ops.h
J
up  
juncaipeng 已提交
38

Y
Yuan Shuai 已提交
39 40 41 42 43 44 45
# 根据指定编译参数编译
./lite/tools/ci_build.sh \
  --arm_os=android \
  --arm_abi=armv8 \
  --arm_lang=gcc \
  build_test_arm_opencl
```
J
up  
juncaipeng 已提交
46

47
编译产物位于`build.lite.android.armv8.gcc.opencl`下的`inference_lite_lib.android.armv8.opencl`文件夹内,这里仅罗列关键产物:
J
up  
juncaipeng 已提交
48

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
- `cxx`:该目录是编译目标的C++的头文件和库文件;
- `demo`:该目录包含了两个demo,用来调用使用`libpaddle_api_full_bundled.a``libpaddle_api_light_bundled.a`,分别对应`mobile_full``mobile_light`文件夹。编译对应的demo仅需在`mobile_full``mobile_light`文件夹下执行`make`命令即可:  
  - `mobile_full`:使用cxx config,可直接加载fluid模型,若使用OpenCL需要在`mobilenetv1_full_api.cc`代码里开启`DEMO_USE_OPENCL`的宏,详细见代码注释;  
  - `mobile_light`:使用mobile config,只能加载`model_optimize_tool`优化过的模型;
- `opencl`:该目录存放opencl实现的相关kernel。

```bash
.
|-- cxx
|   |-- include
|   |   |-- paddle_api.h
|   |   |-- paddle_image_preprocess.h
|   |   |-- paddle_lite_factory_helper.h
|   |   |-- paddle_place.h
|   |   |-- paddle_use_kernels.h
|   |   |-- paddle_use_ops.h
|   |   `-- paddle_use_passes.h
|   `-- lib
|       |-- libpaddle_api_full_bundled.a
|       |-- libpaddle_api_light_bundled.a
|       |-- libpaddle_full_api_shared.so
|       `-- libpaddle_light_api_shared.so
|-- demo
|   `-- cxx
|       |-- Makefile.def
|       |-- README.md
|       |-- include
|       |   |-- paddle_api.h
|       |   |-- paddle_lite_factory_helper.h
|       |   |-- paddle_place.h
|       |   |-- paddle_use_kernels.h
|       |   |-- paddle_use_ops.h
|       |   `-- paddle_use_passes.h
|       |-- mobile_full
|       |   |-- Makefile
|       |   `-- mobilenetv1_full_api.cc
|       `-- mobile_light
|           |-- Makefile
|           `-- mobilenetv1_light_api.cc
`-- opencl
    `-- cl_kernel
        |-- buffer
        |   |-- depthwise_conv2d_kernel.cl
        |   |-- elementwise_add_kernel.cl
        |   |-- fc_kernel.cl
        |   |-- im2col_kernel.cl
        |   |-- layout_kernel.cl
        |   |-- mat_mul_kernel.cl
        |   |-- pool_kernel.cl
        |   `-- relu_kernel.cl
        |-- cl_common.h
        `-- image
            |-- channel_add_kernel.cl
            |-- elementwise_add_kernel.cl
            |-- pool_kernel.cl
            `-- relu_kernel.cl
```

调用`libpaddle_api_full_bundled.a``libpaddle_api_light_bundled.a`见下一部分运行示例。



## 运行示例

下面以android、ARMv8、gcc的环境为例,介绍3个示例,分别如何在手机上执行基于OpenCL的ARM GPU推理过程。  

**注意:** 以下命令均在Lite源码根目录下运行。在3个示例前,下面这段命令都先要执行用来准备环境:
J
up  
juncaipeng 已提交
116 117 118 119 120 121

```bash
# 在/data/local/tmp目录下创建OpenCL文件目录
adb shell mkdir -p /data/local/tmp/opencl
adb shell mkdir -p /data/local/tmp/opencl/cl_kernel/buffer
adb shell mkdir -p /data/local/tmp/opencl/cl_kernel/image
Y
Yuan Shuai 已提交
122

J
up  
juncaipeng 已提交
123
# 将OpenCL的kernels文件推送到/data/local/tmp/opencl目录下
Y
Yuan Shuai 已提交
124 125 126 127 128
adb push lite/backends/opencl/cl_kernel/cl_common.h /data/local/tmp/opencl/cl_kernel/
adb push lite/backends/opencl/cl_kernel/buffer/* /data/local/tmp/opencl/cl_kernel/buffer/
adb push lite/backends/opencl/cl_kernel/image/* /data/local/tmp/opencl/cl_kernel/image/
```

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
### 运行示例1: 编译产物demo示例

```bash
######################################################################
# 编译mobile_full的demo                                              #
######################################################################
# 步骤:                                                              #
#   0.确保编译Paddle-Lite时编译了OpenCL;                             #
#   1.编辑`mobilenetv1_full_api.cc`代码, 开启`DEMO_USE_OPENCL`的宏;  #
#   2.在产物目录`demo/cxx/mobile_full`下编译`mobile_full`的demo;     #
#   3.上传demo, 模型, opencl kernel文件到手机;                       #
#   4.运行demo得到预期结果.                                          #
######################################################################
adb shell mkdir /data/local/tmp/opencl/mobilenet_v1
chmod +x ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_full/mobilenetv1_full_api"
adb push ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_full/mobilenetv1_full_api /data/local/tmp/opencl/
adb push ./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1

# use mobile_full run mobilenet_v1
# `GLOG_v` is log level
adb shell "export GLOG_v=0; \
    /data/local/tmp/opencl/mobilenetv1_full_api \
    --model_dir=/data/local/tmp/opencl/mobilenet_v1 \
    --optimized_model_dir=/data/local/tmp/opencl/full_api_opt_model"


######################################################################
# 编译mobile_light的demo                                             #
######################################################################
# 步骤:                                                              #
#   0.确保编译Paddle-Lite时编译了OpenCL;                             #
#   1.编译model_optimize_tool并对模型优化, `targets`参数为`opencl`;  #
#   2.在产物目录`demo/cxx/mobile_light`下编译`mobile_light`的demo;   #
#   3.上传demo, 模型, opencl kernel文件到手机;                       #
#   4.运行demo得到预期结果.                                          #
######################################################################

# use model_optimize_tool to optimize model
./build.model_optimize_tool/lite/api/model_optimize_tool \
  --model_dir=./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/ \
  --optimize_out_type=naive_buffer \
  --optimize_out=./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/ \
  --valid_targets=opencl

adb shell mkdir /data/local/tmp/opencl/mobilenet_v1
chmod +x ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_light/mobilenetv1_light_api"
adb push ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp/opencl/
adb push ./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1

# use mobile_light run mobilenet_v1
adb shell "export GLOG_v=5; \
  /data/local/tmp/opencl/mobilenetv1_light_api \
  --model_dir=/data/local/tmp/opencl/"
```

### 运行示例2: test_mobilenetv1单元测试
Y
Yuan Shuai 已提交
185 186 187 188

- **运行文件准备**

```bash
J
up  
juncaipeng 已提交
189 190
# 将mobilenet_v1的模型文件推送到/data/local/tmp/opencl目录下
adb shell mkdir -p /data/local/tmp/opencl/mobilenet_v1
Y
Yuan Shuai 已提交
191 192 193 194
adb push build.lite.android.armv8.gcc.opencl/third_party/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1/

# 将OpenCL单元测试程序test_mobilenetv1,推送到/data/local/tmp/opencl目录下
adb push build.lite.android.armv8.gcc.opencl/lite/api/test_mobilenetv1 /data/local/tmp/opencl
J
up  
juncaipeng 已提交
195 196 197 198
```

- **执行OpenCL推理过程**

Y
Yuan Shuai 已提交
199 200 201 202
使用如下命令运行OpenCL程序。其中:

- `--cl_path`指定了OpenCL的kernels文件即cl\_kernel所在目录;
- `--modle_dir`指定了模型文件所在目录。
J
up  
juncaipeng 已提交
203 204

```bash
Y
Yuan Shuai 已提交
205 206 207 208 209 210 211
adb shell chmod +x /data/local/tmp/opencl/test_mobilenetv1

adb shell /data/local/tmp/opencl/test_mobilenetv1 \
  --cl_path=/data/local/tmp/opencl \
  --model_dir=/data/local/tmp/opencl/mobilenet_v1 \
  --warmup=1 \
  --repeats=1
J
up  
juncaipeng 已提交
212 213 214 215
```

**注意:** 因为权重参数均会在Op Kernel第一次运行时进行加载,所以第一次的执行时间会略长。一般将warmup的值设为1,repeats值设为多次。

216
### 运行示例3: test_layout_opencl单元测试
Y
Yuan Shuai 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231

- **运行文件准备**

```bash
# 将OpenCL单元测试程序test_layout_opencl,推送到/data/local/tmp/opencl目录下
adb push build.lite.android.armv8.gcc.opencl/lite/kernels/opencl/test_layout_opencl /data/local/tmp/opencl/
```

- **执行OpenCL推理过程**

```bash
adb shell chmod +x /data/local/tmp/opencl/test_layout_opencl
adb shell /data/local/tmp/opencl/test_layout_opencl
```

J
up  
juncaipeng 已提交
232 233 234

# 如何在Code中使用

235 236 237 238

注意:推荐用户首先参考前文中提到的编译产物`demo`中的使用方法。

下面对单测中的代码进行讲解,Lite支持对ARM CPU和ARM GPU的混调执行,具体描述如下:
J
up  
juncaipeng 已提交
239

Y
Yuan Shuai 已提交
240 241 242 243 244 245
- 设置Lite推断执行的有效Places,使其包含ARM CPU(kARM)和ARM GPU(kOpenCL);
- 确保GPU(kOpenCL)在第一位,位置代表Places的重要性和kernel选择有直接关系。  

通过以上设置,Lite在推断执行过程中如果发现某一Op存在着基于OpenCL的实现,其会优先选择使用该实现执行Op的计算过程。若发现某一Op没有基于OpenCL实现的Kernel,其会自动选择执行基于ARM CPU的实现。

代码示例(来自`lite/api/mobilenetv1_test.cc`):
J
up  
juncaipeng 已提交
246 247

```cpp
Y
Yuan Shuai 已提交
248
// 初始化预测实例、CPU线程数、CPU策略
J
up  
juncaipeng 已提交
249 250 251
DeviceInfo::Init();
DeviceInfo::Global().SetRunMode(LITE_POWER_HIGH, FLAGS_threads);
lite::Predictor predictor;
Y
Yuan Shuai 已提交
252 253

// 设置Lite推断执行的硬件信息Places为{kOpenCL, kARM}
J
up  
juncaipeng 已提交
254
std::vector<Place> valid_places({
255 256 257 258
      Place({TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kNHWC)}),
      Place({TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kNCHW)}),
      Place({TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNHWC)}),
      Place({TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)}),
S
sangoly 已提交
259
      Place({TARGET(kARM), PRECISION(kFloat)})
J
up  
juncaipeng 已提交
260
  });
S
sangoly 已提交
261

Y
Yuan Shuai 已提交
262
// 根据Place构建模型
S
sangoly 已提交
263
predictor.Build(model_dir, "", "", valid_places);
Y
Yuan Shuai 已提交
264

J
up  
juncaipeng 已提交
265 266 267 268 269 270 271 272
// 设置模型的输入
auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224})));
auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production();
for (int i = 0; i < item_size; i++) {
  data[i] = 1;
}
Y
Yuan Shuai 已提交
273 274

// 执行模型推断
J
up  
juncaipeng 已提交
275
predictor.Run();
Y
Yuan Shuai 已提交
276 277 278 279 280 281 282

// 获取模型的预测结果tensor
// 下面展示如何取出第一个输入tensor,及其维度,元素个数,指针
auto* out0_tensor = predictor.GetOutput(0);
auto out0_dims = out0_tensor->dims();
auto out0_item_size = out0_tensor->dims().production();
auto* out0_pointer = out0_tensor->data<float>();
S
sangoly 已提交
283
```