data 類別為: Image/Tensor, TensorInfo
task/workload 類別為: Kernel, Window 與 Function
下列的內容主要為 ARM Compute Library: Documentation 所描述
並且搭配 source code 的內容做特定用途的說明
(取代文件內 MyKernel, MyFunction 的方式)
由於 ARM Computer Library 是做 Computer Vision 與 Machine Learning 應用的
因此主要處理的資料型別為 Image 及 Tensor
在 Compute Library 中基本上只是名稱不同而已
Image, Tensor, TensorInfo
在 NEON 下直接使用 ImageImage src, dst;而在 CL 下則使用 CLImage
CLImage src, tmp, dst;Image 宣告後並沒有實際的 buffer 空間, 必須進一步最配置的動作,配置的方法有二, 兩者都需要傳遞 TensorInfo 資訊, TensorInfo 基本上為 Image/Tensor 各維度的大小以及資料格式
配置的第一種方式為直接透過 Allocator 的 init() 方式
src.allocator()->init(TensorInfo(640, 480, Format::U8));而第二種方式為先呼叫 configure() 在呼叫 Allocator 的 allocate()
TensorInfo dst_tensor_info(src.info()->dimension(0) / scale_factor, src.info()->dimension(1) / scale_factor, Format::U8);
dst.allocator()->init(dst_tensor_info);
dst.allocator()->allocate();
Kernel, Window & Function
空間配置好之後, 就必須透過各種各樣的 Kernel 來套用對應的功能來操作 Image/Tensor使用上的核心 class 為 Kernel, 各個 Kernel 實作了 IKernel 相關的介面
使用的第一個步驟為宣告想使用的 kernel object, 假設我們想作 image scale
//Create a kernel object:在使用之前必須對 kernel 作 input, output 使用參數以及 padding mode 作設定
NEScaleKernel scale_kernel;
// Initialize the kernel with the input/output and options you want to use:這裡使用了 NEAREST Filter, 並且使用 UNDEFINED padding 方式 (對於邊界有缺所需資料的點不做處理)
Tensor offsets;
const TensorShape shape(dst.info()->dimension(0), dst.info()->dimension(1));
TensorInfo tensor_info_offsets(shape, Format::S32);
tensor_info_offsets.auto_padding();
offsets.allocator()->init(tensor_info_offsets);
scale_kernel.configure( &src, nullptr, nullptr, &offset, &dst, InterpolationPolicy::NEAREST_NEIGHBOR, BorderMode::UNDEFINED);
offsets.allocator()->allocate();
// compute offset
...
最後就是呼叫使用該功能,即是呼叫 IKernel 的 run() 介面
// Retrieve the execution window of the kernel:這些即為 Compute Library 基本的使用方法.
const Window& max_window = scale_kernel.window();// Run the whole kernel in the current thread:
scale_kernel.run( max_window ); // Run the kernel on the full window
對於 CL Kernel 則有稍微不同的 flow (需要特別傳入以操作 cl::CommandQueue)
或許會覺得上面例子的 max_window 很多餘, 但它是有進階應用的
Window 用途在於對 Kernel 指定要套用執行的範圍描述
在官方說明文件是以 Multi-Threading 的方式來說明 Window 的用途
const Window &max_window = scale_kernel->window();
const int num_iterations = max_window.num_iterations(split_dimension);
int num_threads = std::min(num_iterations, _num_threads);
for(int t = 0; t < num_threads; ++t){當下列所有的條件都符合後, Window 可以用來分割 workload 為多個子 Window
Window win = max_window.split_window(split_dimension, t, num_threads);
win.set_thread_id(t);
win.set_num_threads(num_threads);
if(t != num_threads - 1){
_threads[t].start(kernel, win); }else{
scale_kernel->run(win); }
}
- max[n].start() <= sub[n].start() < max[n].end()
- sub[n].start() < sub[n].end() <= max[n].end()
- max[n].step() == sub[n].step()
- (sub[n].start() - max[n].start()) % max[n].step() == 0
- (sub[n].end() - sub[n].start()) % max[n].step() == 0
至於 Function 的使用則是為了簡化繁雜的 Kernel, Window 的使用流程, Function 實作內部會自行配置所需的暫存 buffer, 甚至能透過上述的方式自行做 Multi-Threading
// Create and initialize a Scale function object:若使用的為以 CL 實作的 Kernel 最後還有個確保執行完成的額外同步動作
NEScale scale;scale.configure(&src, &dst, InterpolationPolicy::NEAREST_NEIGHBOR, BorderMode::UNDEFINED);
// Run the scale operation:
scale.run();
// Make sure all the OpenCL jobs are done executing:
CLScheduler::get().sync();
如此一來 Function 比起直接使用 Kernel 簡化不少
在下一篇會進入 NEON 與 CL 內部實作方式的說明