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 364void fib_ffrt(void* arg) 365{ 366 fib_ffrt_s* p = (fib_ffrt_s*)arg; 367 int x = p->x; 368 int* y = p->y; 369 370 if (x <= 1) { 371 *y = x; 372 } else { 373 int y1, y2; 374 fib_ffrt_s s1 = {x - 1, &y1}; 375 fib_ffrt_s s2 = {x - 2, &y2}; 376 const std::vector<ffrt_dependence_t> dx_deps = {{ffrt_dependence_data, &x}}; 377 ffrt_deps_t dx{static_cast<uint32_t>(dx_deps.size()), dx_deps.data()}; 378 const std::vector<ffrt_dependence_t> dy1_deps = {{ffrt_dependence_data, &y1}}; 379 ffrt_deps_t dy1{static_cast<uint32_t>(dy1_deps.size()), dy1_deps.data()}; 380 const std::vector<ffrt_dependence_t> dy2_deps = {{ffrt_dependence_data, &y2}}; 381 ffrt_deps_t dy2{static_cast<uint32_t>(dy2_deps.size()), dy2_deps.data()}; 382 const std::vector<ffrt_dependence_t> dy12_deps = {{ffrt_dependence_data, &y1}, {ffrt_dependence_data, &y2}}; 383 ffrt_deps_t dy12{static_cast<uint32_t>(dy12_deps.size()), dy12_deps.data()}; 384 ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL); 385 ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL); 386 ffrt_wait_deps(&dy12); 387 *y = y1 + y2; 388 } 389} 390 391int main(int narg, char** argv) 392{ 393 int r; 394 fib_ffrt_s s = {10, &r}; 395 const std::vector<ffrt_dependence_t> dr_deps = {{ffrt_dependence_data, &r}}; 396 ffrt_deps_t dr{static_cast<uint32_t>(dr_deps.size()), dr_deps.data()}; 397 ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL); 398 ffrt_wait_deps(&dr); 399 printf("fibonacci 10: %d\n", r); 400 return 0; 401} 402``` 403 404`解析`: 405 4061) 将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将结果累加; 407 4082) 虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继续拆分,因此,整个计算图的并行度是非常高的,Task 之间在FFRT 内部形成了一颗调用树; 409 410<img src="figures/ffrtfigure2.png" style="zoom:100%" /> 411 412> 以上实现,因为需要用户显式管理数据生命周期和函数入参打包两个因素,所以使得代码实现异常复杂。 413 414 415 416#### ffrt_deps_t 417 418* C API中对依赖数组的抽象,逻辑上等同于C++ API中的`std::vector<void*>` 419 420##### 声明 421 422```{.c} 423typedef enum { 424 ffrt_dependence_data, 425 ffrt_dependence_task, 426} ffrt_dependence_type_t; 427 428typedef struct { 429 ffrt_dependence_type_t type; 430 const void* ptr; 431} ffrt_dependence_t; 432 433typedef struct { 434 uint32_t len; 435 const ffrt_dependence_t* items; 436} ffrt_deps_t; 437``` 438 439##### 参数 440 441`len` 442 443* 所依赖的Signature的个数,取值大于等于0。 444 445`item` 446 447* len个Signature的起始地址指针。 448 449`type` 450 451* 当前依赖为数据依赖还是任务依赖。 452 453`ptr` 454 455* 所依赖对应Signature内容的实际地址。 456 457##### 返回值 458 459* 不涉及。 460 461##### 描述 462 463* item为len个Signature的起始指针,该指针可以指向堆空间,也可以指向栈空间,但是要求分配的空间大于等于len * sizeof(ffrt_dependence_t)。 464 465##### 样例 466 467* 创建数据依赖或者任务依赖: 468 469```{.c} 470// 创建数据依赖的ffrt_deps_t 471int x = 0; 472const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 473ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 474 475// 提交某个返回handle任务 476ffrt_task_handle_t task = ffrt_submit_h_base( 477 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 478// 创建任务依赖的ffrt_deps_t 479const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 480ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 481``` 482 483#### ffrt_task_attr_t 484 485<hr/> 486* 定义task 的属性的辅助类,与ffrt_submit_base 配合使用。 487 488##### 声明 489 490```{.c} 491typedef enum { 492 ffrt_qos_inherent = -1, 493 ffrt_qos_background, 494 ffrt_qos_utility, 495 ffrt_qos_default, 496 ffrt_qos_user_initiated, 497} ffrt_qos_default_t; 498 499typedef int ffrt_qos_t; 500 501typedef struct { 502 uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 503} ffrt_task_attr_t; 504typedef void* ffrt_task_handle_t; 505 506int ffrt_task_attr_init(ffrt_task_attr_t* attr); 507void ffrt_task_attr_destroy(ffrt_task_attr_t* attr); 508void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos); 509ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr); 510void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name); 511const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr); 512void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us); 513uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr); 514``` 515 516##### 参数 517 518`attr` 519 520* 创建的tasks属性的句柄。 521 522`qos` 523 524* qos 设定的枚举类型。 525* inherent 是一个qos 设定策略,代表即将ffrt_submit 的task 的qos 继承当前task 的qos。 526 527`delay_us` 528 529* 任务延迟执行的时间,单位为us。 530 531##### 返回值 532 533* 不涉及。 534 535##### 描述 536* `attr`所传递的内容会在ffrt_submit内部完成取存,ffrt_submit返回后用户即可销毁。 537* 约定: 538 * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`ffrt_qos_default`。 539 * 在submit 时,如果通过task_attr 设定qos 为`ffrt_qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`ffrt_qos_inherent` 的task,其qos 为`ffrt_qos_default`。 540 * 其他情况下,该提交的task 的qos 被设定为指定的值。 541* ffrt_task_attr_t对象的置空和销毁由用户完成,对同一个ffrt_task_attr_t仅能调用一次`ffrt_task_attr_destroy`,重复对同一个ffrt_task_attr_t调用`ffrt_task_attr_destroy`,其行为是未定义的。 542* 在`ffrt_task_attr_destroy`之后再对task_attr进行访问,其行为是未定义的。 543 544##### 样例 545 546* 提交一个qos 级别为background 的任务: 547 548```{.c} 549#include <stdio.h> 550#include "ffrt.h" 551 552void my_print(void* arg) 553{ 554 printf("hello ffrt\n"); 555} 556 557typedef struct { 558 ffrt_function_header_t header; 559 ffrt_function_t func; 560 ffrt_function_t after_func; 561 void* arg; 562} c_function; 563 564static void ffrt_exec_function_wrapper(void* t) 565{ 566 c_function* f = (c_function*)t; 567 if (f->func) { 568 f->func(f->arg); 569 } 570} 571 572static void ffrt_destroy_function_wrapper(void* t) 573{ 574 c_function* f = (c_function*)t; 575 if (f->after_func) { 576 f->after_func(f->arg); 577 } 578} 579 580#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 581static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 582 const ffrt_function_t after_func, void* arg) 583{ 584 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 585 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 586 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 587 f->header.exec = ffrt_exec_function_wrapper; 588 f->header.destroy = ffrt_destroy_function_wrapper; 589 f->func = func; 590 f->after_func = after_func; 591 f->arg = arg; 592 return (ffrt_function_header_t*)f; 593} 594 595static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 596 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 597{ 598 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 599} 600 601int main(int narg, char** argv) 602{ 603 ffrt_task_attr_t attr; 604 ffrt_task_attr_init(&attr); 605 ffrt_task_attr_set_qos(&attr, ffrt_qos_background); 606 ffrt_task_attr_set_delay(&attr, 10000); 607 ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr); 608 ffrt_task_attr_destroy(&attr); 609 ffrt_wait(); 610 return 0; 611} 612``` 613 614 615 616 617#### ffrt_submit_h_base 618 619<hr/> 620 621* 向调度器提交一个task,与ffrt_submit_base 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。 622 623##### 声明 624 625```{.c} 626typedef void* ffrt_task_handle_t; 627 628ffrt_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); 629void ffrt_task_handle_destroy(ffrt_task_handle_t handle); 630``` 631 632##### 参数 633 634`func` 635 636* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁。 637 638`in_deps` 639 640* 该参数是可选的。 641* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖。 642 643`out_deps` 644 645* 该参数是可选的。 646* 该参数用于描述该任务的输出依赖。 647* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发。 648 649`attr` 650 651* 该参数是可选的。 652* 该参数用于描述Task 的属性,比如qos 等,详见 [ffrt_task_attr_t](#ffrt_task_attr_t)章节。 653 654##### 返回值 655 656* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。 657 658##### 描述 659 660* **C API中的ffrt_task_handle_t需要用户调用`ffrt_task_handle_destroy`显式销毁。** 661* C API中的task_handle_t对象的置空和销毁由用户完成,对同一个ffrt_task_handle_t仅能调用一次`ffrt_task_handle_destroy`,重复对同一个ffrt_task_handle_t调用`ffrt_task_handle_destroy`,其行为是未定义的。 662* 在`ffrt_task_handle_destroy`之后再对ffrt_task_handle_t进行访问,其行为是未定义的。 663 664##### 样例 665 666```{.c} 667#include <stdio.h> 668#include "ffrt.h" 669 670void func0(void* arg) 671{ 672 printf("hello "); 673} 674 675void func1(void* arg) 676{ 677 (*(int*)arg)++; 678} 679 680void func2(void* arg) 681{ 682 printf("world, x = %d\n", *(int*)arg); 683} 684 685void func3(void* arg) 686{ 687 printf("handle wait"); 688 (*(int*)arg)++; 689} 690 691typedef struct { 692 ffrt_function_header_t header; 693 ffrt_function_t func; 694 ffrt_function_t after_func; 695 void* arg; 696} c_function; 697 698static void ffrt_exec_function_wrapper(void* t) 699{ 700 c_function* f = (c_function*)t; 701 if (f->func) { 702 f->func(f->arg); 703 } 704} 705 706static void ffrt_destroy_function_wrapper(void* t) 707{ 708 c_function* f = (c_function*)t; 709 if (f->after_func) { 710 f->after_func(f->arg); 711 } 712} 713 714#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 715static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 716 const ffrt_function_t after_func, void* arg) 717{ 718 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 719 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 720 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 721 f->header.exec = ffrt_exec_function_wrapper; 722 f->header.destroy = ffrt_destroy_function_wrapper; 723 f->func = func; 724 f->after_func = after_func; 725 f->arg = arg; 726 return (ffrt_function_header_t*)f; 727} 728 729static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func, 730 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 731{ 732 return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 733} 734 735static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 736 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 737{ 738 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 739} 740 741 742int main(int narg, char** argv) 743{ 744 // handle work with submit 745 ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task 746 int x = 1; 747 const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}}; 748 ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()}; 749 750 const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}}; 751 ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()}; 752 753 ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL); 754 ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h 755 ffrt_task_handle_destroy(h); 756 757 // handle work with wait 758 ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL); 759 760 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}}; 761 ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 762 ffrt_wait_deps(&d3); 763 ffrt_task_handle_destroy(h2); 764 printf("x = %d", x); 765 ffrt_wait(); 766 return 0; 767} 768``` 769 770* 预期的输出为: 771 772``` 773hello 774handle wait 775x = 2 776world, x = 3 777``` 778 779 780 781#### ffrt_this_task_get_id 782 783<hr/> 784 785* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)。 786 787##### 声明 788 789```{.c} 790uint64_t ffrt_this_task_get_id(); 791``` 792 793##### 参数 794 795* 不涉及。 796 797##### 返回值 798 799* 当前task的id。 800 801##### 描述 802 803* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0。 804* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上。 805* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转。 806 807##### 样例 808 809* 忽略。 810 811 812 813#### ffrt_this_task_update_qos 814 815<hr/> 816 817* 更新当前正在执行的task的优先级。 818 819##### 声明 820 821```{.c} 822int ffrt_this_task_update_qos(ffrt_qos_t qos); 823``` 824 825##### 参数 826 827* `qos` 新的优先级。 828 829##### 返回值 830 831* 0表示成功,非0表示失败。 832 833##### 描述 834 835* 该接口对当前task的qos调整会立即生效。 836* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行。 837* 如果新设定的qos与当前的qos一致,则接口会立即返回0,不做任何处理。 838* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理。** 839 840##### 样例 841 842* 忽略。 843 844### 串行队列 845<hr /> 846* FFRT提供queue来实现Andorid中类似WorkQueue能力,且在使用得当的情况下将有更好的性能。 847 848#### ffrt_queue_attr_t 849 850##### 声明 851```{.c} 852typedef struct { 853 uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; 854} ffrt_queue_attr_t; 855 856int ffrt_queue_attr_init(ffrt_queue_attr_t* attr); 857void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr); 858``` 859 860##### 参数 861 862`attr` 863* 该参数是指向未初始化的ffrt_queue_attr_t。 864 865##### 返回值 866* 若成功返回0,否则返回-1。 867 868##### 描述 869* ffrt_queue_attr_t用于创建ffrt_queue_t且不单独使用,因此必须在创建队列前先创建好队列属性。 870* ffrt_queue_attr_t对象的置空和销毁由用户完成,对同一个ffrt_queue_attr_t仅能调用一次`ffrt_queue_attr_destroy`,重复对同一个ffrt_queue_attr_t调用`ffrt_queue_attr_destroy`,其行为是未定义的。 871* 在`ffrt_queue_attr_destroy`之后再对ffrt_queue_attr_t进行访问,其行为是未定义的。 872 873##### 样例 874参考ffrt_queue_t章节的样例。 875 876#### ffrt_queue_t 877 878##### 声明 879```{.c} 880typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t; 881typedef void* ffrt_queue_t; 882 883ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr) 884void ffrt_queue_destroy(ffrt_queue_t queue) 885``` 886 887##### 参数 888 889`type` 890* 该参数用于描述创建的队列类型。 891 892`name` 893* 该参数用于描述创建队列的名字。 894 895`attr` 896* 该参数用于描述queue的属性,详见ffrt_queue_attr_t章节。 897 898##### 返回值 899* 若成功则返回新创建的队列,否则返回空指针。 900 901##### 描述 902* 提交至该队列的任务将按照顺序执行,如果某个提交的任务中发生阻塞,则无法保证该任务的执行顺序。 903* ffrt_queue_t对象的置空和销毁由用户完成,对同一个ffrt_queue_t仅能调用一次`ffrt_queue_destroy`,重复对同一个ffrt_queue_t调用`ffrt_queue_destroy`,其行为是未定义的。 904* 在`ffrt_queue_destroy`之后再对ffrt_queue_t进行访问,其行为是未定义的。 905 906##### 样例 907``` 908#include <stdio.h> 909#include "ffrt.h" 910 911using namespace std; 912 913template<class T> 914struct Function { 915 ffrt_function_header_t header; 916 T closure; 917}; 918 919template<class T> 920void ExecFunctionWrapper(void* t) 921{ 922 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 923 f->closure(); 924} 925 926template<class T> 927void DestroyFunctionWrapper(void* t) 928{ 929 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 930 f = nullptr; 931} 932 933template<class T> 934static inline ffrt_function_header_t* create_function_wrapper(T&& func, 935 ffrt_function_kind_t kind = ffrt_function_kind_general) 936{ 937 using function_type = Function<std::decay_t<T>>; 938 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 939 auto f = new (p)function_type; 940 f->header.exec = ExecFunctionWrapper<T>; 941 f->header.destroy = DestroyFunctionWrapper<T>; 942 f->closure = std::forward<T>(func); 943 return reinterpret_cast<ffrt_function_header_t*>(f); 944} 945 946int main(int narg, char** argv) 947{ 948 ffrt_queue_attr_t queue_attr; 949 (void)ffrt_queue_attr_init(&queue_attr); 950 ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 951 std::function<void()>&& queueFunc = [] () {printf("Task done.\n");}; 952 ffrt_function_header_t* queueFunc_t = create_function_wrapper((queueFunc), ffrt_function_kind_queue); 953 ffrt_queue_submit(queue_handle, queueFunc_t, nullptr); 954 955 ffrt_queue_attr_destroy(&queue_attr); 956 ffrt_queue_destroy(queue_handle); 957} 958``` 959### 同步原语 960 961#### ffrt_mutex_t 962<hr/> 963* FFRT提供的类似pthread mutex 的性能实现。 964 965##### 声明 966 967```{.c} 968typedef enum { 969 ffrt_error = -1, 970 ffrt_success = 0, 971 ffrt_error_nomem = ENOMEM, 972 ffrt_error_timedout = ETIMEDOUT, 973 ffrt_error_busy = EBUSY, 974 ffrt_error_inval = EINVAL 975} ffrt_error_t; 976 977struct ffrt_mutex_t; 978 979int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr); 980int ffrt_mutex_lock(ffrt_mutex_t* mutex); 981int ffrt_mutex_unlock(ffrt_mutex_t* mutex); 982int ffrt_mutex_trylock(ffrt_mutex_t* mutex); 983int ffrt_mutex_destroy(ffrt_mutex_t* mutex); 984``` 985 986##### 参数 987 988`attr` 989 990* 当前FFRT只支持基础类型的mutex,因此attr必须为空指针。 991 992`mutex` 993 994* 指向所操作的互斥锁的指针。 995 996##### 返回值 997 998* 若成功则为 ffrt_success ,否则发生错误。 999 1000##### 描述 1001* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1002* 该功能能够避免pthread传统的pthread_mutex_t 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能。 1003* **注意:目前暂不支持递归和定时功能。** 1004* **注意:C API中的ffrt_mutex_t需要用户调用`ffrt_mutex_init`和`ffrt_mutex_destroy`显式创建和销毁。** 1005* **注意:C API中的ffrt_mutex_t对象的置空和销毁由用户完成,对同一个ffrt_mutex_t仅能调用一次`ffrt_mutex_destroy`,重复对同一个ffrt_mutex_t调用`ffrt_mutex_destroy`,其行为是未定义的。** 1006* **注意:在`ffrt_mutex_destroy`之后再对ffrt_mutex_t进行访问,其行为是未定义的。** 1007 1008##### 样例 1009 1010```{.c} 1011#include <stdio.h> 1012#include "ffrt.h" 1013 1014typedef struct { 1015 int* sum; 1016 ffrt_mutex_t* mtx; 1017} tuple; 1018 1019void func(void* arg) 1020{ 1021 tuple* t = (tuple*)arg; 1022 1023 int ret = ffrt_mutex_lock(t->mtx); 1024 if (ret != ffrt_success) { 1025 printf("error\n"); 1026 } 1027 (*t->sum)++; 1028 ret = ffrt_mutex_unlock(t->mtx); 1029 if (ret != ffrt_success) { 1030 printf("error\n"); 1031 } 1032} 1033 1034typedef struct { 1035 ffrt_function_header_t header; 1036 ffrt_function_t func; 1037 ffrt_function_t after_func; 1038 void* arg; 1039} c_function; 1040 1041static void ffrt_exec_function_wrapper(void* t) 1042{ 1043 c_function* f = (c_function*)t; 1044 if (f->func) { 1045 f->func(f->arg); 1046 } 1047} 1048 1049static void ffrt_destroy_function_wrapper(void* t) 1050{ 1051 c_function* f = (c_function*)t; 1052 if (f->after_func) { 1053 f->after_func(f->arg); 1054 } 1055} 1056 1057#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1058static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1059 const ffrt_function_t after_func, void* arg) 1060{ 1061 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1062 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1063 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1064 f->header.exec = ffrt_exec_function_wrapper; 1065 f->header.destroy = ffrt_destroy_function_wrapper; 1066 f->func = func; 1067 f->after_func = after_func; 1068 f->arg = arg; 1069 return (ffrt_function_header_t*)f; 1070} 1071 1072static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1073 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1074{ 1075 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1076} 1077 1078void ffrt_mutex_task(void *) 1079{ 1080 int sum = 0; 1081 ffrt_mutex_t mtx; 1082 tuple t = {&sum, &mtx}; 1083 int ret = ffrt_mutex_init(&mtx, NULL); 1084 if (ret != ffrt_success) { 1085 printf("error\n"); 1086 } 1087 for (int i = 0; i < 10; i++) { 1088 ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL); 1089 } 1090 ffrt_mutex_destroy(&mtx); 1091 ffrt_wait(); 1092 printf("sum = %d\n", sum); 1093} 1094 1095int main(int narg, char** argv) 1096{ 1097 int r; 1098 ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL); 1099 ffrt_wait(); 1100 return 0; 1101} 1102``` 1103 1104预期输出为: 1105 1106``` 1107sum=10 1108``` 1109 1110* 该例子为功能示例,实际中并不鼓励这样使用。 1111 1112 1113#### ffrt_cond_t 1114<hr/> 1115 1116* FFRT提供的类似pthread 信号量的性能实现。 1117 1118##### 声明 1119 1120```{.c} 1121typedef enum { 1122 ffrt_error = -1, 1123 ffrt_success = 0, 1124 ffrt_error_nomem = ENOMEM, 1125 ffrt_error_timedout = ETIMEDOUT, 1126 ffrt_error_busy = EBUSY, 1127 ffrt_error_inval = EINVAL 1128} ffrt_error_t; 1129 1130struct ffrt_cond_t; 1131 1132int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr); 1133int ffrt_cond_signal(ffrt_cond_t* cond); 1134int ffrt_cond_broadcast(ffrt_cond_t* cond); 1135int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex); 1136int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point); 1137int ffrt_cond_destroy(ffrt_cond_t* cond); 1138``` 1139 1140##### 参数 1141 1142`cond` 1143 1144* 指向所操作的信号量的指针。 1145 1146`attr` 1147 1148* 属性设定,空指针表示使用默认属性。 1149 1150`mutex` 1151 1152* 指向要在阻塞期间解锁的互斥锁的指针。 1153 1154`time_point` 1155 1156* 指向指定等待时限时间的对象的指针。 1157 1158 1159##### 返回值 1160 1161* 若成功则为 ffrt_success,若在锁定互斥前抵达时限则为 ffrt_error_timedout。 1162 1163##### 描述 1164* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1165* 该功能能够避免传统的pthread_cond_t在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能。 1166* **注意:C API中的ffrt_cond_t需要用户调用`ffrt_cond_init`和`ffrt_cond_destroy`显式创建和销毁。** 1167* **注意:C API中的ffrt_cond_t对象的置空和销毁由用户完成,对同一个ffrt_cond_t仅能调用一次`ffrt_cond_destroy`,重复对同一个ffrt_cond_t调用`ffrt_cond_destroy`,其行为是未定义的。** 1168* **注意:在`ffrt_cond_destroy`之后再对ffrt_cond_t进行访问,其行为是未定义的。** 1169 1170##### 样例 1171 1172```{.c} 1173#include <stdio.h> 1174#include "ffrt.h" 1175 1176typedef struct { 1177 ffrt_cond_t* cond; 1178 int* a; 1179 ffrt_mutex_t* lock_; 1180} tuple; 1181 1182void func1(void* arg) 1183{ 1184 tuple* t = (tuple*)arg; 1185 int ret = ffrt_mutex_lock(t->lock_); 1186 if (ret != ffrt_success) { 1187 printf("error\n"); 1188 } 1189 while (*t->a != 1) { 1190 ret = ffrt_cond_wait(t->cond, t->lock_); 1191 if (ret != ffrt_success) { 1192 printf("error\n"); 1193 } 1194 } 1195 ret = ffrt_mutex_unlock(t->lock_); 1196 if (ret != ffrt_success) { 1197 printf("error\n"); 1198 } 1199 printf("a = %d\n", *(t->a)); 1200} 1201 1202void func2(void* arg) 1203{ 1204 tuple* t = (tuple*)arg; 1205 int ret = ffrt_mutex_lock(t->lock_); 1206 if (ret != ffrt_success) { 1207 printf("error\n"); 1208 } 1209 *(t->a) = 1; 1210 ret = ffrt_cond_signal(t->cond); 1211 if (ret != ffrt_success) { 1212 printf("error\n"); 1213 } 1214 ret = ffrt_mutex_unlock(t->lock_); 1215 if (ret != ffrt_success) { 1216 printf("error\n"); 1217 } 1218} 1219 1220typedef struct { 1221 ffrt_function_header_t header; 1222 ffrt_function_t func; 1223 ffrt_function_t after_func; 1224 void* arg; 1225} c_function; 1226 1227static void ffrt_exec_function_wrapper(void* t) 1228{ 1229 c_function* f = (c_function*)t; 1230 if (f->func) { 1231 f->func(f->arg); 1232 } 1233} 1234 1235static void ffrt_destroy_function_wrapper(void* t) 1236{ 1237 c_function* f = (c_function*)t; 1238 if (f->after_func) { 1239 f->after_func(f->arg); 1240 } 1241} 1242 1243#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1244static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1245 const ffrt_function_t after_func, void* arg) 1246{ 1247 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1248 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1249 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1250 f->header.exec = ffrt_exec_function_wrapper; 1251 f->header.destroy = ffrt_destroy_function_wrapper; 1252 f->func = func; 1253 f->after_func = after_func; 1254 f->arg = arg; 1255 return (ffrt_function_header_t*)f; 1256} 1257 1258static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1259 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1260{ 1261 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1262} 1263 1264void ffrt_cv_task(void *) 1265{ 1266 ffrt_cond_t cond; 1267 int ret = ffrt_cond_init(&cond, NULL); 1268 if (ret != ffrt_success) { 1269 printf("error\n"); 1270 } 1271 int a = 0; 1272 ffrt_mutex_t lock_; 1273 tuple t = {&cond, &a, &lock_}; 1274 ret = ffrt_mutex_init(&lock_, NULL); 1275 if (ret != ffrt_success) { 1276 printf("error\n"); 1277 } 1278 ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL); 1279 ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL); 1280 ffrt_wait(); 1281 ffrt_cond_destroy(&cond); 1282 ffrt_mutex_destroy(&lock_); 1283} 1284 1285int main(int narg, char** argv) 1286{ 1287 ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL); 1288 ffrt_wait(); 1289 return 0; 1290} 1291``` 1292 1293预期输出为: 1294 1295``` 1296a=1 1297``` 1298 1299* 该例子为功能示例,实际中并不鼓励这样使用。 1300 1301### 杂项 1302 1303#### ffrt_usleep 1304 1305<hr/> 1306* FFRT提供的类似C11 sleep和linux usleep的性能实现。 1307 1308##### 声明 1309 1310```{.c} 1311int ffrt_usleep(uint64_t usec); 1312``` 1313 1314##### 参数 1315 1316`usec` 1317 1318* 睡眠的us数。 1319 1320##### 返回值 1321 1322* 不涉及。 1323 1324##### 描述 1325* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1326* 该功能能够避免传统的sleep 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能。 1327 1328##### 样例 1329 1330```{.c} 1331#include <time.h> 1332#include <stdio.h> 1333#include "ffrt.h" 1334 1335void func(void* arg) 1336{ 1337 time_t current_time = time(NULL); 1338 printf("Time: %s", ctime(¤t_time)); 1339 ffrt_usleep(2000000); // 睡眠 2 秒 1340 current_time = time(NULL); 1341 printf("Time: %s", ctime(¤t_time)); 1342} 1343 1344typedef struct { 1345 ffrt_function_header_t header; 1346 ffrt_function_t func; 1347 ffrt_function_t after_func; 1348 void* arg; 1349} c_function; 1350 1351static void ffrt_exec_function_wrapper(void* t) 1352{ 1353 c_function* f = (c_function*)t; 1354 if (f->func) { 1355 f->func(f->arg); 1356 } 1357} 1358 1359static void ffrt_destroy_function_wrapper(void* t) 1360{ 1361 c_function* f = (c_function*)t; 1362 if (f->after_func) { 1363 f->after_func(f->arg); 1364 } 1365} 1366 1367#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1368static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1369 const ffrt_function_t after_func, void* arg) 1370{ 1371 FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size, 1372 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1373 c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general); 1374 f->header.exec = ffrt_exec_function_wrapper; 1375 f->header.destroy = ffrt_destroy_function_wrapper; 1376 f->func = func; 1377 f->after_func = after_func; 1378 f->arg = arg; 1379 return (ffrt_function_header_t*)f; 1380} 1381 1382static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func, 1383 void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr) 1384{ 1385 ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr); 1386} 1387 1388int main(int narg, char** argv) 1389{ 1390 ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL); 1391 ffrt_wait(); 1392 return 0; 1393} 1394``` 1395 1396一种输出情况为: 1397 1398``` 1399Time: Tue Aug 13 15:45:30 2024 1400Time: Tue Aug 13 15:45:32 2024 1401``` 1402 1403#### ffrt_yield 1404<hr/> 1405* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效。 1406 1407##### 声明 1408 1409```{.c} 1410void ffrt_yield(); 1411``` 1412 1413##### 参数 1414 1415* 不涉及。 1416 1417##### 返回值 1418 1419* 不涉及。 1420 1421##### 描述 1422* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为。 1423* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态。 1424 1425##### 样例 1426 1427* 省略。 1428 1429 1430## 开发步骤 1431 1432以下步骤描述了如何使用`FFRT`提供的Native API接口,创建并行任务和串行队列任务以及销毁相应资源。 1433 1434**添加动态链接库** 1435 1436CMakeLists.txt中添加以下lib: 1437```txt 1438libffrt.z.so 1439``` 1440 1441**头文件** 1442```c++ 1443#include "ffrt/task.h" 1444#include "ffrt/type_def.h" 1445#include "ffrt/condition_variable.h" 1446#include "ffrt/mutex.h" 1447#include "ffrt/queue.h" 1448#include "ffrt/sleep.h" 1449``` 1450 14511. **首先需要对执行的函数进行封装**。 1452 ```c++ 1453 // 第一种使用模板,支持C++ 1454 template<class T> 1455 struct Function { 1456 ffrt_function_header_t header; 1457 T closure; 1458 }; 1459 1460 template<class T> 1461 void ExecFunctionWrapper(void* t) 1462 { 1463 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1464 f->closure(); 1465 } 1466 1467 template<class T> 1468 void DestroyFunctionWrapper(void* t) 1469 { 1470 auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t); 1471 f->closure = nullptr; 1472 } 1473 1474 template<class T> 1475 static inline ffrt_function_header_t* create_function_wrapper(T&& func, 1476 ffrt_function_kind_t kind = ffrt_function_kind_general) 1477 { 1478 using function_type = Function<std::decay_t<T>>; 1479 auto p = ffrt_alloc_auto_managed_function_storage_base(kind); 1480 auto f = new (p)function_type; 1481 f->header.exec = ExecFunctionWrapper<T>; 1482 f->header.destroy = DestroyFunctionWrapper<T>; 1483 f->closure = std::forward<T>(func); 1484 return reinterpret_cast<ffrt_function_header_t*>(f); 1485 } 1486 1487 // 第二种创建方式 1488 typedef struct { 1489 ffrt_function_header_t header; 1490 ffrt_function_t func; 1491 ffrt_function_t after_func; 1492 void* arg; 1493 } CFunction; 1494 1495 static void FfrtExecFunctionWrapper(void* t) 1496 { 1497 CFunction* f = static_cast<CFunction*>(t); 1498 if (f->func) { 1499 f->func(f->arg); 1500 } 1501 } 1502 1503 static void FfrtDestroyFunctionWrapper(void* t) 1504 { 1505 CFunction* f = static_cast<CFunction*>(t); 1506 if (f->after_func) { 1507 f->after_func(f->arg); 1508 } 1509 } 1510 1511 #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1]) 1512 static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func, 1513 const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general) 1514 { 1515 FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size, 1516 size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size); 1517 CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t)); 1518 f->header.exec = FfrtExecFunctionWrapper; 1519 f->header.destroy = FfrtDestroyFunctionWrapper; 1520 f->func = func; 1521 f->after_func = after_func; 1522 f->arg = arg; 1523 return reinterpret_cast<ffrt_function_header_t*>(f); 1524 } 1525 1526 // 样例:待提交执行的函数 1527 void OnePlusForTest(void* arg) 1528 { 1529 (*static_cast<int*>(arg)) += 1; 1530 } 1531 ``` 1532 15332. **设置task属性值**。 1534 1535 用户提交任务时可以设置任务属性,包括qos优先级,名称等,具体可参考接口文档。 1536 ```c++ 1537 // ******初始化并行任务属性****** 1538 ffrt_task_attr_t attr; 1539 ffrt_task_attr_init(&attr); 1540 1541 // ******创建串行队列****** 1542 1543 // 创建串行队列的属性 1544 ffrt_queue_attr_t queue_attr; 1545 // 创建串行队列的handle 1546 ffrt_queue_t queue_handle; 1547 1548 // 初始化队列属性 1549 (void)ffrt_queue_attr_init(&queue_attr); 1550 1551 // 如有需要,设置指定优先级 1552 ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit)); 1553 // 如有需要,设置超时时间(ms) 1554 ffrt_queue_attr_set_timeout(&queue_attr, 10000); 1555 // 如有需要,设置超时回调 1556 int x = 0; 1557 ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x, 1558 ffrt_function_kind_queue)); 1559 1560 // 基于属性,初始化队列 1561 queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr); 1562 ``` 1563 15643. **提交任务**。 1565 ```c++ 1566 int a = 0; 1567 // ******并行任务****** 1568 // 提交不带handle返回值的并行任务 1569 ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1570 // 提交带handle返回值的并行任务 1571 ffrt_task_handle_t task = ffrt_submit_h_base( 1572 ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr); 1573 1574 // ******串行任务****** 1575 // 提交不返回handle的串行队列任务 1576 ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, 1577 ffrt_function_kind_queue), nullptr); 1578 // 提交带handle的串行队列任务 1579 ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle, 1580 ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr); 1581 1582 // 如果需要等待执行结果,则调用wait 1583 const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}}; 1584 ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()}; 1585 ffrt_wait_deps(&wait); 1586 1587 ffrt_queue_wait(handle); 1588 ``` 1589 15904. **任务提交完成后销毁相应资源**。 1591 ```c++ 1592 // ******销毁并行任务****** 1593 ffrt_task_attr_destroy(&attr); 1594 ffrt_task_handle_destroy(task); 1595 1596 // ******销毁串行队列任务****** 1597 // 先销毁任务handle,再销毁队列 1598 ffrt_queue_attr_destroy(&queue_attr); 1599 ffrt_task_handle_destroy(handle); 1600 ffrt_queue_destroy(queue_handle); 1601 ``` 1602 1603## 使用建议 1604 1605### 建议1: 函数化 1606 1607**基本思想:计算过程函数化** 1608 1609* 程序过程各步骤以函数封装表达,函数满足类纯函数特性。 1610* 无全局数据访问。 1611* 无内部状态保留。 1612* 通过ffrt_submit_base()接口以异步任务方式提交函数执行。 1613* 将函数访问的数据对象以及访问方式在ffrt_submit_base()接口中的in_deps/out_deps参数表达。 1614* 程序员通过inDeps/outDeps参数表达任务间依赖关系以保证程序执行的正确性。 1615 1616> 做到纯函数的好处在于:1. 能够最大化挖掘并行度,2.避免DataRace和锁的问题。 1617 1618 1619 1620**在实际中,可以根据场景放松纯函数的约束,但前提是:** 1621 1622* 确定添加的in_deps/out_deps可确保程序正确执行。 1623* 通过FFRT提供的锁机制保护对全局变量的访问。 1624 1625 1626### 建议2: 使用FFRT提供的替代API 1627 1628* 禁止在FFRT任务中使用系统线程库API创建线程,使用submit提交任务。 1629* 使用FFRT提供的锁,条件变量,睡眠,IO等API代替系统线程库API。 1630 * 使用系统线程库API可能造成工作线程阻塞,引起额外性能开销。 1631 1632 1633 1634### 建议3: Deadline机制 1635 1636* **必须用于具备周期/重复执行特征的处理流程。** 1637* 在有明确时间约束和性能关键的处理流程中使用,避免滥用。 1638* 在相对大颗粒度的处理流程中使用,例如具有16.6ms时间约束的帧处理流程。 1639 1640 1641 1642### 建议4: 从线程模型迁移 1643 1644* 创建线程替代为创建FFRT任务。 1645 * 线程从逻辑上类似无in_deps的任务。 1646* 识别线程间的依赖关系,并将其表达在任务的依赖关系in_deps/out_deps上。 1647* 线程内计算过程分解为异步任务调用。 1648* 通过任务依赖关系和锁机制避免并发任务数据竞争问题。 1649 1650 1651 1652## 已知限制 1653 1654 1655### C API中初始化ffrt对象后,对象的置空与销毁由用户负责 1656 1657* 为保证较高的性能,ffrt的C API中内部不包含对对象的销毁状态的标记,用户需要合理地进行资源的释放,重复调用各个对象的destroy操作,其结果是未定义的。 1658* 错误示例1,重复调用destroy可能造成不可预知的数据损坏。 1659 1660```{.c} 1661#include "ffrt.h" 1662void abnormal_case_1() 1663{ 1664 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1665 ... 1666 ffrt_task_handle_destroy(h); 1667 ffrt_task_handle_destroy(h); // double free 1668} 1669``` 1670 1671* 错误示例2,未调用destroy会造成内存泄漏 1672 1673```{.c} 1674#include "ffrt.h" 1675void abnormal_case_2() 1676{ 1677 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1678 ... 1679 // memory leak 1680} 1681``` 1682 1683* 建议示例,仅调用一次destroy,如有必要可进行置空 1684 1685```{.c} 1686#include "ffrt.h" 1687void normal_case() 1688{ 1689 ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL); 1690 ... 1691 ffrt_task_handle_destroy(h); 1692 h = nullptr; // if necessary 1693} 1694```