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