opencl.md 5.2 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 25 26 27
### 编译环境

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两种|

### 编译范例

注:以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

Y
Yuan Shuai 已提交
47
## 运行示例准备
J
up  
juncaipeng 已提交
48

Y
Yuan Shuai 已提交
49
下面以android、ARMv8、gcc的环境为例,介绍如何在手机上执行基于OpenCL的ARM GPU推理过程。  
J
up  
juncaipeng 已提交
50 51 52 53 54 55 56
**注意:** 以下命令均在Lite源码根目录下运行。

```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 已提交
57

J
up  
juncaipeng 已提交
58
# 将OpenCL的kernels文件推送到/data/local/tmp/opencl目录下
Y
Yuan Shuai 已提交
59 60 61 62 63 64 65 66 67 68
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/
```

### 运行示例1: test_mobilenetv1

- **运行文件准备**

```bash
J
up  
juncaipeng 已提交
69 70
# 将mobilenet_v1的模型文件推送到/data/local/tmp/opencl目录下
adb shell mkdir -p /data/local/tmp/opencl/mobilenet_v1
Y
Yuan Shuai 已提交
71 72 73 74
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 已提交
75 76 77 78
```

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

Y
Yuan Shuai 已提交
79 80 81 82
使用如下命令运行OpenCL程序。其中:

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

```bash
Y
Yuan Shuai 已提交
85 86 87 88 89 90 91
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 已提交
92 93 94 95
```

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

Y
Yuan Shuai 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
### 运行示例2: test_layout_opencl

- **运行文件准备**

```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 已提交
112 113 114 115 116

# 如何在Code中使用

Lite支持对ARM CPU和ARM GPU的混调执行,具体描述如下:

Y
Yuan Shuai 已提交
117 118 119 120 121 122
- 设置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 已提交
123 124

```cpp
Y
Yuan Shuai 已提交
125
// 初始化预测实例、CPU线程数、CPU策略
J
up  
juncaipeng 已提交
126 127 128
DeviceInfo::Init();
DeviceInfo::Global().SetRunMode(LITE_POWER_HIGH, FLAGS_threads);
lite::Predictor predictor;
Y
Yuan Shuai 已提交
129 130

// 设置Lite推断执行的硬件信息Places为{kOpenCL, kARM}
J
up  
juncaipeng 已提交
131
std::vector<Place> valid_places({
S
sangoly 已提交
132 133
      Place({TARGET(kOpenCL), PRECISION(kFloat)}),
      Place({TARGET(kARM), PRECISION(kFloat)})
J
up  
juncaipeng 已提交
134
  });
S
sangoly 已提交
135

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

J
up  
juncaipeng 已提交
139 140 141 142 143 144 145 146
// 设置模型的输入
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 已提交
147 148

// 执行模型推断
J
up  
juncaipeng 已提交
149
predictor.Run();
Y
Yuan Shuai 已提交
150 151 152 153 154 155 156

// 获取模型的预测结果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 已提交
157
```