1# FFRT 开发指导 2 3## 场景介绍 4 5Function Flow编程模型是一种基于任务和数据驱动的并发编程模型,允许开发者通过任务及其依赖关系描述的方式进行应用开发。FFRT(Function Flow运行时)是支持Function Flow编程模型的软件运行时库,用于调度执行开发者基于Function Flow编程模型开发的应用。通过Function Flow编程模型和FFRT,开发者可专注于应用功能开发,由FFRT在运行时根据任务依赖状态和可用执行资源自动并发调度和执行任务。 6 7本文用于指导开发者基于Function Flow编程模型和FFRT实现并行编程。 8 9### 两种编程模型 10 11 12| | 线程编程模型 | FFRT任务编程模型 | 13| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | 14| 并行度挖掘方式 | 程序员通过创建多线程并把任务分配到每个线程中执行来挖掘运行时的并行度。 | 程序员(编译器工具或语言特性配合)静态编程时将应用分解成任务及其数据依赖关系,运行时调度器分配任务到工作线程执行。 | 15| 谁负责线程创建 | 程序员负责创建线程,线程编程模型无法约束线程的创建,滥用可能造成系统中大量线程。| 由调度器负责工作线程池的创建和管理,程序员无法直接创建线程。 | 16| 负载均衡 | 程序员静态编程时将任务映射到线程,映射不合理或任务执行时间不确定造成线程负载不均。 | FFRT运行时根据线程执行状态调度就绪任务到空闲线程执行,减轻了线程负载不均问题。 | 17| 调度开销 | 线程调度由内核态调度器完成,调度开销大。 | FFRT运行时在用户态以协程方式调度执行,相比内核线程调度机制更为轻量,减小调度的开销,并可通过硬化调度卸载进一步减小调度开销。 | 18| 依赖表达 | 线程创建时即处于可执行状态,执行时与其他线程同步操作,增加线程切换。 | FFRT运行时根据任务创建时显式表达的输入依赖和输出依赖关系判断任务可执行状态,当输入依赖不满足时,任务不被调度执行。 | 19 20## 基本概念 21 22### Function Flow 任务编程模型 23 24Function Flow编程模型允许开发者通过任务及其依赖关系描述的方式进行应用开发,其主要特性包括`Task-Based` 和 `Data-Driven` 。 25 26#### Task-Based 特性 27 28`Task-Based` 指在Function Flow编程模型中开发者以任务方式来组织应用程序表达,运行时以任务粒度执行调度。 29 30任务定义为一种面向开发者的编程线索和面向运行时的执行对象,通常包含一组指令序列及其操作的数据上下文环境。 31 32Function Flow编程模型中的任务包含以下主要特征: 33 34- 任务之间可指定依赖关系,依赖关系通过`Data-Driven`方式表达。 35- 任务可支持嵌套,即任务在执行过程中可生成新的任务下发给运行时,形成父子任务关系。 36- 多任务支持互同步操作,例如等待、锁、条件变量等。 37 38> 注意 39> 40> 任务颗粒度影响应用执行性能,颗粒度过小增加调度开销,颗粒度过大降低并行度。Function Flow编程模型中任务的目标颗粒度最小为100us量级,开发者应注意合理控制任务颗粒度。 41 42#### Data-Driven 特性 43 44`Data-Driven`指任务之间的依赖关系通过数据依赖表达。 45 46任务执行过程中对其关联的数据对象进行读写操作。在Function Flow编程模型中,数据对象表达抽象为数据签名,每个数据签名唯一对应一个数据对象。 47 48数据依赖抽象为任务所操作的数据对象的数据签名列表,包括输入数据依赖`in_deps`和输出数据依赖`out_deps`。数据对象的签名出现在一个任务的`in_deps`中时,该任务称为数据对象的消费者任务,消费者任务执行不改变其输入数据对象的内容;数据对象的签名出现在任务的`out_deps`中时,该任务称为数据对象的生产者任务,生产者任务执行改变其输出数据对象的内容,从而生成该数据对象的一个新的版本。 49 50一个数据对象可能存在多个版本,每个版本对应一个生产者任务和零个,一个或多个消费者任务,根据生产者任务和消费者任务的下发顺序定义数据对象的多个版本的顺序以及每个版本所对应的生产者和消费者任务。 51 52数据依赖解除的任务进入就绪状态允许被调度执行,依赖解除状态指任务所有输入数据对象版本的生产者任务执行完成,且所有输出数据对象版本的所有消费者任务执行完成的状态。 53 54通过上述`Data-Driven`的数据依赖表达,FFRT在运行时可动态构建任务之间的基于生产者/消费者的数据依赖关系并遵循任务数据依赖状态执行调度,包括: 55 56- Producer-Consumer 依赖 57 58 一个数据对象版本的生产者任务和该数据对象版本的消费者任务之间形成的依赖关系,也称为Read-after-Write依赖。 59 60- Consumer-Producer 依赖 61 62 一个数据对象版本的消费者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Read依赖。 63 64- Producer-Producer 依赖 65 66 一个数据对象版本的生产者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Write依赖。 67 68 69例如,如果有这么一些任务,与数据A的关系表述为: 70```{.c} 71task1(OUT A); 72task2(IN A); 73task3(IN A); 74task4(OUT A); 75task5(OUT A); 76``` 77 78<img src="figures/ffrtfigure3.png" style="zoom:40%" /> 79 80> 为表述方便,本文中的数据流图均以圆圈表示 Task,方块表示数据。 81 82可以得出以下结论: 83- task1 与task2/task3 构成Producer-Consumer 依赖,即:task2/task3 需要等到task1 写完A之后才能读A。 84- task2/task3 与task4 构成Consumer-Producer 依赖,即:task4 需要等到task2/task3 读完A之后才能写A。 85- task4 与task5 构成Producer-Producer 依赖,即:task5 需要等到task4 写完A之后才能写A。 86 87## 接口说明 88 89| 接口名 | 描述 | 90| ------------------------------------------------------------ | ------------------------------------------------------------ | 91| ffrt_condattr_init (ffrt_condattr_t* attr) | 初始化条件变量属性。 | 92| ffrt_condattr_destroy(ffrt_condattr_t* attr) | 销毁条件变量属性。 | 93| ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock) | 设置条件变量的时钟属性。 | 94| ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock) | 获取条件变量的时钟属性。 | 95| ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr) | 初始化条件变量。 | 96| ffrt_cond_signal(ffrt_cond_t* cond) | 唤醒阻塞在条件变量上的一个任务。 | 97| ffrt_cond_broadcast(ffrt_cond_t* cond) | 唤醒阻塞在条件变量上的所有任务。 | 98| ffrt_cond_wait(ffrt_cond_t* cond, ffrt_mutex_t* mutex) | 条件变量等待函数,条件变量不满足时阻塞当前任务。 | 99| ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point) | 条件变量超时等待函数,条件变量不满足时阻塞当前任务,超时等待返回。 | 100| ffrt_cond_destroy(ffrt_cond_t* cond) | 销毁条件变量。 | 101| ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr) | 初始化mutex。 | 102| ffrt_mutex_lock(ffrt_mutex_t* mutex) | 获取mutex。 | 103| ffrt_mutex_unlock(ffrt_mutex_t* mutex) | 释放mutex。 | 104| ffrt_mutex_trylock(ffrt_mutex_t* mutex) | 尝试获取mutex。 | 105| ffrt_mutex_destroy(ffrt_mutex_t* mutex) | 销毁mutex。 | 106| ffrt_queue_attr_init(ffrt_queue_attr_t* attr) | 初始化串行队列属性。 | 107| ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr) | 销毁串行队列属性。 | 108| ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos) | 设置串行队列qos属性。 | 109| ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr) | 获取串行队列qos属性。 | 110| ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) | 创建队列。 | 111| ffrt_queue_destroy(ffrt_queue_t queue) | 销毁队列。 | 112| ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr) | 提交一个任务到队列中调度执行。 | 113| ffrt_queue_submit_h(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr) | 提交一个任务到队列中调度执行,并返回任务句柄。 | 114| ffrt_queue_wait(ffrt_task_handle_t handle) | 等待队列中一个任务执行完成。 | 115| ffrt_queue_cancel(ffrt_task_handle_t handle) | 取消队列中一个任务。 | 116| ffrt_usleep(uint64_t usec) | 延迟usec微秒。 | 117| ffrt_yield(void) | 当前任务主动放权,让其他任务有机会调度执行。 | 118| ffrt_task_attr_init(ffrt_task_attr_t* attr) | 初始化任务属性。 | 119| ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name) | 设置任务名字。 | 120| ffrt_task_attr_get_name(const ffrt_task_attr_t* attr) | 获取任务名字。 | 121| ffrt_task_attr_destroy(ffrt_task_attr_t* attr) | 销毁任务属性。 | 122| ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos) | 设置任务qos。 | 123| ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr) | 获取任务qos。 | 124| ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us) | 设置任务延迟时间。 | 125| ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr) | 获取任务延迟时间。 | 126| ffrt_this_task_update_qos(ffrt_qos_t qos) | 更新任务qos。 | 127| ffrt_this_task_get_id(void) | 获取任务id。 | 128| ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind) | 申请函数执行结构的内存。 | 129| ffrt_submit_base(ffrt_function_header_t* f, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) | 提交任务调度执行。 | 130| ffrt_submit_h_base(ffrt_function_header_t* f, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) | 提交任务调度执行并返回任务句柄。 | 131| ffrt_task_handle_destroy(ffrt_task_handle_t handle) | 销毁任务句柄。 | 132| ffrt_skip(ffrt_task_handle_t handle) | 跳过指定任务。 | 133| ffrt_wait_deps(const ffrt_deps_t* deps) | 等待依赖的任务完成,当前任务开始执行。 | 134 135 136 137## 函数介绍 138 139 140### 任务管理 141 142#### ffrt_submit_base 143 144* 该接口为ffrt动态库的导出接口,基于此可以封装出C API ffrt_submit,满足二进制兼容。 145 146##### 声明 147 148```{.c} 149const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t); 150typedef enum { 151 ffrt_function_kind_general, 152 ffrt_function_kind_queue 153} ffrt_function_kind_t; 154 155void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind); 156 157typedef void(*ffrt_function_t)(void*); 158typedef struct { 159 ffrt_function_t exec; 160 ffrt_function_t destroy; 161 uint64_t reserve[2]; 162} ffrt_function_header_t; 163 164void ffrt_submit_base(ffrt_function_header_t* func, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr); 165``` 166 167##### 参数 168 169`kind` 170 171* function子类型,用于优化内部数据结构,默认使用ffrt_function_kind_general类型。 172 173`func` 174 175* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁。 176 177`in_deps` 178 179* 该参数是可选的。 180* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖。 181 182`out_deps` 183 184* 该参数是可选的。 185* 该参数用于描述该任务的输出依赖。 186* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发。 187 188`attr` 189 190* 该参数是可选的。 191* 该参数用于描述Task 的属性,比如qos 等,详见 [ffrt_task_attr_t](#ffrt_task_attr_t)章节。 192 193##### 返回值 194 195* 不涉及。 196 197##### 描述 198* 建议用户对ffrt_submit_base进行封装后调用,具体可参考样例。 199* **ffrt_submit_base作为底层能力,使用时需要满足以下限制:** 200 * ffrt_submit_base入参中的func指针只能通过ffrt_alloc_auto_managed_function_storage_base申请,且二者的调用需一一对应。 201 * ffrt_alloc_auto_managed_function_storage_base申请的内存为ffrt_auto_managed_function_storage_size字节,其生命周期归ffrt管理,在该task结束时,由FFRT自动释放,用户无需释放。 202* ffrt_function_header_t 中定义了两个函数指针: 203 * exec:用于描述该Task如何被执行,当FFRT需要执行该Task时由FFRT调用。 204 * destroy:用于描述该Task如何被销毁,当FFRT需要销毁该Task时由FFRT调用。 205 206##### 样例 207 208 209```{.c} 210template<class T> 211struct function { 212 template<class CT> 213 function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 214 ffrt_function_header_t header; 215 T closure; 216}; 217 218template<class T> 219void exec_function_wrapper(void* t) 220{ 221 auto f = (function<std::decay_t<T>>*)t; 222 f->closure(); 223} 224 225template<class T> 226void destroy_function_wrapper(void* t) 227{ 228 auto f = (function<std::decay_t<T>>*)t; 229 f->closure = nullptr; 230} 231 232template<class T> 233inline ffrt_function_header_t* create_function_wrapper(T&& func) 234{ 235 using function_type = function<std::decay_t<T>>; 236 static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size, 237 "size of function must be less than ffrt_auto_managed_function_storage_size"); 238 239 auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 240 auto f = new (p) function_type( 241 {exec_function_wrapper<T>, destroy_function_wrapper<T>}, 242 std::forward<T>(func)); 243 return (ffrt_function_header_t*)f; 244} 245 246static inline void submit(std::function<void()>&& func) 247{ 248 return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL); 249} 250``` 251 252#### ffrt_wait 253 254<hr/> 255* 同步等待,与ffrt_submit_base 配合使用。 256* 等待指定的数据被生产完成,或等待当前任务的所有子任务完成,在不满足条件之前,当前的执行上下文被suspend,在满足条件后恢复执行。 257 258##### 声明 259 260```{.c} 261void ffrt_wait_deps(ffrt_deps_t* deps); 262void ffrt_wait(); 263``` 264 265##### 参数 266 267`deps` 268 269* 需要等待被生产完成的数据的虚拟地址,这些地址可能作为某些任务在submit 时的out_deps,该依赖的生成见ffrt_deps_t章节,空指针表示无依赖。 270 271##### 返回值 272 273* 不涉及。 274 275##### 描述 276* ffrt_wait_deps(deps) 用于等待deps指代的数据被生产完成才能执行后面的代码。 277* ffrt_wait() 用于等待当前上下文提交的所有子任务(`注意:不包括孙任务和下级子任务`)都完成才能执行后面的代码。 278* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用。 279* 在FFRT task 外部调用的wait 是OS 能够感知的等待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部。 280 281##### 样例 282 283**recursive fibonacci** 284 285串行版的fibonacci 可以实现为: 286 287```{.c} 288#include <stdio.h> 289 290void fib(int x, int* y) { 291 if (x <= 1) { 292 *y = x; 293 } else { 294 int y1, y2; 295 fib(x - 1, &y1); 296 fib(x - 2, &y2); 297 *y = y1 + y2; 298 } 299} 300int main(int narg, char** argv) 301{ 302 int r; 303 fib(10, &r); 304 printf("fibonacci 10: %d\n", r); 305 return 0; 306} 307``` 308 309若要使用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加速的意义,但这种调用pattern 对并行编程模型的灵活性考验是非常高的),其中1种可行的实现为: 310 311```{.c} 312#include <stdio.h> 313#include "ffrt.h" //包含所有ffrt涉及的头文件 314 315typedef struct { 316 int x; 317 int* y; 318} fib_ffrt_s; 319 320typedef struct { 321 ffrt_function_header_t header; 322 ffrt_function_t func; 323 ffrt_function_t after_func; 324 void* arg; 325} c_function; 326 327static void ffrt_exec_function_wrapper(void* t) 328{ 329 c_function* f = (c_function*)t; 330 if (f->func) { 331 f->func(f->arg); 332 } 333} 334 335static void ffrt_destroy_function_wrapper(void* t) 336{ 337 c_function* f = (c_function*)t; 338 if (f->after_func) { 339 f->after_func(f->arg); 340 } 341} 342 343#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 344static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 345 const ffrt_function_t after_func, void* arg) 346{ 347 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 348 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 349 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 350 f->header.exec = ffrt_exec_function_wrapper; 351 f->header.destroy = ffrt_destroy_function_wrapper; 352 f->func = func; 353 f->after_func = after_func; 354 f->arg = arg; 355 return (ffrt_function_header_t*)f; 356} 357 358static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 359 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 360{ 361 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 362} 363 364#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \ 365 ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name} 366 367void fib_ffrt(void* arg) 368{ 369 fib_ffrt_s* p = (fib_ffrt_s*)arg; 370 int x = p->x; 371 int* y = p->y; 372 373 if (x <= 1) { 374 *y = x; 375 } else { 376 int y1, y2; 377 fib_ffrt_s s1 = {x - 1, &y1}; 378 fib_ffrt_s s2 = {x - 2, &y2}; 379 ffrt_deps_define(dx, &x); 380 ffrt_deps_define(dy1, &y1); 381 ffrt_deps_define(dy2, &y2); 382 ffrt_deps_define(dy12, &y1, &y2); 383 ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL); 384 ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL); 385 ffrt_wait_deps(&dy12); 386 *y = y1 + y2; 387 } 388} 389 390int main(int narg, char** argv) 391{ 392 int r; 393 fib_ffrt_s s = {10, &r}; 394 ffrt_deps_define(dr, &r); 395 ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL); 396 ffrt_wait_deps(&dr); 397 printf("fibonacci 10: %d\n", r); 398 return 0; 399} 400``` 401 402`解析`: 403 4041) 将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将结果累加; 405 4062) 虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继续拆分,因此,整个计算图的并行度是非常高的,Task 之间在FFRT 内部形成了一颗调用树; 407 408<img src="figures/ffrtfigure2.png" style="zoom:100%" /> 409 410> 以上实现,因为需要用户显式管理数据生命周期和函数入参打包两个因素,所以使得代码实现异常复杂。 411 412 413 414#### ffrt_deps_t 415 416* C API中对依赖数组的抽象,逻辑上等同于C++ API中的`std::vector<void*>` 417 418##### 声明 419 420```{.c} 421typedef enum { 422 ffrt_dependence_data, 423 ffrt_dependence_task, 424} ffrt_dependence_type_t; 425 426typedef struct { 427 ffrt_dependence_type_t type; 428 const void* ptr; 429} ffrt_dependence_t; 430 431typedef struct { 432 uint32_t len; 433 const ffrt_dependence_t* items; 434} ffrt_deps_t; 435``` 436 437##### 参数 438 439`len` 440 441* 所依赖的Signature的个数,取值大于等于0。 442 443`item` 444 445* len个Signature的起始地址指针。 446 447`type` 448 449* 当前依赖为数据依赖还是任务依赖。 450 451`ptr` 452 453* 所依赖对应Signature内容的实际地址。 454 455##### 返回值 456 457* 不涉及。 458 459##### 描述 460 461* item为len个Signature的起始指针,该指针可以指向堆空间,也可以指向栈空间,但是要求分配的空间大于等于len * sizeof(ffrt_dependence_t)。 462 463##### 样例 464 465* 创建数据依赖或者任务依赖: 466 467```{.c} 468// 创建数据依赖的ffrt_deps_t 469int x = 0; 470const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 471ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 472 473// 提交某个返回handle任务 474ffrt_task_handle_t task = ffrt_submit_h_base( 475 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 476// 创建任务依赖的ffrt_deps_t 477const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 478ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 479``` 480 481#### ffrt_task_attr_t 482 483<hr/> 484* 定义task 的属性的辅助类,与ffrt_submit_base 配合使用。 485 486##### 声明 487 488```{.c} 489typedef enum { 490 ffrt_qos_inherent = -1, 491 ffrt_qos_background, 492 ffrt_qos_utility, 493 ffrt_qos_default, 494 ffrt_qos_user_initiated, 495} ffrt_qos_default_t; 496 497typedef int ffrt_qos_t; 498 499typedef struct { 500 uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 501} ffrt_task_attr_t; 502typedef void* ffrt_task_handle_t; 503 504int ffrt_task_attr_init(ffrt_task_attr_t* attr); 505void ffrt_task_attr_destroy(ffrt_task_attr_t* attr); 506void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos); 507ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr); 508void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name); 509const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr); 510void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us); 511uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr); 512``` 513 514##### 参数 515 516`attr` 517 518* 创建的tasks属性的句柄。 519 520`qos` 521 522* qos 设定的枚举类型。 523* inherent 是一个qos 设定策略,代表即将ffrt_submit 的task 的qos 继承当前task 的qos。 524 525`delay_us` 526 527* 任务延迟执行的时间,单位为us。 528 529##### 返回值 530 531* 不涉及。 532 533##### 描述 534* `attr`所传递的内容会在ffrt_submit内部完成取存,ffrt_submit返回后用户即可销毁。 535* 约定: 536 * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`ffrt_qos_default`。 537 * 在submit 时,如果通过task_attr 设定qos 为`ffrt_qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`ffrt_qos_inherent` 的task,其qos 为`ffrt_qos_default`。 538 * 其他情况下,该提交的task 的qos 被设定为指定的值。 539* ffrt_task_attr_t对象的置空和销毁由用户完成,对同一个ffrt_task_attr_t仅能调用一次`ffrt_task_attr_destroy`,重复对同一个ffrt_task_attr_t调用`ffrt_task_attr_destroy`,其行为是未定义的。 540* 在`ffrt_task_attr_destroy`之后再对task_attr进行访问,其行为是未定义的。 541 542##### 样例 543 544* 提交一个qos 级别为background 的任务: 545 546```{.c} 547#include <stdio.h> 548#include "ffrt.h" 549 550void my_print(void* arg) 551{ 552 printf("hello ffrt\n"); 553} 554 555typedef struct { 556 ffrt_function_header_t header; 557 ffrt_function_t func; 558 ffrt_function_t after_func; 559 void* arg; 560} c_function; 561 562static void ffrt_exec_function_wrapper(void* t) 563{ 564 c_function* f = (c_function*)t; 565 if (f->func) { 566 f->func(f->arg); 567 } 568} 569 570static void ffrt_destroy_function_wrapper(void* t) 571{ 572 c_function* f = (c_function*)t; 573 if (f->after_func) { 574 f->after_func(f->arg); 575 } 576} 577 578#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 579static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 580 const ffrt_function_t after_func, void* arg) 581{ 582 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 583 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 584 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 585 f->header.exec = ffrt_exec_function_wrapper; 586 f->header.destroy = ffrt_destroy_function_wrapper; 587 f->func = func; 588 f->after_func = after_func; 589 f->arg = arg; 590 return (ffrt_function_header_t*)f; 591} 592 593static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 594 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 595{ 596 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 597} 598 599int main(int narg, char** argv) 600{ 601 ffrt_task_attr_t attr; 602 ffrt_task_attr_init(&attr); 603 ffrt_task_attr_set_qos(&attr, ffrt_qos_background); 604 ffrt_task_attr_set_delay(&attr, 10000); 605 ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr); 606 ffrt_task_attr_destroy(&attr); 607 ffrt_wait(); 608 return 0; 609} 610``` 611 612 613 614 615#### ffrt_submit_h_base 616 617<hr/> 618 619* 向调度器提交一个task,与ffrt_submit_base 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。 620 621##### 声明 622 623```{.c} 624typedef void* ffrt_task_handle_t; 625 626ffrt_task_handle_t ffrt_submit_h_base(ffrt_function_t func, void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr); 627void ffrt_task_handle_destroy(ffrt_task_handle_t handle); 628``` 629 630##### 参数 631 632`func` 633 634* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁。 635 636`in_deps` 637 638* 该参数是可选的。 639* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖。 640 641`out_deps` 642 643* 该参数是可选的。 644* 该参数用于描述该任务的输出依赖。 645* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发。 646 647`attr` 648 649* 该参数是可选的。 650* 该参数用于描述Task 的属性,比如qos 等,详见 [ffrt_task_attr_t](#ffrt_task_attr_t)章节。 651 652##### 返回值 653 654* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。 655 656##### 描述 657 658* **C API中的ffrt_task_handle_t需要用户调用`ffrt_task_handle_destroy`显式销毁。** 659* C API中的task_handle_t对象的置空和销毁由用户完成,对同一个ffrt_task_handle_t仅能调用一次`ffrt_task_handle_destroy`,重复对同一个ffrt_task_handle_t调用`ffrt_task_handle_destroy`,其行为是未定义的。 660* 在`ffrt_task_handle_destroy`之后再对ffrt_task_handle_t进行访问,其行为是未定义的。 661 662##### 样例 663 664```{.c} 665#include <stdio.h> 666#include "ffrt.h" 667 668void func0(void* arg) 669{ 670 printf("hello "); 671} 672 673void func1(void* arg) 674{ 675 (*(int*)arg)++; 676} 677 678void func2(void* arg) 679{ 680 printf("world, x = %d\n", *(int*)arg); 681} 682 683void func3(void* arg) 684{ 685 printf("handle wait"); 686 (*(int*)arg)++; 687} 688 689typedef struct { 690 ffrt_function_header_t header; 691 ffrt_function_t func; 692 ffrt_function_t after_func; 693 void* arg; 694} c_function; 695 696static void ffrt_exec_function_wrapper(void* t) 697{ 698 c_function* f = (c_function*)t; 699 if (f->func) { 700 f->func(f->arg); 701 } 702} 703 704static void ffrt_destroy_function_wrapper(void* t) 705{ 706 c_function* f = (c_function*)t; 707 if (f->after_func) { 708 f->after_func(f->arg); 709 } 710} 711 712#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 713static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 714 const ffrt_function_t after_func, void* arg) 715{ 716 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 717 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 718 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 719 f->header.exec = ffrt_exec_function_wrapper; 720 f->header.destroy = ffrt_destroy_function_wrapper; 721 f->func = func; 722 f->after_func = after_func; 723 f->arg = arg; 724 return (ffrt_function_header_t*)f; 725} 726 727static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func, 728 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 729{ 730 return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 731} 732 733static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 734 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 735{ 736 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 737} 738 739 740int main(int narg, char** argv) 741{ 742 // handle work with submit 743 ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task 744 int x = 1; 745 const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 746 ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 747 748 const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}}; 749 ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()}; 750 751 ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL); 752 ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h 753 ffrt_task_handle_destroy(h); 754 755 // handle work with wait 756 ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL); 757 758 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}}; 759 ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 760 ffrt_wait_deps(&d3); 761 ffrt_task_handle_destroy(h2); 762 printf("x = %d", x); 763 ffrt_wait(); 764 return 0; 765} 766``` 767 768* 预期的输出为: 769 770``` 771hello world, x = 2 772handle wait 773x = 3 774``` 775 776 777 778#### ffrt_this_task_get_id 779 780<hr/> 781 782* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)。 783 784##### 声明 785 786```{.c} 787uint64_t ffrt_this_task_get_id(); 788``` 789 790##### 参数 791 792* 不涉及。 793 794##### 返回值 795 796* 当前task的id。 797 798##### 描述 799 800* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0。 801* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上。 802* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转。 803 804##### 样例 805 806* 忽略。 807 808 809 810#### ffrt_this_task_update_qos 811 812<hr/> 813 814* 更新当前正在执行的task的优先级。 815 816##### 声明 817 818```{.c} 819int ffrt_this_task_update_qos(ffrt_qos_t qos); 820``` 821 822##### 参数 823 824* `qos` 新的优先级。 825 826##### 返回值 827 828* 0表示成功,非0表示失败。 829 830##### 描述 831 832* 该接口对当前task的qos调整会立即生效。 833* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行。 834* 如果新设定的qos与当前的qos一致,则接口会立即返回0,不做任何处理。 835* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理。** 836 837##### 样例 838 839* 忽略。 840 841### 串行队列 842<hr /> 843* FFRT提供queue来实现Andorid中类似WorkQueue能力,且在使用得当的情况下将有更好的性能。 844 845#### ffrt_queue_attr_t 846 847##### 声明 848```{.c} 849typedef struct { 850 uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 851} ffrt_queue_attr_t; 852 853int ffrt_queue_attr_init(ffrt_queue_attr_t* attr); 854void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr); 855``` 856 857##### 参数 858 859`attr` 860* 该参数是指向未初始化的ffrt_queue_attr_t。 861 862##### 返回值 863* 若成功返回0,否则返回-1。 864 865##### 描述 866* ffrt_queue_attr_t用于创建ffrt_queue_t且不单独使用,因此必须在创建队列前先创建好队列属性。 867* ffrt_queue_attr_t对象的置空和销毁由用户完成,对同一个ffrt_queue_attr_t仅能调用一次`ffrt_queue_attr_destroy`,重复对同一个ffrt_queue_attr_t调用`ffrt_queue_attr_destroy`,其行为是未定义的。 868* 在`ffrt_queue_attr_destroy`之后再对ffrt_queue_attr_t进行访问,其行为是未定义的。 869 870##### 样例 871参考ffrt_queue_t章节的样例。 872 873#### ffrt_queue_t 874 875##### 声明 876```{.c} 877typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t; 878typedef void* ffrt_queue_t; 879 880ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) 881void ffrt_queue_destroy(ffrt_queue_t queue) 882``` 883 884##### 参数 885 886`type` 887* 该参数用于描述创建的队列类型。 888 889`name` 890* 该参数用于描述创建队列的名字。 891 892`attr` 893* 该参数用于描述queue的属性,详见ffrt_queue_attr_t章节。 894 895##### 返回值 896* 若成功则返回新创建的队列,否则返回空指针。 897 898##### 描述 899* 提交至该队列的任务将按照顺序执行,如果某个提交的任务中发生阻塞,则无法保证该任务的执行顺序。 900* ffrt_queue_t对象的置空和销毁由用户完成,对同一个ffrt_queue_t仅能调用一次`ffrt_queue_destroy`,重复对同一个ffrt_queue_t调用`ffrt_queue_destroy`,其行为是未定义的。 901* 在`ffrt_queue_destroy`之后再对ffrt_queue_t进行访问,其行为是未定义的。 902 903##### 样例 904``` 905#include <stdio.h> 906#include "ffrt.h" 907 908using namespace std; 909 910template<class T> 911struct Function { 912 template<class CT> 913 Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 914 ffrt_function_header_t header; 915 T closure; 916}; 917 918template<class T> 919void ExecFunctionWrapper(void* t) 920{ 921 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 922 f->closure(); 923} 924 925template<class T> 926void DestroyFunctionWrapper(void* t) 927{ 928 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 929 f->closure = nullptr; 930} 931 932template<class T> 933static inline ffrt_function_header_t* create_function_wrapper(T&& func, 934 ffrt_function_kind_t kind = ffrt_function_kind_general) 935{ 936 using function_type = Function<std::decay_t<T>>; 937 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 938 auto f = 939 new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func)); 940 return reinterpret_cast<ffrt_function_header_t*>(f); 941} 942 943int main(int narg, char** argv) 944{ 945 ffrt_queue_attr_t queue_attr; 946 (void)ffrt_queue_attr_init(&queue_attr); 947 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 948 949 ffrt_queue_submit(queue_handle, create_function_wrapper([]() {printf("Task done.\n");}, ffrt_function_kind_queue), nullptr); 950 951 ffrt_queue_attr_destroy(&queue_attr); 952 ffrt_queue_destroy(queue_handle); 953} 954``` 955### 同步原语 956 957#### ffrt_mutex_t 958<hr/> 959* FFRT提供的类似pthread mutex 的性能实现。 960 961##### 声明 962 963```{.c} 964typedef enum { 965 ffrt_error = -1, 966 ffrt_success = 0, 967 ffrt_error_nomem = ENOMEM, 968 ffrt_error_timedout = ETIMEDOUT, 969 ffrt_error_busy = EBUSY, 970 ffrt_error_inval = EINVAL 971} ffrt_error_t; 972 973struct ffrt_mutex_t; 974 975int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr); 976int ffrt_mutex_lock(ffrt_mutex_t* mutex); 977int ffrt_mutex_unlock(ffrt_mutex_t* mutex); 978int ffrt_mutex_trylock(ffrt_mutex_t* mutex); 979int ffrt_mutex_destroy(ffrt_mutex_t* mutex); 980``` 981 982##### 参数 983 984`attr` 985 986* 当前FFRT只支持基础类型的mutex,因此attr必须为空指针。 987 988`mutex` 989 990* 指向所操作的互斥锁的指针。 991 992##### 返回值 993 994* 若成功则为 ffrt_success ,否则发生错误。 995 996##### 描述 997* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 998* 该功能能够避免pthread传统的pthread_mutex_t 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能。 999* **注意:目前暂不支持递归和定时功能。** 1000* **注意:C API中的ffrt_mutex_t需要用户调用`ffrt_mutex_init`和`ffrt_mutex_destroy`显式创建和销毁。** 1001* **注意:C API中的ffrt_mutex_t对象的置空和销毁由用户完成,对同一个ffrt_mutex_t仅能调用一次`ffrt_mutex_destroy`,重复对同一个ffrt_mutex_t调用`ffrt_mutex_destroy`,其行为是未定义的。** 1002* **注意:在`ffrt_mutex_destroy`之后再对ffrt_mutex_t进行访问,其行为是未定义的。** 1003 1004##### 样例 1005 1006```{.c} 1007#include <stdio.h> 1008#include "ffrt.h" 1009 1010typedef struct { 1011 int* sum; 1012 ffrt_mutex_t* mtx; 1013} tuple; 1014 1015void func(void* arg) 1016{ 1017 tuple* t = (tuple*)arg; 1018 1019 int ret = ffrt_mutex_lock(t->mtx); 1020 if (ret != ffrt_success) { 1021 printf("error\n"); 1022 } 1023 (*t->sum)++; 1024 ret = ffrt_mutex_unlock(t->mtx); 1025 if (ret != ffrt_success) { 1026 printf("error\n"); 1027 } 1028} 1029 1030typedef struct { 1031 ffrt_function_header_t header; 1032 ffrt_function_t func; 1033 ffrt_function_t after_func; 1034 void* arg; 1035} c_function; 1036 1037static void ffrt_exec_function_wrapper(void* t) 1038{ 1039 c_function* f = (c_function*)t; 1040 if (f->func) { 1041 f->func(f->arg); 1042 } 1043} 1044 1045static void ffrt_destroy_function_wrapper(void* t) 1046{ 1047 c_function* f = (c_function*)t; 1048 if (f->after_func) { 1049 f->after_func(f->arg); 1050 } 1051} 1052 1053#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1054static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1055 const ffrt_function_t after_func, void* arg) 1056{ 1057 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1058 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1059 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1060 f->header.exec = ffrt_exec_function_wrapper; 1061 f->header.destroy = ffrt_destroy_function_wrapper; 1062 f->func = func; 1063 f->after_func = after_func; 1064 f->arg = arg; 1065 return (ffrt_function_header_t*)f; 1066} 1067 1068static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1069 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1070{ 1071 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1072} 1073 1074void ffrt_mutex_task() 1075{ 1076 int sum = 0; 1077 ffrt_mutex_t mtx; 1078 tuple t = {&sum, &mtx}; 1079 int ret = ffrt_mutex_init(&mtx, NULL); 1080 if (ret != ffrt_success) { 1081 printf("error\n"); 1082 } 1083 for (int i = 0; i < 10; i++) { 1084 ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL); 1085 } 1086 ffrt_mutex_destroy(&mtx); 1087 ffrt_wait(); 1088 printf("sum = %d", sum); 1089} 1090 1091int main(int narg, char** argv) 1092{ 1093 int r; 1094 ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL); 1095 ffrt_wait(); 1096 return 0; 1097} 1098``` 1099 1100预期输出为: 1101 1102``` 1103sum=10 1104``` 1105 1106* 该例子为功能示例,实际中并不鼓励这样使用。 1107 1108 1109#### ffrt_cond_t 1110<hr/> 1111 1112* FFRT提供的类似pthread 信号量的性能实现。 1113 1114##### 声明 1115 1116```{.c} 1117typedef enum { 1118 ffrt_error = -1, 1119 ffrt_success = 0, 1120 ffrt_error_nomem = ENOMEM, 1121 ffrt_error_timedout = ETIMEDOUT, 1122 ffrt_error_busy = EBUSY, 1123 ffrt_error_inval = EINVAL 1124} ffrt_error_t; 1125 1126struct ffrt_cond_t; 1127 1128int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr); 1129int ffrt_cond_signal(ffrt_cond_t* cond); 1130int ffrt_cond_broadcast(ffrt_cond_t* cond); 1131int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex); 1132int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point); 1133int ffrt_cond_destroy(ffrt_cond_t* cond); 1134``` 1135 1136##### 参数 1137 1138`cond` 1139 1140* 指向所操作的信号量的指针。 1141 1142`attr` 1143 1144* 属性设定,空指针表示使用默认属性。 1145 1146`mutex` 1147 1148* 指向要在阻塞期间解锁的互斥锁的指针。 1149 1150`time_point` 1151 1152* 指向指定等待时限时间的对象的指针。 1153 1154 1155##### 返回值 1156 1157* 若成功则为 ffrt_success,若在锁定互斥前抵达时限则为 ffrt_error_timedout。 1158 1159##### 描述 1160* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1161* 该功能能够避免传统的pthread_cond_t在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能。 1162* **注意:C API中的ffrt_cond_t需要用户调用`ffrt_cond_init`和`ffrt_cond_destroy`显式创建和销毁。** 1163* **注意:C API中的ffrt_cond_t对象的置空和销毁由用户完成,对同一个ffrt_cond_t仅能调用一次`ffrt_cond_destroy`,重复对同一个ffrt_cond_t调用`ffrt_cond_destroy`,其行为是未定义的。** 1164* **注意:在`ffrt_cond_destroy`之后再对ffrt_cond_t进行访问,其行为是未定义的。** 1165 1166##### 样例 1167 1168```{.c} 1169#include <stdio.h> 1170#include "ffrt.h" 1171 1172typedef struct { 1173 ffrt_cond_t* cond; 1174 int* a; 1175 ffrt_mutex_t* lock_; 1176} tuple; 1177 1178void func1(void* arg) 1179{ 1180 tuple* t = (tuple*)arg; 1181 int ret = ffrt_mutex_lock(t->lock_); 1182 if (ret != ffrt_success) { 1183 printf("error\n"); 1184 } 1185 while (*t->a != 1) { 1186 ret = ffrt_cond_wait(t->cond, t->lock_); 1187 if (ret != ffrt_success) { 1188 printf("error\n"); 1189 } 1190 } 1191 ret = ffrt_mutex_unlock(t->lock_); 1192 if (ret != ffrt_success) { 1193 printf("error\n"); 1194 } 1195 printf("a = %d", *(t->a)); 1196} 1197 1198void func2(void* arg) 1199{ 1200 tuple* t = (tuple*)arg; 1201 int ret = ffrt_mutex_lock(t->lock_); 1202 if (ret != ffrt_success) { 1203 printf("error\n"); 1204 } 1205 *(t->a) = 1; 1206 ret = ffrt_cond_signal(t->cond); 1207 if (ret != ffrt_success) { 1208 printf("error\n"); 1209 } 1210 ret = ffrt_mutex_unlock(t->lock_); 1211 if (ret != ffrt_success) { 1212 printf("error\n"); 1213 } 1214} 1215 1216typedef struct { 1217 ffrt_function_header_t header; 1218 ffrt_function_t func; 1219 ffrt_function_t after_func; 1220 void* arg; 1221} c_function; 1222 1223static void ffrt_exec_function_wrapper(void* t) 1224{ 1225 c_function* f = (c_function*)t; 1226 if (f->func) { 1227 f->func(f->arg); 1228 } 1229} 1230 1231static void ffrt_destroy_function_wrapper(void* t) 1232{ 1233 c_function* f = (c_function*)t; 1234 if (f->after_func) { 1235 f->after_func(f->arg); 1236 } 1237} 1238 1239#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1240static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1241 const ffrt_function_t after_func, void* arg) 1242{ 1243 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1244 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1245 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1246 f->header.exec = ffrt_exec_function_wrapper; 1247 f->header.destroy = ffrt_destroy_function_wrapper; 1248 f->func = func; 1249 f->after_func = after_func; 1250 f->arg = arg; 1251 return (ffrt_function_header_t*)f; 1252} 1253 1254static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1255 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1256{ 1257 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1258} 1259 1260void ffrt_cv_task() 1261{ 1262 ffrt_cond_t cond; 1263 int ret = ffrt_cond_init(&cond, NULL); 1264 if (ret != ffrt_success) { 1265 printf("error\n"); 1266 } 1267 int a = 0; 1268 ffrt_mutex_t lock_; 1269 tuple t = {&cond, &a, &lock_}; 1270 ret = ffrt_mutex_init(&lock_, NULL); 1271 if (ret != ffrt_success) { 1272 printf("error\n"); 1273 } 1274 ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL); 1275 ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL); 1276 ffrt_wait(); 1277 ffrt_cond_destroy(&cond); 1278 ffrt_mutex_destroy(&lock_); 1279} 1280 1281int main(int narg, char** argv) 1282{ 1283 ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL); 1284 ffrt_wait(); 1285 return 0; 1286} 1287``` 1288 1289预期输出为: 1290 1291``` 1292a=1 1293``` 1294 1295* 该例子为功能示例,实际中并不鼓励这样使用。 1296 1297### 杂项 1298 1299#### ffrt_usleep 1300 1301<hr/> 1302* FFRT提供的类似C11 sleep和linux usleep的性能实现。 1303 1304##### 声明 1305 1306```{.c} 1307int ffrt_usleep(uint64_t usec); 1308``` 1309 1310##### 参数 1311 1312`usec` 1313 1314* 睡眠的us数。 1315 1316##### 返回值 1317 1318* 不涉及。 1319 1320##### 描述 1321* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1322* 该功能能够避免传统的sleep 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能。 1323 1324##### 样例 1325 1326```{.c} 1327#include <time.h> 1328#include <stdio.h> 1329#include "ffrt.h" 1330 1331void func(void* arg) 1332{ 1333 printf("Time: %s", ctime(&(time_t){time(NULL)})); 1334 ffrt_usleep(2000000); // 睡眠 2 秒 1335 printf("Time: %s", ctime(&(time_t){time(NULL)})); 1336} 1337 1338typedef struct { 1339 ffrt_function_header_t header; 1340 ffrt_function_t func; 1341 ffrt_function_t after_func; 1342 void* arg; 1343} c_function; 1344 1345static void ffrt_exec_function_wrapper(void* t) 1346{ 1347 c_function* f = (c_function*)t; 1348 if (f->func) { 1349 f->func(f->arg); 1350 } 1351} 1352 1353static void ffrt_destroy_function_wrapper(void* t) 1354{ 1355 c_function* f = (c_function*)t; 1356 if (f->after_func) { 1357 f->after_func(f->arg); 1358 } 1359} 1360 1361#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1362static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1363 const ffrt_function_t after_func, void* arg) 1364{ 1365 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1366 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1367 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1368 f->header.exec = ffrt_exec_function_wrapper; 1369 f->header.destroy = ffrt_destroy_function_wrapper; 1370 f->func = func; 1371 f->after_func = after_func; 1372 f->arg = arg; 1373 return (ffrt_function_header_t*)f; 1374} 1375 1376static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1377 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1378{ 1379 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1380} 1381 1382int main(int narg, char** argv) 1383{ 1384 ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL); 1385 ffrt_wait(); 1386 return 0; 1387} 1388``` 1389 1390#### ffrt_yield 1391<hr/> 1392* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效。 1393 1394##### 声明 1395 1396```{.c} 1397void ffrt_yield(); 1398``` 1399 1400##### 参数 1401 1402* 不涉及。 1403 1404##### 返回值 1405 1406* 不涉及。 1407 1408##### 描述 1409* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1410* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态。 1411 1412##### 样例 1413 1414* 省略。 1415 1416 1417## 开发步骤 1418 1419以下步骤描述了如何使用`FFRT`提供的Native API接口,创建并行任务和串行队列任务以及销毁相应资源。 1420 1421**添加动态链接库** 1422 1423CMakeLists.txt中添加以下lib: 1424```txt 1425libffrt.z.so 1426``` 1427 1428**头文件** 1429```c++ 1430#include "ffrt/task.h" 1431#include "ffrt/type_def.h" 1432#include "ffrt/condition_variable.h" 1433#include "ffrt/mutex.h" 1434#include "ffrt/queue.h" 1435#include "ffrt/sleep.h" 1436``` 1437 14381. **首先需要对执行的函数进行封装**。 1439 ```c++ 1440 // 第一种使用模板,支持C++ 1441 template<class T> 1442 struct Function { 1443 template<class CT> 1444 Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {} 1445 ffrt_function_header_t header; 1446 T closure; 1447 }; 1448 1449 template<class T> 1450 void ExecFunctionWrapper(void* t) 1451 { 1452 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1453 f->closure(); 1454 } 1455 1456 template<class T> 1457 void DestroyFunctionWrapper(void* t) 1458 { 1459 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1460 f->closure = nullptr; 1461 } 1462 1463 template<class T> 1464 static inline ffrt_function_header_t* create_function_wrapper(T&& func, 1465 ffrt_function_kind_t kind = ffrt_function_kind_general) 1466 { 1467 using function_type = Function<std::decay_t<T>>; 1468 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 1469 auto f = 1470 new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func)); 1471 return reinterpret_cast<ffrt_function_header_t*>(f); 1472 } 1473 1474 // 第二种创建方式 1475 typedef struct { 1476 ffrt_function_header_t header; 1477 ffrt_function_t func; 1478 ffrt_function_t after_func; 1479 void* arg; 1480 } CFunction; 1481 1482 static void FfrtExecFunctionWrapper(void* t) 1483 { 1484 CFunction* f = static_cast<CFunction*>(t); 1485 if (f->func) { 1486 f->func(f->arg); 1487 } 1488 } 1489 1490 static void FfrtDestroyFunctionWrapper(void* t) 1491 { 1492 CFunction* f = static_cast<CFunction*>(t); 1493 if (f->after_func) { 1494 f->after_func(f->arg); 1495 } 1496 } 1497 1498 #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1499 static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1500 const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general) 1501 { 1502 FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size, 1503 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1504 CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t)); 1505 f->header.exec = FfrtExecFunctionWrapper; 1506 f->header.destroy = FfrtDestroyFunctionWrapper; 1507 f->func = func; 1508 f->after_func = after_func; 1509 f->arg = arg; 1510 return reinterpret_cast<ffrt_function_header_t*>(f); 1511 } 1512 1513 // 样例:待提交执行的函数 1514 void OnePlusForTest(void* arg) 1515 { 1516 (*static_cast<int*>(arg)) += 1; 1517 } 1518 ``` 1519 15202. **设置task属性值**。 1521 1522 用户提交任务时可以设置任务属性,包括qos优先级,名称等,具体可参考接口文档。 1523 ```c++ 1524 // ******初始化并行任务属性****** 1525 ffrt_task_attr_t attr; 1526 ffrt_task_attr_init(&attr); 1527 1528 // ******创建串行队列****** 1529 1530 // 创建串行队列的属性 1531 ffrt_queue_attr_t queue_attr; 1532 // 创建串行队列的handle 1533 ffrt_queue_t queue_handle; 1534 1535 // 初始化队列属性 1536 (void)ffrt_queue_attr_init(&queue_attr); 1537 1538 // 如有需要,设置指定优先级 1539 ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit)); 1540 // 如有需要,设置超时时间(ms) 1541 ffrt_queue_attr_set_timeout(&queue_attr, 10000); 1542 // 如有需要,设置超时回调 1543 int x = 0; 1544 ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x, 1545 ffrt_function_kind_queue)); 1546 1547 // 基于属性,初始化队列 1548 queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 1549 ``` 1550 15513. **提交任务**。 1552 ```c++ 1553 int a = 0; 1554 // ******并行任务****** 1555 // 提交不带handle返回值的并行任务 1556 ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1557 // 提交带handle返回值的并行任务 1558 ffrt_task_handle_t task = ffrt_submit_h_base( 1559 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1560 1561 // ******串行任务****** 1562 // 提交不返回handle的串行队列任务 1563 ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, 1564 ffrt_function_kind_queue), nullptr); 1565 // 提交带handle的串行队列任务 1566 ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle, 1567 ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr); 1568 1569 // 如果需要等待执行结果,则调用wait 1570 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 1571 ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 1572 ffrt_wait_deps(&wait); 1573 1574 ffrt_queue_wait(handle); 1575 ``` 1576 15774. **任务提交完成后销毁相应资源**。 1578 ```c++ 1579 // ******销毁并行任务****** 1580 ffrt_task_attr_destroy(&attr); 1581 ffrt_task_handle_destroy(task); 1582 1583 // ******销毁串行队列任务****** 1584 // 先销毁任务handle,再销毁队列 1585 ffrt_queue_attr_destroy(&queue_attr); 1586 ffrt_task_handle_destroy(handle); 1587 ffrt_queue_destroy(queue_handle); 1588 ``` 1589 1590## 使用建议 1591 1592### 建议1: 函数化 1593 1594**基本思想:计算过程函数化** 1595 1596* 程序过程各步骤以函数封装表达,函数满足类纯函数特性。 1597* 无全局数据访问。 1598* 无内部状态保留。 1599* 通过ffrt_submit_base()接口以异步任务方式提交函数执行。 1600* 将函数访问的数据对象以及访问方式在ffrt_submit_base()接口中的in_deps/out_deps参数表达。 1601* 程序员通过inDeps/outDeps参数表达任务间依赖关系以保证程序执行的正确性。 1602 1603> 做到纯函数的好处在于:1. 能够最大化挖掘并行度,2.避免DataRace和锁的问题。 1604 1605 1606 1607**在实际中,可以根据场景放松纯函数的约束,但前提是:** 1608 1609* 确定添加的in_deps/out_deps可确保程序正确执行。 1610* 通过FFRT提供的锁机制保护对全局变量的访问。 1611 1612 1613### 建议2: 使用FFRT提供的替代API 1614 1615* 禁止在FFRT任务中使用系统线程库API创建线程,使用submit提交任务。 1616* 使用FFRT提供的锁,条件变量,睡眠,IO等API代替系统线程库API。 1617 * 使用系统线程库API可能造成工作线程阻塞,引起额外性能开销。 1618 1619 1620 1621### 建议3: Deadline机制 1622 1623* **必须用于具备周期/重复执行特征的处理流程。** 1624* 在有明确时间约束和性能关键的处理流程中使用,避免滥用。 1625* 在相对大颗粒度的处理流程中使用,例如具有16.6ms时间约束的帧处理流程。 1626 1627 1628 1629### 建议4: 从线程模型迁移 1630 1631* 创建线程替代为创建FFRT任务。 1632 * 线程从逻辑上类似无in_deps的任务。 1633* 识别线程间的依赖关系,并将其表达在任务的依赖关系in_deps/out_deps上。 1634* 线程内计算过程分解为异步任务调用。 1635* 通过任务依赖关系和锁机制避免并发任务数据竞争问题。 1636 1637 1638 1639## 已知限制 1640 1641 1642### C API中初始化ffrt对象后,对象的置空与销毁由用户负责 1643 1644* 为保证较高的性能,ffrt的C API中内部不包含对对象的销毁状态的标记,用户需要合理地进行资源的释放,重复调用各个对象的destroy操作,其结果是未定义的。 1645* 错误示例1,重复调用destroy可能造成不可预知的数据损坏。 1646 1647```{.c} 1648#include "ffrt.h" 1649void abnormal_case_1() 1650{ 1651 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1652 ... 1653 ffrt_task_handle_destroy(h); 1654 ffrt_task_handle_destroy(h); // double free 1655} 1656``` 1657 1658* 错误示例2,未调用destroy会造成内存泄漏 1659 1660```{.c} 1661#include "ffrt.h" 1662void abnormal_case_2() 1663{ 1664 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1665 ... 1666 // memory leak 1667} 1668``` 1669 1670* 建议示例,仅调用一次destroy,如有必要可进行置空 1671 1672```{.c} 1673#include "ffrt.h" 1674void normal_case() 1675{ 1676 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1677 ... 1678 ffrt_task_handle_destroy(h); 1679 h = nullptr; // if necessary 1680} 1681``` 1682