什么是GPU
GPU(Graphic Processing Unit)是图形处理器,相当于在计算机和移动终端上做图形图像运算工作的微处理器,显示芯片。通过向量计算和并行计算等方式加速了原有的计算工作,能够更好地处理几何转换和光照计算等,更是在深度学习领域起着不可或缺的作用。
什么是Adreno GPU
Adreno GPU为采用了骁龙处理器的移动终端提供游戏机品质的3D图形处理能力,为游戏、用户界面和高性能计算任务提供更快的图形处理。
如何获取Adreno GPU性能数据
1. 使用Adreno profiler
链接:Adreno GPU Profiler - Qualcomm Developer Network
高通官方提供了对应的Profiler 工具,不过具体的内容并没有开源,如果需要实时获取profiler性能还是推荐直接使用官方的工具。
2. 使用第三方库
链接:https://github.com/google/hardware-perfcounter
在这个Hardware-perfcounter里面,通过对一些工具的解析,能够通过cycle获取基础的GPU数据,从而帮助开发者更好地实时记录需要关注的GPU内容。
具体操作
这边只需要获取adreno gpu数据,因此对于库中所使用的mali gpu就不加赘述了。
准备工具:
cmake 版本大于3.13
g++ 或者 gcc 能够编译 c11/c++14的
ninja
1. 先git clone GitHub - google/hardware-perfcounter: libraries and tilities for sampling hardware performance counterslibraries and utilities for sampling hardware performance counters - GitHub - google/hardware-perfcounter: libraries and utilities for sampling hardware performance countershttps://github.com/google/hardware-perfcounter.git
到本地文件夹,然后进入到third_party,这边对envytools有依赖,所以需要拉取里面的内容,同样的使用git工具 clone
GitHub - freedreno/envytoolsContribute to freedreno/envytools development by creating an account on GitHub.https://github.com/freedreno/envytools.git
2. 直接build就行了。
Android
git clone https://github.com/google/HardwarePerfCounter.git cd HardwarePerfCounter cmake -G Ninja -S ./ -B build-android/ \ -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK?}/build/cmake/android.toolchain.cmake" \ -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-30 cmake --build build-android/
Linux/macOS
git clone https://github.com/google/HardwarePerfCounter.git cd HardwarePerfCounter cmake -G Ninja -S ./ -B build/ cmake --build build/
Windows
在Git网页上说明尚且不支持windows,应该是尚且不支持在windows操作系统执行获取gpu数据,编译方法同Linux/macOS,在Powershell/cmd执行对应的操作即可。
3. 具体使用方式
比如我build到android上,那么就会在build-android/examples内部出现多个example执行文件,这边adreno分为common, a5xx和a6xx三种,假设对应手机是iQOO 8,使用的是Adreno 660, 那么就执行
adb push adreno_a6xx_c_example /data/local/tmp
adb shell
cd /data/local/tmp
./adreno_a6xx_c_example
这样就能够获取示例所输出的内容。
解读示例
打开examples/adreno_a6xx_example.c文件,我们可以看到具体是如何获取的。
在a6xx.h文件中可以看到,库的作者做了不少工作,将对应内容的counter号所代表的含义已经通过enum标记好了,同时,封装好了如下几个方法:
//创建context
int hpc_gpu_adreno_a6xx_create_context(
uint32_t num_counters, hpc_gpu_adreno_a6xx_counter_t *counters,
const hpc_gpu_host_allocation_callbacks_t *allocator,
hpc_gpu_adreno_context_t **out_context);
//销毁context
int hpc_gpu_adreno_a6xx_destroy_context(
hpc_gpu_adreno_context_t *context,
const hpc_gpu_host_allocation_callbacks_t *allocator);
//开始counter
int hpc_gpu_adreno_a6xx_start_counters(const hpc_gpu_adreno_context_t *context);
//停止counter
int hpc_gpu_adreno_a6xx_stop_counters(const hpc_gpu_adreno_context_t *context);
//获取counter
int hpc_gpu_adreno_a6xx_query_counters(hpc_gpu_adreno_context_t *context,
uint64_t *values);
1. 首先定义好counter数组,数组内部对应的enum值为需要获取的数值。
2. 创建context, 并且使用对应的context开启counter
3. 通过query_counter的操作获取对应的数值,数值对应步骤1的counter数组
4. 停止counter同时销毁context.
那么,Query 又是如何获取counter的呢?还是通过查看源码,我们最终可以知道,调用的是ioctl的方法来读取对应counter数值。
int hpc_gpu_adreno_ioctl_query_counters(
int gpu_device, uint32_t num_counters,
hpc_gpu_adreno_ioctl_counter_read_counter_t *counters, uint64_t *values) {
struct adreno_counter_read payload;
memset(&payload, 0, sizeof(struct adreno_counter_read));
payload.num_counters = num_counters;
payload.counters = counters;
int status = ioctl(gpu_device, ADRENO_IOCTL_COUNTER_READ, &payload);
if (status) return status;
for (int i = 0; i < num_counters; ++i) values[i] = counters[i].value;
return 0;
}
linux 内核 - ioctl 函数详解 - 知乎1. 概念ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。 在文件 I/O 中…https://zhuanlan.zhihu.com/p/478259733具体涉及到linux内核,类似操作应该是和写驱动的方法一样,此处就不长篇大论了。
注意事项
在博主具体操作过程中发现,当我使用perfmance-counter采集骁龙处理器的gpucounter时,当counter数组中含有的enum大于6时,后续数字经常会出错或为0。这边是每次通过gpu cycle就获取counter数组量的数值速度太慢,来不及获取已经更新。因此具体使用时建议在每次调用query时获取的counter数据尽量保持在6个以内。