1## FFRT 用户编程指南
2
3> Function Flow编程模型是一种基于任务和数据驱动的并发编程模型,允许开发者通过任务及其依赖关系描述的方式进行应用开发。FFRT(Function Flow运行时)是支持Function Flow编程模型的软件运行时库,用于调度执行开发者基于Function Flow编程模型开发的应用。通过Function Flow编程模型和FFRT,开发者可专注于应用功能开发,由FFRT在运行时根据任务依赖状态和可用执行资源自动并发调度和执行任务。
4>
5> 本文用于指导开发者基于Function Flow编程模型和FFRT实现并行编程。
6
7<hr/>
8# 版本
9
10| 版本 | 编辑                                                         | 主要变更                                                     | 日期       |
11| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ---------- |
12| V0.1 | linjiashu <br />zhangguowei <br />huangyouzhong  | 发布以下API:<br />1. task 管理,包括:submit,wait,task_attr, task_handle/submit_h<br />2. 同步原语,包括:mutex,shared_mutex, condition_variable<br />3. Deadline 调度<br />4. 杂项:sleep,yield<br /> | 2022/09/26 |
13| V0.1.1 | shengxia  | 部分描述更新 | 2023/08/24 |
14| V0.1.2 | wanghuachen  | 新增串行队列相关接口以及说明,增加规范以避免double free问题 | 2023/10/07 |
15| V0.1.3 | shengxia  | 优化串行队列内容描述 | 2024/01/12 |
16
17<br/>
18
19<hr/>
20# 缩写
21
22| 缩略语        | 英文全名                        | 中文解释                                                     |
23| ------------- | ------------------------------- | ------------------------------------------------------------ |
24| FFRT          | Function Flow Run Time          | 软件实现Function Flow运行时用于任务调度和执行                |
25| Function Flow | Function Flow Programming Model | Function Flow编程模型                                        |
26| Pure Function | Pure Function                   | 纯函数,注意本文中定义的纯函数指的是通过表达相互间数据依赖即可由调度系统保证正确执行的任务。 |
27
28
29<br/>
30<hr/>
31# 编程模型
32## 两种编程模型
33
34|                | 线程编程模型                                                 | FFRT任务编程模型                                             |
35| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
36| 并行度挖掘方式 | 程序员通过创建多线程并把任务分配到每个线程中执行来挖掘运行时的并行度 | 程序员(编译器工具或语言特性配合)静态编程时将应用分解成任务及其数据依赖关系,运行时调度器分配任务到工作线程执行 |
37| 谁负责线程创建 | 程序员负责创建线程,线程编程模型无法约束线程的创建,滥用可能造成系统中大量线程 | FFRT运行时负责工作线程池的创建和管理由调度器负责,程序员无法直接创建线程 |
38| 负载均衡       | 程序员静态编程时将任务映射到线程,映射不合理或任务执行时间不确定造成线程负载不均 | FFRT运行时根据线程执行状态调度就绪任务到空闲线程执行,减轻了线程负载不均问题 |
39| 调度开销       | 线程调度由内核态调度器完成,调度开销大                       | FFRT运行时在用户态以协程方式调度执行,相比内核线程调度机制更为轻量,减小调度的开销,并可通过硬化调度卸载进一步减小调度开销 |
40| 依赖表达       | 线程创建时即处于可执行状态,执行时与其他线程同步操作,增加线程切换 | FFRT运行时根据任务创建时显式表达的输入依赖和输出依赖关系判断任务可执行状态,当输入依赖不满足时,任务不被调度执行 |
41
42
43
44## Function Flow 任务编程模型
45
46Function Flow编程模型允许开发者通过任务及其依赖关系描述的方式进行应用开发,其主要特性包括`Task-Based` 和 `Data-Driven` 。
47
48### Task-Based 特性
49
50`Task-Based` 指在Function Flow编程模型中开发者以任务方式来组织应用程序表达,运行时以任务粒度执行调度。
51
52任务定义为一种面向开发者的编程线索和面向运行时的执行对象,通常包含一组指令序列及其操作的数据上下文环境。
53
54Function Flow编程模型中的任务包含以下主要特征:
55
56- 任务之间可指定依赖关系,依赖关系通过`Data-Driven`方式表达。
57- 任务可支持嵌套,即任务在执行过程中可生成新的任务下发给运行时,形成父子任务关系。
58- 多任务支持互同步操作,例如等待,锁,条件变量等。
59
60> 注意
61>
62> 任务颗粒度影响应用执行性能,颗粒度过小增加调度开销,颗粒度过大降低并行度。Function Flow编程模型中任务的目标颗粒度最小为100us量级,开发者应注意合理控制任务颗粒度。
63
64### Data-Driven 特性
65
66`Data-Driven`指任务之间的依赖关系通过数据依赖表达。
67
68任务执行过程中对其关联的数据对象进行读写操作。在Function Flow编程模型中,数据对象表达抽象为数据签名,每个数据签名唯一对应一个数据对象。
69
70数据依赖抽象为任务所操作的数据对象的数据签名列表,包括输入数据依赖`in_deps`和输出数据依赖`out_deps`。数据对象的签名出现在一个任务的`in_deps`中时,该任务称为数据对象的消费者任务,消费者任务执行不改变其输入数据对象的内容;数据对象的签名出现在任务的`out_deps`中时,该任务称为数据对象的生产者任务,生产者任务执行改变其输出数据对象的内容,从而生成该数据对象的一个新的版本。
71
72一个数据对象可能存在多个版本,每个版本对应一个生产者任务和零个,一个或多个消费者任务,根据生产者任务和消费者任务的下发顺序定义数据对象的多个版本的顺序以及每个版本所对应的生产者和消费者任务。
73
74数据依赖解除的任务进入就绪状态允许被调度执行,依赖解除状态指任务所有输入数据对象版本的生产者任务执行完成,且所有输出数据对象版本的所有消费者任务执行完成的状态。
75
76通过上述`Data-Driven`的数据依赖表达,FFRT在运行时可动态构建任务之间的基于生产者/消费者的数据依赖关系并遵循任务数据依赖状态执行调度,包括:
77
78- Producer-Consumer 依赖
79
80  一个数据对象版本的生产者任务和该数据对象版本的消费者任务之间形成的依赖关系,也称为Read-after-Write依赖。
81
82- Consumer-Producer 依赖
83
84  一个数据对象版本的消费者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Read依赖。
85
86- Producer-Producer 依赖
87
88  一个数据对象版本的生产者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Write依赖。
89
90
91例如,如果有这么一些任务,与数据A的关系表述为:
92```{.cpp}
93task1(OUT A);
94task2(IN A);
95task3(IN A);
96task4(OUT A);
97task5(OUT A);
98```
99
100<img src="images/image-20220926150341102.png" style="zoom:60%" />
101
102> 为表述方便,本文中的数据流图均以圆圈表示 Task,方块表示数据。
103
104可以得出以下结论:
105- task1 与task2/task3 构成Producer-Consumer 依赖,即:task2/task3 需要等到task1 写完A之后才能读A
106- task2/task3 与task4 构成Consumer-Producer 依赖,即:task4 需要等到task2/task3 读完A之后才能写A
107- task4 与task5 构成Producer-Producer 依赖,即:task5 需要等到task4 写完A之后才能写A
108
109
110
111# C++ API
112
113> C++ API采用接近C++11的命名风格,以`ffrt`命名空间替代`std`命名空间
114> 需编译使用-std=c++17
115
116## 任务管理
117### submit
118<hr/>
119* 向调度器提交一个task
120* 该接口是异步的,即该接口不等到task完成即可返回,因此,通常与[wait](#wait) 配合使用
121
122* 建议FFRT任务上下文使用ffrt::mutex替代std::mutex
123  * API上,二者仅在命令空间上有差异,可平滑替换(ffrt::mutex可在非ffrt task中调用,效果与普通的锁一致)
124  * ffrt::mutex相对std::mutex开销更小,且不会阻塞FFRT的worker线程(提交到FFRT的任务中大量使用std::mutex有FFRT worker线程被耗尽而死锁的风险)
125
126#### 声明
127
128```{.cpp}
129namespace ffrt {
130void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
131void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
132}
133```
134
135#### 参数
136
137`func`
138
139* 可被std::function 接收的一切CPU 可执行体,可以为C++ 定义的Lambda 函数闭包,函数指针,甚至是函数对象
140
141`in_deps`
142
143* 该参数是可选的
144* 该参数用于描述该任务的输入依赖,FFRT 通过数据的虚拟地址作为数据的Signature 来建立依赖
145
146`out_deps`
147
148* 该参数是可选的
149* 该参数用于描述该任务的输出依赖
150* `注意`:该依赖值本质上是一个数值,ffrt没办法区分该值是合理的还是不合理的,会假定输入的值是合理的进行处理;但不建议采用NULL,1, 2 等值来建立依赖关系,建议采用实际的内存地址,因为前者使用不当会建立起不必要的依赖,影响并发
151
152`attr`
153
154* 该参数是可选的
155* 该参数用于描述Task 的属性,比如qos 等,详见 [task_attr](#task_attr)章节
156
157#### 返回值
158
159* 不涉及
160
161#### 描述
162* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用
163
164* 该接口支持嵌套调用,即任务中可以继续提交子任务
165
166* 该接口在实现上使用多个重载版本以优化性能(基于移动语义,初始化列表等),用户只需按上述原型使用,编译时会自动选择最佳版本,支持的重载版本有:
167
168  ```{.cpp}
169  void submit(std::function<void()>&& func);
170  void submit(std::function<void()>&& func, std::initializer_list<const void*> in_deps);
171  void submit(std::function<void()>&& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps);
172  void submit(std::function<void()>&& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps, const task_attr& attr);
173
174  void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps);
175  void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps);
176  void submit(std::function<void()>&& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps, const task_attr& attr);
177
178  void submit(const std::function<void()>& func);
179  void submit(const std::function<void()>& func, std::initializer_list<const void*> in_deps);
180  void submit(const std::function<void()>& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps);
181  void submit(const std::function<void()>& func, std::initializer_list<const void*> in_deps, std::initializer_list<const void*> out_deps, const task_attr& attr);
182
183  void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps);
184  void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps);
185  void submit(const std::function<void()>& func, const std::vector<const void*>& in_deps, const std::vector<const void*>& out_deps, const task_attr& attr);
186  ```
187
188
189#### 样例
190
191**submit and wait**
192
193```{.cpp}
194#include <iostream>
195#include "ffrt.h"
196
197int main(int narg, char** argv)
198{
199    int i = 0;
200    for (i = 0; i < 3; i++) {
201        ffrt::submit([i] { std::cout << "num: " << i << std::endl; });
202    }
203    ffrt::wait();
204    return 0;
205}
206```
207
208`解析`:
209
2101)   该示例中连续下发了3个Task,Task 使用C++ 11 Lambda 来描述(实际中Task 还可以使用函数指针,函数对象来描述),这些 Task 都会读取i,但是不会写任何变量;
211
2122)   ffrt::submit 为异步下发,因此,Task2 并不会等到 Task1 执行完成之后再下发;
213
2143)   ffrt::wait 用于实现待所有Task 都执行完成之后 main 函数再退出;
215
2164)   由于3个Task 在数据依赖关系上没有生产者-消费者或生产者-生产者依赖关系,因此3个 Task 是可以并行的,1种可能的输出是:
217
218```{.cpp}
219num: 0
220num: 2
221num: 1
222```
223
224`注意`:
225
226如果将Lambda 表达式中的值捕获设置成引用捕获(即`[&i] { std::cout << "num: " << i << std::endl; }`),可能得到的输出为:
227
228```{.cpp}
229num: 2
230num: 2
231num: 2
232```
233
234这是因为FFRT 是异步编程模型,在第一个task 真正开始执行的时候,i 的值可能已经被修改为1或者2
235
236
237
238**data verison**
239
240<img src="images/image-20220926150341102.png" style="zoom:60%" />
241
242```{.cpp}
243#include <iostream>
244#include "ffrt.h"
245
246int main(int narg, char** argv)
247{
248    int x = 1;
249    ffrt::submit([&] {x = 100; std::cout << "x:" << x << std::endl;}, {}, {&x});
250    ffrt::submit([&] {std::cout << "x:" << x << std::endl;}, {&x}, {});
251    ffrt::submit([&] {std::cout << "x:" << x << std::endl;}, {&x}, {});
252    ffrt::submit([&] {x++; std::cout << "x:" << x << std::endl;}, {}, {&x});
253    ffrt::submit([&] {x++; std::cout << "x:" << x << std::endl;}, {}, {&x});
254
255    ffrt::wait();
256    return 0;
257}
258```
259
260 `解析`:
261
2621)   按上一章节[Data-Driven 特性](#Data-Driven 特性)的描述,输出一定为:
263
264```{.cpp}
265x:100
266x:100
267x:100
268x:101
269x:102
270```
271
272**nested task**
273
274```{.cpp}
275#include <iostream>
276#include "ffrt.h"
277
278int main(int narg, char** argv)
279{
280    ffrt::submit([&] {
281        std::cout << "task 1" << std::endl;
282        ffrt::submit([&] {std::cout << "nested task 1.1" << std::endl;}, {}, {});
283        ffrt::submit([&] {std::cout << "nested task 1.2" << std::endl;}, {}, {});
284        ffrt::wait();
285    }, {}, {});
286
287    ffrt::submit([&] {
288        std::cout << "task 2" << std::endl;
289        ffrt::submit([&] {std::cout << "nested task 2.1" << std::endl;}, {}, {});
290        ffrt::submit([&] {std::cout << "nested task 2.2" << std::endl;}, {}, {});
291        ffrt::wait();
292    }, {}, {});
293    ffrt::wait();
294    return 0;
295}
296```
297
298 `解析`:
299
3001)   FFRT允许在 Task 内部继续提交多个SubTask,这样 Task 之间可以建立起一颗调用树;
301
3022)   Task1 和Task2 可以并行,Task 1.1/1.2/2.1/2.2 之间也可以并行,因此1种可行的输出为:
303
304```
305task 1
306nested task  1.1
307task 2
308nested task 1.2
309nested task 2.2
310nested task 2.1
311```
312
313### wait
314<hr/>
315* 同步等待,与[submit](#submit) 配合使用
316* 等待指定的数据被生产完成,或等待当前任务的所有子任务完成,在不满足条件之前,当前的执行上下文被suspend,在满足条件后恢复执行
317
318#### 声明
319
320```{.cpp}
321namespace ffrt {
322void wait(const std::vector<const void*>& deps);
323void wait();
324}
325```
326
327#### 参数
328
329`deps`
330
331* 需要等待被生产完成的数据的虚拟地址,这些地址可能作为某些任务在submit 时的out_deps
332
333#### 返回值
334
335* 不涉及
336
337#### 描述
338* wait(deps) 用于等待deps指代的数据被生产完成才能执行后面的代码
339* wait() 用于等待当前上下文提交的所有子任务(`注意:不包括孙子任务`)都完成才能执行后面的代码
340* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用
341* 在FFRT task 外部调用的wait 是OS 能够感知的等待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部
342
343#### 样例
344
345**recursive fibonacci**
346
347串行版的fibonacci 可以实现为:
348
349```{.cpp}
350#include <iostream>
351
352void fib(int x, int& y) {
353    if (x <= 1) {
354        y = x;
355    } else {
356        int y1, y2;
357        fib(x - 1, y1);
358        fib(x - 2, y2);
359        y = y1 + y2;
360    }
361}
362
363int main(int narg, char** argv)
364{
365    int r;
366    fib(10, r);
367    std::cout << "fibonacci 10: " << r << std::endl;
368    return 0;
369}
370```
371
372若要使用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加速的意义,但这种调用pattern 对并行编程模型的灵活性考验是非常高的),其中1种可行的实现为:
373
374```{.cpp}
375#include <iostream>
376
377#include "ffrt.h"
378
379void fib_ffrt(int x, int& y)
380{
381    if (x <= 1) {
382        y = x;
383    } else {
384        int y1, y2;
385        ffrt::submit([&] {fib_ffrt(x - 1, y1);}, {&x}, {&y1} );
386        ffrt::submit([&] {fib_ffrt(x - 2, y2);}, {&x}, {&y2} );
387        ffrt::wait({&y1, &y2});
388        y = y1 + y2;
389    }
390}
391
392int main(int narg, char** argv)
393{
394    int r;
395    ffrt::submit([&] { fib_ffrt(10, r); }, {}, {&r});
396    ffrt::wait({&r});
397    std::cout << "fibonacci 10: " << r << std::endl;
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="images/image-20220926152331554.png" style="zoom:100%" />
409
410
411### task_attr
412<hr/>
413* 定义task 的属性的辅助类,与[submit](#submit) 配合使用
414
415#### 声明
416
417```{.cpp}
418namespace ffrt {
419enum qos {
420    qos_inherit = -1,
421    qos_background,
422    qos_utility,
423    qos_default,
424    qos_user_initiated,
425};
426
427class task_attr {
428public:
429    task_attr& qos(enum qos qos); // set qos
430    enum qos qos() const; // get qos
431    task_attr& name(const char* name); // set name
432    const char* name() const; // get name
433};
434}
435```
436
437#### 参数
438
439`qos`
440
441* qos 设定的枚举类型
442* inherent 是一个qos 设定策略,代表即将submit 的task 的qos 继承当前task 的qos
443
444#### 返回值
445
446* 不涉及
447
448#### 描述
449* 约定
450  * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`qos_default`
451  * 在submit 时,如果通过task_attr 设定qos 为`qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`qos_inherent` 的task,其qos 为`qos_default`
452  * 其他情况下,该提交的task 的qos 被设定为指定的值
453* qos 级别从上到下依次递增,qos_user_interactive拥有最高优先级
454
455#### 样例
456
457```{.cpp}
458#include <iostream>
459#include "ffrt.h"
460
461int main(int narg, char** argv)
462{
463    ffrt::submit([] { std::cout << "hello ffrt" << std::endl; }, {}, {},
464        ffrt::task_attr().qos(ffrt::qos_background));
465    ffrt::wait();
466    return 0;
467}
468```
469
470* 提交一个qos 级别为background 的任务
471
472
473
474### submit_h
475
476<hr/>
477
478* 向调度器提交一个task,与[submit](#submit) 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
479
480#### 声明
481
482```{.cpp}
483namespace ffrt {
484class task_handle {
485public:
486    task_handle();
487    task_handle(ffrt_task_handle_t p);
488
489    task_handle(task_handle const&) = delete;
490    void operator=(task_handle const&) = delete;
491
492    task_handle(task_handle&& h);
493    task_handle& operator=(task_handle&& h);
494
495    operator void* () const;
496};
497
498task_handle submit_h(std::function<void()>&& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
499task_handle submit_h(const std::function<void()>& func, const std::vector<const void*>& in_deps = {}, const std::vector<const void*>& out_deps = {}, const task_attr& attr = {});
500}
501```
502
503#### 参数
504
505`func`
506
507* 同submit,详见[submit](#submit) 定义
508
509`in_deps`
510
511* 同submit,详见[submit](#submit) 定义
512
513`out_deps`
514
515* 同submit,详见[submit](#submit) 定义
516
517`attr`
518
519* 同submit,详见[submit](#submit) 定义
520
521#### 返回值
522
523* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
524
525#### 描述
526
527* 该接口与submit 使用基本相同,从性能的角度,在不需要返回task handle 的场景,可以调用submit 接口相对于submit_h 有更好的性能
528* task_handle 可以和其他的数据depends 同时作为某个task 的in_deps,表示该task 的执行依赖task_handle 对应的task 执行完成
529* task_handle 可以和其他的数据depends 同时作为wait 的deps,表示当前任务将被suspend,直到task_handle 对应的task 执行完成后将被恢复
530* task_handle 不建议作为某个task 的out_deps,其行为是未定义的
531
532
533#### 样例
534
535```{.cpp}
536#include <iostream>
537#include "ffrt.h"
538
539int main(int narg, char** argv)
540{
541    // handle work with submit
542    ffrt::task_handle h = ffrt::submit_h([] { std::cout << "hello "; }); // not need some data in this task
543    int x = 1;
544    ffrt::submit([&] { x++; }, {}, {&x});
545    ffrt::submit([&] { std::cout << "world, x = " << x << std::endl; }, {&x, h}); // this task depend x and h
546
547    // handle work with wait
548    ffrt::task_handle h2 = ffrt::submit_h([&] { std::cout << "handle wait" << std::endl; x++; });
549    ffrt::wait({h2});
550    std::cout << "x = " << x << std::endl;
551    ffrt::wait();
552    return 0;
553}
554```
555
556* 预期的输出为
557
558```
559hello world, x = 2
560handle wait
561x = 3
562```
563
564### get_id
565
566<hr/>
567
568* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)
569
570#### 声明
571
572```{.cpp}
573namespace ffrt {
574namespace this_task {
575uint64_t get_id();
576}
577}
578```
579
580#### 参数
581
582* 不涉及
583
584#### 返回值
585
586* 当前task的id
587
588#### 描述
589
590* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0
591* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上
592* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转
593
594#### 样例
595
596```{.cpp}
597#include <iostream>
598#include "ffrt.h"
599
600int main(int narg, char** argv)
601{
602    ffrt::submit([] { std::cout << "task id: " << ffrt::this_task::get_id() << std::endl; });
603    ffrt::submit([] { std::cout <<"task id: " << ffrt::this_task::get_id() << std::endl; });
604    ffrt::wait();
605    std::cout << "task id: " << ffrt::this_task::get_id() << std::endl;
606    return 0;
607}
608```
609
610* 可能的输出为:
611
612```
613task id: 1
614task id: 2
615task id: 0
616```
617
618### update_qos
619
620<hr/>
621
622* 更新当前正在执行的task的优先级
623
624#### 声明
625
626```{.cpp}
627namespace ffrt {
628namespace this_task {
629int update_qos(enum qos qos);
630}
631}
632```
633
634#### 参数
635
636`qos`
637* 新的qos等级
638
639#### 返回值
640
641* 0表示成功,非0表示失败
642
643#### 描述
644
645* 该接口对当前task的qos调整会立即生效
646* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行
647* 如果新设定的qos与当前的qos一致,则接口会立即返回,不做任何处理
648* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理**
649
650#### 样例
651
652```{.cpp}
653#include <iostream>
654#include <thread>
655#include "ffrt.h"
656
657int main(int narg, char** argv)
658{
659    ffrt::submit([] {
660        std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
661        std::cout << "return " << ffrt::this_task::update_qos(ffrt::qos_user_initiated) << std::endl;
662        std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
663    });
664    ffrt::wait();
665    std::cout << "return " << ffrt::this_task::update_qos(ffrt::qos_user_initiated) << std::endl;
666    return 0;
667}
668```
669
670* 可能的输出为:
671
672```
673thread id: 1024
674return 0
675thread id: 2222
676return 1
677```
678
679
680
681## 串行队列
682<hr />
683
684串行队列基于FFRT协程调度模型,实现了消息队列功能。串行任务执行在FFRT worker上,用户无需维护一个专用的线程,拥有更轻量级的调度开销。
685
686支持以下基本功能:
687
688* 支持创建队列,创建队列时可指定队列名称和优先级,每个队列在功能上相当于一个单独的线程,队列中的任务相对于用户线程异步执行。
689
690* 支持延时任务,向队列提交任务时支持设置 delay 属性,单位为微秒 us,提交给队列的延时任务,在提交时刻+delay时间后才会被调度执行。
691
692* 支持串行调度,同一个队列中的多个任务按照 uptime (提交时刻+delay时间)升序排列、串行执行,队列中一个任务完成后下一个任务才会开始。
693
694* 支持取消任务,支持根据任务句柄取消单个未执行的任务,如果这个任务已出队(开始执行或已执行完),取消接口返回异常值。
695
696* 支持同步等待,支持根据任务句柄等待指定任务完成,该任务完成,也代表同一队列中uptime在此任务之前的所有任务都已完成。
697
698### queue
699<hr/>
700
701#### 描述
702FFRT串行队列 C++ API,提供提交任务、取消任务、等待任务执行完成等功能
703
704#### 声明
705
706```{.cpp}
707namespace ffrt {
708class queue {
709public:
710    queue(queue const&) = delete;
711    void operator=(queue const&) = delete;
712
713    void submit(const std::function<void()>& func);
714    void submit(const std::function<void()>& func, const task_attr& attr);
715    void submit(std::function<void()>&& func);
716    void submit(std::function<void()>&& func, const task_attr& attr);
717
718    task_handle submit_h(const std::function<void()>& func);
719    task_handle submit_h(const std::function<void()>& func, const task_attr& attr);
720    task_handle submit_h(std::function<void()>&& func);
721    task_handle submit_h(std::function<void()>&& func, const task_attr& attr);
722
723    int cancel(const task_handle& handle);
724
725    void wait(const task_handle& handle);
726};
727}
728```
729
730#### 方法
731
732##### submit
733
734```{.cpp}
735namespace ffrt {
736void queue::submit(const std::function<void()>& func);
737void queue::submit(const std::function<void()>& func, const task_attr& attr);
738void queue::submit(std::function<void()>&& func);
739void queue::submit(std::function<void()>&& func, const task_attr& attr);
740}
741```
742
743* 描述:提交一个任务到队列中调度执行
744
745* 参数:
746
747  `func`:可被std::function接收的一切CPU可执行体,可以为C++定义的Lambda函数闭包,函数指针,甚至时函数对象
748
749  `attr`:该参数时可选的,用于描述task的属性,如qos、delay、timeout等,详见[task_attr](#task_attr)章节
750
751* 返回值:不涉及
752
753##### submit_h
754
755```{.cpp}
756namespace ffrt {
757task_handle queue::submit_h(const std::function<void()>& func);
758task_handle queue::submit_h(const std::function<void()>& func, const task_attr& attr);
759task_handle queue::submit_h(std::function<void()>&& func);
760task_handle queue::submit_h(std::function<void()>&& func, const task_attr& attr);
761}
762```
763
764* 描述:提交一个任务到队列中调度执行,并返回一个句柄
765
766* 参数:
767
768  `func`:可被std::function接收的一切CPU可执行体,可以为C++定义的Lambda函数闭包,函数指针,甚至时函数对象
769
770  `attr`:该参数时可选的,用于描述task的属性,如qos、delay、timeout等,详见[task_attr](#task_attr)章节
771
772* 返回值:
773
774  `task_handle`:task的句柄,该句柄可以用于建立task之间的依赖
775
776##### cancel
777
778```{.cpp}
779namespace ffrt {
780int queue::cancel(const task_handle& handle);
781}
782```
783
784* 描述:根据句柄取消对应的任务
785
786* 参数:
787
788  `handle`:任务的句柄
789
790* 返回值:
791
792  若成功返回0,否则返回其他非0值
793
794##### wait
795
796```{.cpp}
797namespace ffrt {
798void queue::wait(const task_handle& handle);
799}
800```
801
802* 描述:等待句柄对应的任务执行完成
803
804* 参数:
805
806  `handle`:任务的句柄
807
808* 返回值:不涉及
809
810
811#### 样例
812
813```{.cpp}
814#include "ffrt.h"
815
816int main(int narg, char** argv)
817{
818    // 创建队列,可设置队列优先级,默认为default等级
819    ffrt::queue q("test_queue", ffrt::queue_attr().qos(ffrt::qos_utility));
820
821    int x = 0;
822    // 提交串行任务
823    q.submit([&x] { x += 10; });
824
825    // 提交串行任务,并返回任务句柄
826    task_handle t1 = q.submit_h([&x] { x += 10; });
827
828    // 提交串行任务,设置延时时间1000us,并返回任务句柄
829    task_handle t2 = q.submit_h([&x] { x += 10; }, ffrt::task_attr().delay(1000));
830
831    // 等待指定任务执行完成
832    q.wait(t1);
833
834    // 取消句柄为t2的任务
835    q.cancel(t2);
836}
837```
838
839#### 使用约束
840
841* 队列销毁时,会等待正在执行的任务执行完成,队列中还没有开始执行的任务会被取消。
842
843* 任务粒度,串行队列支持任务执行超时检测(默认阈值30s,进程可配),因此队列中的单个任务不应该常驻(如循环任务),超过30s会向DFX上报超时。
844
845* 同步原语,任务中如果使用了 std::mutex/std::shared_mutex/std::condition_variable等std同步原语,会影响协程效率,需修改ffrt同步原语。
846
847  当前FFRT仅支持ffrt::mutex / ffrt::shared_mutex / ffrt::recursive_mutex / ffrt::condition_variable,用法和std相同,在ffrt的任务中使用未支持同步原语可能导致未定义的行为。
848
849* 生命周期,进程结束前需要释放FFRT资源。
850
851  例如SA业务,会在全局变量中管理串行队列。由于进程会先卸载libffrt.so再释放全局变量,如果进程结束时,SA未显式释放持有的队列,队列将随全局变量析构,析构时会访问已释放的ffrt资源,导致Fuzz用例出现use-after-free问题。
852
853* 不允许再串行任务中调用ffrt::submit和ffrt::wait,其行为是未定义的
854
855* 不允许使用ffrt::wait等待一个串行任务
856
857### queue_attr
858<hr/>
859
860#### 描述
861FFRT串行队列 C++ API,提供设置与获取串行队列优先级、设置与获取串行队列任务执行超时时间、设置与获取串行队列超时回调函数等功能
862
863#### 声明
864
865```{.cpp}
866namespace ffrt {
867class queue_attr {
868public:
869    queue_attr(const queue_attr&) = delete;
870    queue_attr& operator=(const queue_attr&) = delete;
871
872    queue_attr& qos(qos qos_);
873    uint64_t timeout() const;
874
875    queue_attr& callback(const std::function<void()>& func);
876    ffrt_function_header_t* callback() const;
877};
878}
879```
880
881#### 方法
882
883##### set qos
884
885```{.cpp}
886namespace ffrt {
887queue_attr& queue_attr::qos(qos qos_);
888}
889```
890
891* 描述:设置队列属性的qos成员
892
893* 参数:
894
895  `qos_`:串行队列的优先级
896
897* 返回值:
898
899  `queue_attr`:串行队列的属性
900
901##### get qos
902
903```{.cpp}
904namespace ffrt {
905int queue_attr::qos() const;
906}
907```
908
909* 描述:获取队列的优先级
910
911* 参数:不涉及
912
913* 返回值:
914
915  `qos`:串行队列的优先级
916
917##### set timeout
918
919```{.cpp}
920namespace ffrt {
921queue_attr& queue_attr::timeout(uint64_t timeout_us);
922}
923```
924
925* 描述:设置串行队列任务执行超时时间
926
927* 参数:
928
929  `timeout_us`:串行队列任务执行超时时间,单位为us
930
931* 返回值:
932
933  `queue_attr`:串行队列的属性
934
935##### get timeout
936
937```{.cpp}
938namespace ffrt {
939uint64_t queue_attr::timeout() const;
940}
941```
942
943* 描述:获取所设的串行队列任务执行超时时间
944
945* 参数:不涉及
946
947* 返回值:
948
949  `timeout`:串行队列任务执行超时时间,单位为us
950
951##### set timeout callback
952
953```{.cpp}
954namespace ffrt {
955queue_attr& callback(std::function<void()>& func);
956}
957```
958
959* 描述:设置串行队列超时回调函数
960
961* 参数:
962
963  `func`:可被std::function接收的一切CPU可执行体,可以为C++定义的Lambda函数闭包,函数指针,甚至时函数对象
964
965* 返回值:
966
967  `queue_attr`:串行队列的属性
968
969##### get timeout callback
970
971```{.cpp}
972namespace ffrt {
973ffrt_function_header_t* callback() const;
974}
975```
976
977* 描述:获取所设的串行队列超时回调函数
978
979* 参数:不涉及
980
981* 返回值:
982
983  `ffrt_function_header_t`:任务执行器,描述了该CPU Task如何执行和销毁的函数指针
984
985#### 样例
986```{.cpp}
987#include <stdio.h>
988#include "ffrt.h"
989
990int main(int narg, char** argv)
991{
992    std::function<void()> callbackFunc = [&x]() {
993        ...
994    };
995
996    // 创建队列,可设置队列优先级,默认为default等级
997    ffrt::queue q1("test_queue", queue_attr().qos(qos_utility));
998
999    // 创建队列,可通过设置timeout打开队列任务超时监测,默认不设置(关闭)
1000    // 超时会打印Error日志并执行用户设置的callback(可选)
1001    ffrt::queue q2("test_queue", ffrt::queue_attr().timeout(1000).callback(callbackFunc));
1002
1003    return 0;
1004}
1005```
1006
1007
1008## 同步原语
1009
1010### mutex
1011<hr/>
1012* FFRT提供的类似std::mutex 的性能实现
1013
1014#### 声明
1015
1016```{.cpp}
1017namespace ffrt {
1018class mutex {
1019public:
1020    mutex(mutex const &) = delete;
1021    void operator =(mutex const &) = delete;
1022
1023    void lock();
1024    void unlock();
1025    bool try_lock();
1026};
1027}
1028```
1029
1030#### 参数
1031
1032* 不涉及
1033
1034#### 返回值
1035
1036* 不涉及
1037
1038#### 描述
1039* 该功能能够避免传统的std::mutex 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能
1040
1041#### 样例
1042
1043```{.cpp}
1044#include <iostream>
1045#include "ffrt.h"
1046
1047void ffrt_mutex_task()
1048{
1049    int sum = 0;
1050    ffrt::mutex mtx;
1051    for (int i = 0; i < 10; i++) {
1052        ffrt::submit([&sum, i, &mtx] {
1053             mtx.lock();
1054             sum = sum + i;
1055             mtx.unlock();
1056        }, {}, {});
1057    }
1058    ffrt::wait();
1059    std::cout << "sum = " << sum << std::endl;
1060}
1061
1062int main(int narg, char** argv)
1063{
1064    int r;
1065    ffrt::submit(ffrt_mutex_task);
1066    ffrt::wait();
1067    return 0;
1068}
1069```
1070
1071预期输出为
1072
1073```
1074sum=45
1075```
1076
1077* 该例子为功能示例,实际中并不鼓励这样使用
1078
1079### shared_mutex
1080<hr/>
1081* FFRT提供的类似std::shared_mutex 的性能实现
1082
1083#### 声明
1084
1085```{.cpp}
1086namespace ffrt {
1087class shared_mutex {
1088public:
1089    shared_mutex(shared_mutex const &) = delete;
1090    void operator =(shared_mutex const &) = delete;
1091
1092    void lock();
1093    void unlock();
1094    bool try_lock();
1095
1096    void lock_shared();
1097    void unlock_shared();
1098    bool try_lock_shared();
1099};
1100}
1101```
1102
1103#### 参数
1104
1105* 不涉及
1106
1107#### 返回值
1108
1109* 不涉及
1110
1111#### 描述
1112* 该功能能够避免传统的std::shared_mutex 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能
1113
1114#### 样例
1115
1116```{.cpp}
1117#include <iostream>
1118#include "ffrt_inner.h"
1119
1120void ffrt_shared_mutex_task()
1121{
1122    int sum = 0;
1123    ffrt::shared_mutex mtx;
1124    for (int i = 0; i < 10; i++) {
1125        ffrt::submit([&sum, i, &mtx] {
1126             mtx.lock();
1127             sum = sum + i;
1128             mtx.unlock();
1129        }, {}, {});
1130        for (int j = 0; j < 5; j++) {
1131            ffrt::submit([&sum, j, &mtx] {
1132                mtx.lock_shared();
1133                std::cout << "sum = " << sum << std::endl;
1134                mtx.unlock_shared();
1135            }, {}, {});
1136        }
1137    }
1138    ffrt::wait();
1139    std::cout << "sum = " << sum << std::endl;
1140}
1141
1142int main(int narg, char** argv)
1143{
1144    int r;
1145    ffrt::submit(ffrt_shared_mutex_task);
1146    ffrt::wait();
1147    return 0;
1148}
1149```
1150
1151预期输出为
1152
1153```
1154sum=45
1155```
1156
1157* 该例子为功能示例,实际中并不鼓励这样使用
1158
1159### condition_variable
1160<hr/>
1161
1162* FFRT提供的类似std::condition_variable 的性能实现
1163
1164#### 声明
1165
1166```{.cpp}
1167namespace ffrt {
1168enum class cv_status {
1169    no_timeout,
1170    timeout
1171};
1172
1173class condition_variable {
1174public:
1175    using TimePoint = std::chrono::steady_clock::time_point;
1176    template<typename Clock, typename Duration, typename Pred>
1177    bool wait_until(std::unique_lock<mutex>& lk,
1178            const std::chrono::time_point<Clock, Duration>& tp,
1179            Pred&& pred) noexcept;
1180
1181    template<typename Clock, typename Duration>
1182    cv_status wait_until(std::unique_lock<mutex>& lk,
1183            const std::chrono::time_point<Clock, Duration>& tp) noexcept;
1184
1185    template<typename Rep, typename Period>
1186    cv_status wait_for(std::unique_lock<mutex>& lk,
1187            const std::chrono::duration<Rep, Period>& sleep_time) noexcept;
1188
1189    template<typename Rep, typename Period, typename Pred>
1190    bool wait_for(std::unique_lock<mutex>& lk,
1191            const std::chrono::duration<Rep, Period>& sleepTime,
1192            Pred&& pred) noexcept;
1193
1194    void wait(std::unique_lock<mutex>& lk);
1195
1196    template<typename Pred>
1197    void wait(std::unique_lock<mutex>& lk, Pred&& pred);
1198
1199    void notify_one() noexcept;
1200
1201    void notify_all() noexcept;
1202};
1203}
1204```
1205
1206#### 参数
1207
1208`lk`
1209* mutex互斥量
1210`tp`
1211* 等待时间
1212`sleep_time`
1213* 等待时间
1214`pred`
1215* 检查是否等待函数
1216#### 返回值
1217
1218* 不涉及
1219
1220#### 描述
1221* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1222* 该功能能够避免传统的std::condition_variable  在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能
1223
1224#### 样例
1225
1226```{.cpp}
1227#include <iostream>
1228#include "ffrt.h"
1229
1230void ffrt_cv_task()
1231{
1232    ffrt::condition_variable cond;
1233    int a = 0;
1234    ffrt::mutex lock_;
1235    ffrt::submit([&] {
1236        std::unique_lock lck(lock_);
1237        cond.wait(lck, [&] { return a == 1; });
1238        std::cout << "a = " << a << std::endl;
1239    }, {}, {});
1240    ffrt::submit([&] {
1241        std::unique_lock lck(lock_);
1242        a = 1;
1243        cond.notify_one();
1244    }, {}, {});
1245
1246    ffrt::wait();
1247}
1248
1249int main(int narg, char** argv)
1250{
1251    int r;
1252    ffrt::submit(ffrt_cv_task);
1253    ffrt::wait();
1254    return 0;
1255}
1256
1257```
1258
1259预期输出为:
1260
1261```
1262a=1
1263```
1264
1265* 该例子为功能示例,实际中并不鼓励这样使用
1266
1267## 杂项
1268
1269### sleep
1270
1271<hr/>
1272* FFRT提供的类似std::this_thread::sleep_for / std::this_thread::sleep_until 的性能实现
1273
1274#### 声明
1275
1276```{.cpp}
1277namespace ffrt {
1278namespace this_task {
1279template<class _Rep, class _Period>
1280void sleep_for(const std::chrono::duration<_Rep, _Period>& sleep_duration);
1281
1282template<class _Clock, class _Duration>
1283void sleep_until(const std::chrono::time_point<_Clock, _Duration>& sleep_time);
1284}
1285}
1286```
1287
1288#### 参数
1289
1290`sleep_duration`
1291
1292* 睡眠的时长
1293
1294`sleep_time`
1295
1296* 睡眠到达的时间点
1297
1298#### 返回值
1299
1300* 不涉及
1301
1302#### 描述
1303* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1304* 该功能能够避免传统的std::this_thread::sleep_for 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能
1305* 该接口调用后实际睡眠时长不小于配置值
1306
1307#### 样例
1308
1309```{.cpp}
1310#include <chrono>
1311#include <iostream>
1312#include "ffrt.h"
1313
1314using namespace std::chrono_literals;
1315int main(int narg, char** argv)
1316{
1317    ffrt::submit([] {
1318        std::cout << "Hello waiter\n" << std::flush;
1319        auto start = std::chrono::high_resolution_clock::now();
1320        ffrt::this_task::sleep_for(2000ms);
1321        auto end = std::chrono::high_resolution_clock::now();
1322        std::chrono::duration<double, std::milli> elapsed = end-start;
1323        std::cout << "Waited " << elapsed.count() << " ms\n";
1324    });
1325    ffrt::wait();
1326    return 0;
1327}
1328```
1329
1330* 预期输出为
1331
1332```
1333Hello waiter
1334Waited 2000.12 ms
1335```
1336
1337### yield
1338<hr/>
1339* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效
1340
1341#### 声明
1342
1343```{.cpp}
1344namespace ffrt {
1345namespace this_task {
1346void yield();
1347}
1348}
1349```
1350
1351#### 参数
1352
1353* 不涉及
1354
1355#### 返回值
1356
1357* 不涉及
1358
1359#### 描述
1360* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
1361* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态
1362
1363#### 样例
1364
1365```{.cpp}
1366#include <chrono>
1367#include "ffrt.h"
1368
1369using namespace std::chrono_literals;
1370// "busy sleep" while suggesting that other tasks run
1371// for a small amount of time
1372void little_sleep(std::chrono::microseconds us)
1373{
1374    auto start = std::chrono::high_resolution_clock::now();
1375    auto end = start + us;
1376    do {
1377        ffrt::this_task::yield();
1378    } while (std::chrono::high_resolution_clock::now() < end);
1379}
1380
1381int main(int narg, char** argv)
1382{
1383    ffrt::submit([] { little_sleep(200us); });
1384    ffrt::wait();
1385    return 0;
1386}
1387```
1388
1389* 这是一个`busy sleep`,同时允许其他可以被执行的task 插入执行
1390
1391
1392# C API
1393
1394> C API采用接近C11/pthread (https://zh.cppreference.com/w/c) 的命名风格,并冠以`ffrt_`前缀,以`_base`为后缀的API是内部API,通常不被用户直接调用
1395>
1396> **出于易用性方面的考虑,除非必要,强烈建议你使用C++ API(亦满足二进制兼容要求),调用C API将会使你的代码非常臃肿**
1397
1398## 任务管理
1399
1400### ffrt_submit_base
1401
1402* 该接口为ffrt动态库的导出接口,基于此可以封装出不同的C++ API ffrt::submit和C API ffrt_submit,满足二进制兼容
1403
1404#### 声明
1405
1406```{.cpp}
1407const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t);
1408typedef enum {
1409    ffrt_function_kind_general,
1410    ffrt_function_kind_queue
1411} ffrt_function_kind_t;
1412
1413void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind);
1414
1415typedef void(*ffrt_function_t)(void*);
1416typedef struct {
1417    ffrt_function_t exec;
1418    ffrt_function_t destroy;
1419    uint64_t reserve[2];
1420} ffrt_function_header_t;
1421
1422void 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);
1423```
1424
1425#### 参数
1426
1427`kind`
1428
1429* function子类型,用于优化内部数据结构,默认使用ffrt_function_kind_general类型
1430
1431`func`
1432
1433* CPU Function的指针,该指针执行的数据结构,按照`ffrt_function_header_t`定义的描述了该CPU Task如何执行和销毁的函数指针,FFRT通过这两个函数指针完成Task的执行和销毁
1434
1435`in_deps`
1436
1437* 同ffrt_submit
1438
1439
1440`out_deps`
1441
1442* 同ffrt_submit
1443
1444`attr`
1445
1446* 同ffrt_submit
1447
1448#### 返回值
1449
1450* 不涉及
1451
1452#### 描述
1453
1454* ffrt_submit_base不建议用户直接调用,推荐使用基于此封装的C++接口(亦满足二进制兼容)
1455* **ffrt_submit_base作为底层能力,只有在用户需要自定义task类型时使用,使用时需要满足以下限制:**
1456  * ffrt_submit_base入参中的func指针只能通过ffrt_alloc_auto_managed_function_storage_base申请,且二者的调用需一一对应
1457  * ffrt_alloc_auto_managed_function_storage_base申请的内存为ffrt_auto_managed_function_storage_size字节,其生命周期归ffrt管理,在该task结束时,由FFRT自动释放,用户无需释放
1458* ffrt_function_header_t 中定义了两个函数指针:
1459  * exec:用于描述该Task如何被执行,当FFRT需要执行该Task时由FFRT调用
1460  * destroy:用于描述该Task如何被执行,当FFRT需要执行该Task时由FFRT调用
1461
1462#### 样例
1463
1464* 通过该接口提供C++11 Lambda表达式的支持(该代码已经在ffrr.h中提供,默认支持)
1465
1466```{.cpp}
1467template<class T>
1468struct function {
1469    template<class CT>
1470    function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
1471    ffrt_function_header_t header;
1472    T closure;
1473};
1474
1475template<class T>
1476void exec_function_wrapper(void* t)
1477{
1478    auto f = (function<std::decay_t<T>>*)t;
1479    f->closure();
1480}
1481
1482template<class T>
1483void destroy_function_wrapper(void* t)
1484{
1485    auto f = (function<std::decay_t<T>>*)t;
1486    f->closure = nullptr;
1487}
1488
1489template<class T>
1490inline ffrt_function_header_t* create_function_wrapper(T&& func)
1491{
1492    using function_type = function<std::decay_t<T>>;
1493    static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size,
1494        "size of function must be less than ffrt_auto_managed_function_storage_size");
1495
1496    auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1497    auto f = new (p) function_type(
1498        {exec_function_wrapper<T>, destroy_function_wrapper<T>},
1499        std::forward<T>(func));
1500    return (ffrt_function_header_t*)f;
1501}
1502
1503static inline void submit(std::function<void()>&& func)
1504{
1505    return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL);
1506}
1507```
1508
1509### ffrt_wait
1510
1511<hr/>
1512* 同步等待,与ffrt_submit 配合使用
1513* 等待指定的数据被生产完成,或等待当前任务的所有子任务完成,在不满足条件之前,当前的执行上下文被suspend,在满足条件后恢复执行
1514
1515#### 声明
1516
1517```{.cpp}
1518void ffrt_wait_deps(ffrt_deps_t* deps);
1519void ffrt_wait();
1520```
1521
1522#### 参数
1523
1524`deps`
1525
1526* 需要等待被生产完成的数据的虚拟地址,这些地址可能作为某些任务在submit 时的out_deps,该依赖的生成见ffrt_deps_t章节,空指针表示无依赖
1527
1528#### 返回值
1529
1530* 不涉及
1531
1532#### 描述
1533* ffrt_wait_deps(deps) 用于等待deps指代的数据被生产完成才能执行后面的代码
1534* ffrt_wait() 用于等待当前上下文提交的所有子任务(`注意:不包括孙任务和下级子任务`)都完成才能执行后面的代码
1535* 该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用
1536* 在FFRT task 外部调用的wait 是OS 能够感知的等待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部
1537
1538#### 样例
1539
1540**recursive fibonacci**
1541
1542串行版的fibonacci 可以实现为:
1543
1544```{.c}
1545#include <stdio.h>
1546
1547void fib(int x, int* y) {
1548    if (x <= 1) {
1549        *y = x;
1550    } else {
1551        int y1, y2;
1552        fib(x - 1, &y1);
1553        fib(x - 2, &y2);
1554        *y = y1 + y2;
1555    }
1556}
1557int main(int narg, char** argv)
1558{
1559    int r;
1560    fib(10, &r);
1561    printf("fibonacci 10: %d\n", r);
1562    return 0;
1563}
1564```
1565
1566若要使用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加速的意义,但这种调用pattern 对并行编程模型的灵活性考验是非常高的),其中1种可行的实现为:
1567
1568```{.c}
1569#include <stdio.h>
1570#include "ffrt.h"
1571
1572typedef struct {
1573    int x;
1574    int* y;
1575} fib_ffrt_s;
1576
1577typedef struct {
1578    ffrt_function_header_t header;
1579    ffrt_function_t func;
1580    ffrt_function_t after_func;
1581    void* arg;
1582} c_function;
1583
1584static void ffrt_exec_function_wrapper(void* t)
1585{
1586    c_function* f = (c_function*)t;
1587    if (f->func) {
1588        f->func(f->arg);
1589    }
1590}
1591
1592static void ffrt_destroy_function_wrapper(void* t)
1593{
1594    c_function* f = (c_function*)t;
1595    if (f->after_func) {
1596        f->after_func(f->arg);
1597    }
1598}
1599
1600#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1601static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1602    const ffrt_function_t after_func, void* arg)
1603{
1604    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1605        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1606    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1607    f->header.exec = ffrt_exec_function_wrapper;
1608    f->header.destroy = ffrt_destroy_function_wrapper;
1609    f->func = func;
1610    f->after_func = after_func;
1611    f->arg = arg;
1612    return (ffrt_function_header_t*)f;
1613}
1614
1615static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1616    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1617{
1618    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1619}
1620
1621#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \
1622    ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name}
1623
1624void fib_ffrt(void* arg)
1625{
1626    fib_ffrt_s* p = (fib_ffrt_s*)arg;
1627    int x = p->x;
1628    int* y = p->y;
1629
1630    if (x <= 1) {
1631        *y = x;
1632    } else {
1633        int y1, y2;
1634        fib_ffrt_s s1 = {x - 1, &y1};
1635        fib_ffrt_s s2 = {x - 2, &y2};
1636        ffrt_deps_define(dx, &x);
1637        ffrt_deps_define(dy1, &y1);
1638        ffrt_deps_define(dy2, &y2);
1639        ffrt_deps_define(dy12, &y1, &y2);
1640        ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL);
1641        ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL);
1642        ffrt_wait_deps(&dy12);
1643        *y = y1 + y2;
1644    }
1645}
1646
1647int main(int narg, char** argv)
1648{
1649    int r;
1650    fib_ffrt_s s = {10, &r};
1651    ffrt_deps_define(dr, &r);
1652    ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL);
1653    ffrt_wait_deps(&dr);
1654    printf("fibonacci 10: %d\n", r);
1655    return 0;
1656}
1657```
1658
1659`解析`:
1660
16611)   将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将结果累加;
1662
16632)   虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继续拆分,因此,整个计算图的并行度是非常高的,Task 之间在FFRT 内部形成了一颗调用树;
1664
1665<img src="images/image-20220926152331554.png" style="zoom:100%" />
1666
1667> 以上实现,逻辑上虽与C++ API中的实现类似,但是用户显式管理数据生命周期和函数入参打包两个因素将使代码异常复杂
1668
1669
1670
1671### ffrt_deps_t
1672
1673* C API中对依赖数组的抽象,逻辑上等同于C++ API中的`std::vector<void*>`
1674
1675#### 声明
1676
1677```{.cpp}
1678typedef struct {
1679    uint32_t len;
1680    const void* const * items;
1681} ffrt_deps_t;
1682```
1683
1684#### 参数
1685
1686`len`
1687
1688* 所依赖的Signature的个数,取值大于等于0
1689
1690`item`
1691
1692* len个Signature的起始地址指针
1693
1694#### 返回值
1695
1696* 不涉及
1697
1698#### 描述
1699
1700* item为len个Signature的起始指针,该指针可以指向堆空间,也可以指向栈空间,但是要求分配的空间大于等于len * sizeof(void*)
1701
1702#### 样例
1703
1704* item指向栈空间的ffrt_deps_t
1705
1706```{.c}
1707#include "ffrt.h"
1708
1709int main(int narg, char** argv)
1710{
1711    int x1 = 1;
1712    int x2 = 2;
1713
1714    void *t[] = {&x1, &x2};
1715    ffrt_deps_t deps = {2, (const void* const *)&t};
1716    // some code use deps
1717    return 0;
1718}
1719```
1720
1721* item指向栈空间的ffrt_deps_t
1722
1723```{.c}
1724#include <stdlib.h>
1725#include "ffrt.h"
1726
1727int main(int narg, char** argv)
1728{
1729    int x1 = 1;
1730    int x2 = 2;
1731
1732    void** t = (void**)malloc(sizeof(void*) * 2);
1733    t[0]= &x1;
1734    t[1]= &x2;
1735    ffrt_deps_t deps = {2, t};
1736
1737    // some code use deps
1738    free(t);
1739    return 0;
1740}
1741```
1742
1743### ffrt_task_attr_t
1744
1745<hr/>
1746* 定义task 的属性的辅助类,与ffrt_submit 配合使用
1747
1748#### 声明
1749
1750```{.c}
1751typedef enum {
1752    ffrt_qos_inherent = -1,
1753    ffrt_qos_background,
1754    ffrt_qos_utility,
1755    ffrt_qos_default,
1756    ffrt_qos_user_initiated,
1757} ffrt_qos_t;
1758
1759typedef struct {
1760    char storage[ffrt_task_attr_storage_size];
1761} ffrt_task_attr_t;
1762typedef void* ffrt_task_handle_t;
1763
1764int ffrt_task_attr_init(ffrt_task_attr_t* attr);
1765void ffrt_task_attr_destroy(ffrt_task_attr_t* attr);
1766void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos);
1767ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr);
1768void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name);
1769const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr);
1770```
1771
1772#### 参数
1773
1774`attr`
1775
1776* 创建的tasks属性的句柄
1777
1778`qos`
1779
1780* qos 设定的枚举类型
1781* inherent 是一个qos 设定策略,代表即将ffrt_submit 的task 的qos 继承当前task 的qos
1782
1783#### 返回值
1784
1785* 不涉及
1786
1787#### 描述
1788* `attr`所传递的内容会在ffrt_submit内部完成取存,ffrt_submit返回后用户即可销毁
1789* 约定
1790  * 在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为`ffrt_qos_default`
1791  * 在submit 时,如果通过task_attr 设定qos 为`ffrt_qos_inherent`,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为`ffrt_qos_inherent` 的task,其qos 为`ffrt_qos_default`
1792  * 其他情况下,该提交的task 的qos 被设定为指定的值
1793* ffrt_task_attr_t对象的置空和销毁由用户完成,对同一个ffrt_task_attr_t仅能调用一次`ffrt_task_attr_destroy`,重复对同一个ffrt_task_attr_t调用`ffrt_task_attr_destroy`,其行为是未定义的
1794* 在`ffrt_task_attr_destroy`之后再对task_attr进行访问,其行为是未定义的
1795
1796#### 样例
1797
1798```{.c}
1799#include <stdio.h>
1800#include "ffrt.h"
1801
1802void my_print(void* arg)
1803{
1804    printf("hello ffrt\n");
1805}
1806
1807typedef struct {
1808    ffrt_function_header_t header;
1809    ffrt_function_t func;
1810    ffrt_function_t after_func;
1811    void* arg;
1812} c_function;
1813
1814static void ffrt_exec_function_wrapper(void* t)
1815{
1816    c_function* f = (c_function*)t;
1817    if (f->func) {
1818        f->func(f->arg);
1819    }
1820}
1821
1822static void ffrt_destroy_function_wrapper(void* t)
1823{
1824    c_function* f = (c_function*)t;
1825    if (f->after_func) {
1826        f->after_func(f->arg);
1827    }
1828}
1829
1830#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1831static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1832    const ffrt_function_t after_func, void* arg)
1833{
1834    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1835        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1836    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1837    f->header.exec = ffrt_exec_function_wrapper;
1838    f->header.destroy = ffrt_destroy_function_wrapper;
1839    f->func = func;
1840    f->after_func = after_func;
1841    f->arg = arg;
1842    return (ffrt_function_header_t*)f;
1843}
1844
1845static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1846    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1847{
1848    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1849}
1850
1851int main(int narg, char** argv)
1852{
1853    ffrt_task_attr_t attr;
1854    ffrt_task_attr_init(&attr);
1855    ffrt_task_attr_set_qos(&attr, ffrt_qos_background);
1856    ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr);
1857    ffrt_task_attr_destroy(&attr);
1858    ffrt_wait();
1859    return 0;
1860}
1861```
1862
1863* 提交一个qos 级别为background 的任务
1864
1865
1866
1867### ffrt_submit_h
1868
1869<hr/>
1870
1871* 向调度器提交一个task,与ffrt_submit 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
1872
1873#### 声明
1874
1875```{.cpp}
1876typedef void* ffrt_task_handle_t;
1877
1878ffrt_task_handle_t ffrt_submit_h(ffrt_function_t func, void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr);
1879void ffrt_task_handle_destroy(ffrt_task_handle_t handle);
1880```
1881
1882#### 参数
1883
1884`func`
1885
1886* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1887
1888`in_deps`
1889
1890* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1891
1892`out_deps`
1893
1894* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1895
1896`attr`
1897
1898* 同ffrt_submit,详见[ffrt_submit](#ffrt_submit) 定义
1899
1900#### 返回值
1901
1902* task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步
1903
1904#### 描述
1905
1906* C API中的ffrt_task_handle_t的使用与C++ API中的ffrt::task_handle相同
1907* **差异在于:C API中的ffrt_task_handle_t需要用户调用`ffrt_task_handle_destroy`显式销毁,而C++ API无需该操作**
1908* C API中的task_handle_t对象的置空和销毁由用户完成,对同一个ffrt_task_handle_t仅能调用一次`ffrt_task_handle_destroy`,重复对同一个ffrt_task_handle_t调用`ffrt_task_handle_destroy`,其行为是未定义的
1909* 在`ffrt_task_handle_destroy`之后再对ffrt_task_handle_t进行访问,其行为是未定义的
1910
1911#### 样例
1912
1913```{.c}
1914#include <stdio.h>
1915#include "ffrt.h"
1916
1917void func0(void* arg)
1918{
1919    printf("hello ");
1920}
1921
1922void func1(void* arg)
1923{
1924    (*(int*)arg)++;
1925}
1926
1927void func2(void* arg)
1928{
1929    printf("world, x = %d\n", *(int*)arg);
1930}
1931
1932void func3(void* arg)
1933{
1934    printf("handle wait");
1935    (*(int*)arg)++;
1936}
1937
1938typedef struct {
1939    ffrt_function_header_t header;
1940    ffrt_function_t func;
1941    ffrt_function_t after_func;
1942    void* arg;
1943} c_function;
1944
1945static void ffrt_exec_function_wrapper(void* t)
1946{
1947    c_function* f = (c_function*)t;
1948    if (f->func) {
1949        f->func(f->arg);
1950    }
1951}
1952
1953static void ffrt_destroy_function_wrapper(void* t)
1954{
1955    c_function* f = (c_function*)t;
1956    if (f->after_func) {
1957        f->after_func(f->arg);
1958    }
1959}
1960
1961#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
1962static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
1963    const ffrt_function_t after_func, void* arg)
1964{
1965    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
1966        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
1967    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
1968    f->header.exec = ffrt_exec_function_wrapper;
1969    f->header.destroy = ffrt_destroy_function_wrapper;
1970    f->func = func;
1971    f->after_func = after_func;
1972    f->arg = arg;
1973    return (ffrt_function_header_t*)f;
1974}
1975
1976static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func,
1977    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1978{
1979    return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1980}
1981
1982static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
1983    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
1984{
1985    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
1986}
1987
1988#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \
1989    ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name}
1990
1991int main(int narg, char** argv)
1992{
1993    // handle work with submit
1994    ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task
1995    int x = 1;
1996    ffrt_deps_define(d1, &x);
1997    ffrt_deps_define(d2, &x, h);
1998    ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL);
1999    ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h
2000    ffrt_task_handle_destroy(h);
2001
2002    // handle work with wait
2003    ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL);
2004    ffrt_deps_define(d3, h2);
2005    ffrt_wait_deps(&d3);
2006    ffrt_task_handle_destroy(h2);
2007    printf("x = %d", x);
2008    ffrt_wait();
2009    return 0;
2010}
2011```
2012
2013* 预期的输出为
2014
2015```
2016hello world, x = 2
2017handle wait
2018x = 3
2019```
2020
2021
2022
2023### ffrt_this_task_get_id
2024
2025<hr/>
2026
2027* 返回当前task的id标识,更多使用用于维测(原因是task name可能重名)
2028
2029#### 声明
2030
2031```{.c}
2032uint64_t ffrt_this_task_get_id();
2033```
2034
2035#### 参数
2036
2037* 不涉及
2038
2039#### 返回值
2040
2041* 当前task的id
2042
2043#### 描述
2044
2045* 该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0
2046* 可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还是非FFRT工作线程上
2047* task id为从1开始编码,每提交一个task便增加1,被设计成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转
2048
2049#### 样例
2050
2051* 忽略
2052
2053
2054
2055### ffrt_this_task_update_qos
2056
2057<hr/>
2058
2059* 更新当前正在执行的task的优先级
2060
2061#### 声明
2062
2063```{.cpp}
2064int ffrt_this_task_update_qos(ffrt_qos_t qos);
2065```
2066
2067#### 参数
2068
2069* `qos` 新的优先级
2070
2071#### 返回值
2072
2073* 0表示成功,非0表示失败
2074
2075#### 描述
2076
2077* 该接口对当前task的qos调整会立即生效
2078* 如果新设定的qos与当前的qos不一致,则会block当前task的执行,再按照新的qos恢复执行
2079* 如果新设定的qos与当前的qos一致,则接口会立即返回0,不做任何处理
2080* **如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理**
2081
2082#### 样例
2083
2084* 忽略
2085
2086## 串行队列
2087<hr />
2088
2089基本功能与使用约束见 C++ API 中的串行队列部分
2090
2091### ffrt_queue_t
2092<hr/>
2093
2094#### 描述
2095FFRT串行队列 C API,提供提交任务、取消任务、等待任务执行完成等功能
2096
2097#### 声明
2098
2099```{.cpp}
2100typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t;
2101typedef void* ffrt_queue_t;
2102
2103ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr);
2104void ffrt_queue_destroy(ffrt_queue_t queue);
2105
2106void ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr);
2107ffrt_task_handle_t ffrt_queue_submit_h(
2108    ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr);
2109
2110void ffrt_queue_wait(ffrt_task_handle_t handle);
2111
2112int ffrt_queue_cancel(ffrt_task_handle_t handle);
2113```
2114
2115#### 方法
2116
2117##### ffrt_queue_create
2118
2119```{.cpp}
2120ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr);
2121```
2122
2123* 描述:创建串行队列
2124
2125* 参数:
2126
2127  `type`:用于描述创建的队列类型,串行队列对应 `type` 为 `ffrt_queue_serial`
2128
2129  `name`:用于描述创建的队列名称
2130
2131  `attr`:所创建的queue属性,若未设定则会使用默认值
2132
2133* 返回值:如果成功创建了队列,则返回一个非空的队列句柄;否则返回空指针
2134
2135##### ffrt_queue_destroy
2136
2137```{.cpp}
2138void ffrt_queue_destroy(ffrt_queue_t queue);
2139```
2140
2141* 描述:销毁串行队列
2142
2143* 参数:
2144
2145  `queue`:想要销毁的队列的句柄
2146
2147* 返回值:不涉及
2148
2149##### ffrt_queue_submit
2150
2151```{.cpp}
2152void ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr);
2153```
2154
2155* 描述:提交一个任务到队列中调度执行
2156
2157* 参数:
2158
2159  `queue`:串行队列的句柄
2160
2161  `f`:任务执行指针
2162
2163  `attr`:所创建的queue属性
2164
2165* 返回值:不涉及
2166
2167##### ffrt_queue_submit_h
2168
2169```{.cpp}
2170ffrt_task_handle_t ffrt_queue_submit_h(
2171    ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr);
2172```
2173
2174* 描述:提交一个任务到队列中调度执行,并返回任务句柄
2175
2176* 参数:
2177
2178  `queue`:串行队列的句柄
2179
2180  `f`:任务执行指针
2181
2182  `attr`:所创建的queue属性
2183
2184* 返回值:如果任务被提交,则返回一个非空的任务句柄;否则返回空指针
2185
2186##### ffrt_queue_wait
2187
2188```{.cpp}
2189void ffrt_queue_wait(ffrt_task_handle_t handle);
2190```
2191
2192* 描述:等待串行队列中一个任务执行完成
2193
2194* 参数:
2195
2196  `handle`:任务的句柄
2197
2198* 返回值:不涉及
2199
2200##### ffrt_queue_cancel
2201
2202```{.cpp}
2203int ffrt_queue_cancel(ffrt_task_handle_t handle);
2204```
2205
2206* 描述:取消队列中一个任务。必须使用submit_h后拿到的task_handle,否则会报异常;任务开始执行后则无法取消,仅能成功取消未开始执行的任务
2207
2208* 参数:
2209
2210  `handle`:任务的句柄
2211
2212* 返回值:若成功返回0,否则返回其他非0值
2213
2214#### 样例
2215
2216```{.cpp}
2217#include <stdio.h>
2218#include "ffrt.h"
2219
2220using namespace ffrt;
2221using namespace std;
2222
2223int main(int narg, char** argv)
2224{
2225    ffrt_queue_attr_t queue_attr;
2226    // 1、初始化队列属性,必需
2227    (void)ffrt_queue_attr_init(&queue_attr);
2228
2229    // 2、创建串行队列,并返回队列句柄queue_handle
2230    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
2231
2232    int result = 0;
2233    std::function<void()>&& basicFunc = [&result]() { result += 1; };
2234
2235    // 3、提交串行任务
2236    ffrt_queue_submit(queue_handle, create_function_wrapper(basicFunc, ffrt_function_kind_queue), nullptr);
2237
2238    // 4、提交出啊逆行任务,并返回任务句柄
2239    ffrt_task_handle_t t1 = ffrt_queue_submit_h(queue_handle, create_function_wrapper(basicFunc, ffrt_function_kind_queue), nullptr);
2240    // 5、等待指定任务执行完成
2241    ffrt_queue_wait(t1);
2242
2243    ffrt_task_handle_t t2 = ffrt_queue_submit_h(queue_handle, create_function_wrapper(basicFunc, ffrt_function_kind_queue), nullptr);
2244    // 6、取消句柄为t2的任务
2245    int ret = ffrt_queue_cancel(t2);
2246
2247    // 7、销毁提交给串行队列任务的句柄t1和t2,必需
2248    ffrt_task_handle_destroy(t1);
2249    ffrt_task_handle_destroy(t2);
2250    // 8、销毁队列属性,必需
2251    ffrt_queue_attr_destroy(&queue_attr);
2252    // 9、销毁队列句柄,必需
2253    ffrt_queue_destroy(queue_handle);
2254}
2255```
2256
2257### ffrt_queue_attr_t
2258<hr/>
2259
2260#### 描述
2261FFRT串行队列 C API,提供设置与获取串行队列优先级、设置与获取串行队列任务执行超时时间、设置与获取串行队列超时回调函数等功能
2262
2263#### 声明
2264
2265```{.cpp}
2266typedef struct {
2267    uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
2268} ffrt_queue_attr_t;
2269
2270int ffrt_queue_attr_init(ffrt_queue_attr_t* attr);
2271void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr);
2272
2273void ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos);
2274ffrt_qos_t ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr);
2275
2276void ffrt_queue_attr_set_timeout(ffrt_queue_attr_t* attr, uint64_t timeout_us);
2277uint64_t ffrt_queue_attr_get_timeout(const ffrt_queue_attr_t* attr);
2278
2279void ffrt_queue_attr_set_callback(ffrt_queue_attr_t* f);
2280ffrt_function_header_t* ffrt_queue_attr_get_callback(const ffrt_queue_attr_t* attr);
2281```
2282
2283#### 方法
2284
2285##### ffrt_queue_attr_init
2286```{.cpp}
2287int ffrt_queue_attr_init(ffrt_queue_attr_t* attr);
2288```
2289
2290* 描述:初始化串行队列的属性
2291
2292* 参数:
2293
2294  `attr`:已初始化的串行队列属性
2295
2296* 返回值:若成功返回0,否则返回-1
2297
2298##### ffrt_queue_attr_destroy
2299```{.cpp}
2300void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr);
2301```
2302
2303* 描述:销毁串行队列的属性
2304
2305  ffrt_queue_attr_t对象的置空和销毁由用户完成,对同一个ffrt_queue_t仅能调用一次 `ffrt_queue_attr_destroy` ,重复对同一个ffrt_queue_t调用 `ffrt_queue_attr_destroy` ,其行为是未定义的
2306
2307  在`ffrt_queue_attr_destroy`之后再对ffrt_queue_t进行访问,其行为是未定义的
2308
2309* 参数:
2310
2311  `attr`:所创建的串行队列属性
2312
2313* 返回值:不涉及
2314
2315##### ffrt_queue_attr_set_qos
2316```{.cpp}
2317void ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos);
2318```
2319
2320* 描述:设置串行队列qos属性,默认为default等级
2321
2322* 参数:
2323
2324  `attr`:所创建的串行队列属性
2325
2326  `qos`:串行队列优先级
2327
2328* 返回值:不涉及
2329
2330##### ffrt_queue_attr_get_qos
2331```{.cpp}
2332ffrt_qos_t ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr);
2333```
2334
2335* 描述:获取串行队列qos属性
2336
2337* 参数:
2338
2339  `attr`:所创建的串行队列属性
2340
2341* 返回值:所设置的串行队列的qos等级,默认为default等级
2342
2343##### ffrt_queue_attr_set_timeout
2344```{.cpp}
2345void ffrt_queue_attr_set_timeout(ffrt_queue_attr_t* attr, uint64_t timeout_us);
2346```
2347
2348* 描述:设置串行队列任务执行超时时间
2349
2350* 参数:
2351
2352  `attr`:所创建的串行队列属性
2353
2354  `timeout_us`:串行队列任务执行超时时间,单位为us
2355
2356* 返回值:不涉及
2357
2358##### ffrt_queue_attr_get_timeout
2359```{.cpp}
2360uint64_t ffrt_queue_attr_get_timeout(const ffrt_queue_attr_t* attr);
2361```
2362
2363* 描述:获取串行队列任务执行超时时间
2364
2365* 参数:
2366
2367  `attr`:所创建的串行队列属性
2368
2369* 返回值:串行队列任务执行超时时间,单位为us
2370
2371##### ffrt_queue_attr_set_callback
2372```{.cpp}
2373void ffrt_queue_attr_set_callback(ffrt_queue_attr_t* f);
2374```
2375
2376* 描述:设置串行队列超时回调函数
2377
2378* 参数:
2379
2380  `attr`:所创建的串行队列属性
2381
2382  `f`:串行队列超时回调函数
2383
2384* 返回值:不涉及
2385
2386##### ffrt_queue_attr_get_callback
2387```{.cpp}
2388ffrt_function_header_t* ffrt_queue_attr_get_callback(const ffrt_queue_attr_t* attr);
2389```
2390
2391* 描述:获取串行队列超时回调函数
2392
2393* 参数:
2394
2395  `attr`:所创建的串行队列属性
2396
2397* 返回值:串行队列超时回调函数
2398
2399#### 样例
2400```
2401#include <stdio.h>
2402#include "ffrt.h"
2403
2404using namespace ffrt;
2405using namespace std;
2406
2407int main(int narg, char** argv)
2408{
2409    ffrt_queue_attr_t queue_attr;
2410    // 1、初始化串行队列属性,必需
2411    int result = ffrt_queue_attr_init(&queue_attr);
2412
2413    int x = 0;
2414    std::function<void()>&& basicFunc = [&x]() { x += 1; };
2415
2416    // 2、可设置队列优先级,默认为default等级
2417    ffrt_queue_attr_set_qos(&queue_attr, static_cast<int>(ffrt_qos_utility));
2418    int qos = ffrt_queue_attr_get_qos(&queue_attr);
2419
2420    // 3、可通过设置timeout打开队列任务超时监测,默认不设置(关闭)
2421    ffrt_queue_attr_set_timeout(&queue_attr, 10000);
2422    uint64_t time = ffrt_queue_attr_get_timeout(&queue_attr);
2423
2424    // 4、超时会打印Error日志并执行用户设置的callback(可选)
2425    ffrt_queue_attr_set_callback(&queue_attr, ffrt::create_function_wrapper(basicFunc, ffrt_function_kind_queue));
2426    ffrt_function_header_t* func = ffrt_queue_attr_get_callback(&queue_attr);
2427
2428    // 5、销毁串行队列属性,必需
2429    ffrt_queue_attr_destroy(&queue_attr);
2430}
2431```
2432
2433
2434## 同步原语
2435
2436### ffrt_mutex_t
2437<hr/>
2438* FFRT提供的类似pthread mutex 的性能实现
2439
2440#### 声明
2441
2442```{.cpp}
2443typedef enum {
2444    ffrt_error = -1,
2445    ffrt_success = 0,
2446    ffrt_error_nomem = ENOMEM,
2447    ffrt_error_timedout = ETIMEDOUT,
2448    ffrt_error_busy = EBUSY,
2449    ffrt_error_inval = EINVAL
2450} ffrt_error_t;
2451
2452struct ffrt_mutex_t;
2453
2454struct ffrt_mutexattr_t;
2455
2456typedef enum {
2457    ffrt_mutex_normal = 0,
2458    ffrt_mutex_recursive = 2,
2459    ffrt_mutex_default = ffrt_mutex_normal
2460} ffrt_mutex_type;
2461
2462int ffrt_mutexattr_init(ffrt_mutexattr_t* attr);
2463int ffrt_mutexattr_settype(ffrt_mutexattr_t* attr, int type);
2464int ffrt_mutexattr_gettype(ffrt_mutexattr_t* attr, int* type);
2465int ffrt_mutexattr_destroy(ffrt_mutexattr_t* attr);
2466int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr);
2467int ffrt_mutex_lock(ffrt_mutex_t* mutex);
2468int ffrt_mutex_unlock(ffrt_mutex_t* mutex);
2469int ffrt_mutex_trylock(ffrt_mutex_t* mutex);
2470int ffrt_mutex_destroy(ffrt_mutex_t* mutex);
2471```
2472
2473#### 参数
2474`type`
2475
2476* FFRT锁类型,当前仅支持互斥锁ffrt_mutex_normal和递归锁ffrt_mutex_recursive
2477
2478`attr`
2479
2480* FFRT锁属性,attr如果为空指针代表互斥锁mutex
2481
2482`mutex`
2483
2484* 指向所操作的锁指针
2485
2486#### 返回值
2487
2488* 若成功则为 ffrt_success ,否则发生错误
2489
2490#### 描述
2491* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2492* 该功能能够避免pthread传统的pthread_mutex_t 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能
2493* **注意:目前暂不支持定时功能**
2494* **注意:C API中的ffrt_mutexattr_t需要用户调用`ffrt_mutexattr_init`和`ffrt_mutexattr_destroy`显示创建和销毁,而C++ API无需该操作**
2495* **注意:C API中的ffrt_mutex_t需要用户调用`ffrt_mutex_init`和`ffrt_mutex_destroy`显式创建和销毁,而C++ API无需该操作**
2496* **注意:C API中的ffrt_mutex_t对象的置空和销毁由用户完成,对同一个ffrt_mutex_t仅能调用一次`ffrt_mutex_destroy`,重复对同一个ffrt_mutex_t调用`ffrt_mutex_destroy`,其行为是未定义的**
2497* **注意:C API中的同一个ffrt_mutexattr_t只能调用一次`ffrt_mutexattr_init`和`ffrt_mutexattr_destroy`,重复调用其行为是未定义的**
2498* **注意:用户需要在调用`ffrt_mutex_init`之后和调用`ffrt_mutex_destroy`之前显示调用`ffrt_mutexattr_destroy`**
2499* **注意:在`ffrt_mutex_destroy`之后再对ffrt_mutex_t进行访问,其行为是未定义的**
2500
2501#### 样例
2502
2503```{.c}
2504#include <stdio.h>
2505#include "ffrt.h"
2506
2507typedef struct {
2508    int* sum;
2509    ffrt_mutex_t* mtx;
2510} tuple;
2511
2512void func(void* arg)
2513{
2514    tuple* t = (tuple*)arg;
2515
2516    int ret = ffrt_mutex_lock(t->mtx);
2517    if (ret != ffrt_success) {
2518        printf("error\n");
2519    }
2520    (*t->sum)++;
2521    ret = ffrt_mutex_unlock(t->mtx);
2522    if (ret != ffrt_success) {
2523        printf("error\n");
2524    }
2525}
2526
2527typedef struct {
2528    ffrt_function_header_t header;
2529    ffrt_function_t func;
2530    ffrt_function_t after_func;
2531    void* arg;
2532} c_function;
2533
2534static void ffrt_exec_function_wrapper(void* t)
2535{
2536    c_function* f = (c_function*)t;
2537    if (f->func) {
2538        f->func(f->arg);
2539    }
2540}
2541
2542static void ffrt_destroy_function_wrapper(void* t)
2543{
2544    c_function* f = (c_function*)t;
2545    if (f->after_func) {
2546        f->after_func(f->arg);
2547    }
2548}
2549
2550#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
2551static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
2552    const ffrt_function_t after_func, void* arg)
2553{
2554    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
2555        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
2556    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
2557    f->header.exec = ffrt_exec_function_wrapper;
2558    f->header.destroy = ffrt_destroy_function_wrapper;
2559    f->func = func;
2560    f->after_func = after_func;
2561    f->arg = arg;
2562    return (ffrt_function_header_t*)f;
2563}
2564
2565static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
2566    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
2567{
2568    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
2569}
2570
2571void ffrt_mutex_task()
2572{
2573    int sum = 0;
2574    ffrt_mutex_t mtx;
2575    tuple t = {&sum, &mtx};
2576    int ret = ffrt_mutex_init(&mtx, NULL);
2577    if (ret != ffrt_success) {
2578        printf("error\n");
2579    }
2580    for (int i = 0; i < 10; i++) {
2581        ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL);
2582    }
2583    ffrt_mutex_destroy(&mtx);
2584    ffrt_wait();
2585    printf("sum = %d", sum);
2586}
2587
2588void ffrt_recursive_mutex_task()
2589{
2590    int sum = 0;
2591    int ret = 0;
2592    ffrt_mutexattr_t attr;
2593    ffrt_mutex_t mtx;
2594    ret = ffrt_mutexattr_init(&attr);
2595    if (ret != ffrt_success) {
2596        printf("mutexattr init error\n");
2597    }
2598    ret = ffrt_mutexattr_settype(&attr, ffrt_mutex_recursive);
2599    if (ret != ffrt_success) {
2600        printf("mutexattr settype error\n");
2601    }
2602    tuple t = {&sum, &mtx};
2603    int ret = ffrt_mutex_init(&mtx, &attr);
2604    if (ret != ffrt_success) {
2605        printf("error\n");
2606    }
2607    for (int i = 0; i < 10; i++) {
2608        ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL);
2609    }
2610    ffrt_mutexattr_destory(&attr);
2611    ffrt_mutex_destroy(&mtx);
2612    ffrt_wait();
2613    printf("sum = %d", sum);
2614}
2615
2616int main(int narg, char** argv)
2617{
2618    int r;
2619    /* mutex */
2620    ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL);
2621    ffrt_wait();
2622    /* recursive mutex */
2623    ffrt_submit_c(ffrt_recursive_mutex_task, NULL, NULL, NULL, NULL, NULL);
2624    ffrt_wait();
2625    return 0;
2626}
2627```
2628
2629预期输出为
2630
2631```
2632sum=10
2633```
2634
2635* 该例子为功能示例,实际中并不鼓励这样使用
2636
2637### ffrt_rwlock_t
2638<hr/>
2639* FFRT提供的类似pthread rwlock 的性能实现
2640
2641#### 声明
2642
2643```{.cpp}
2644typedef enum {
2645    ffrt_error = -1,
2646    ffrt_success = 0,
2647    ffrt_error_nomem = ENOMEM,
2648    ffrt_error_timedout = ETIMEDOUT,
2649    ffrt_error_busy = EBUSY,
2650    ffrt_error_inval = EINVAL
2651} ffrt_error_t;
2652
2653struct ffrt_rwlock_t;
2654
2655int ffrt_rwlock_init(ffrt_rwlock_t* rwlock, const ffrt_rwlockattr_t* attr);
2656int ffrt_rwlock_wrlock(ffrt_rwlock_t* rwlock);
2657int ffrt_rwlock_trywrlock(ffrt_rwlock_t* rwlock);
2658int ffrt_rwlock_rdlock(ffrt_rwlock_t* rwlock);
2659int ffrt_rwlock_tryrdlock(ffrt_rwlock_t* rwlock);
2660int ffrt_rwlock_unlock(ffrt_rwlock_t* rwlock);
2661int ffrt_rwlock_destroy(ffrt_rwlock_t* rwlock);
2662```
2663
2664#### 参数
2665
2666`attr`
2667
2668* 当前FFRT只支持基础类型的rwlock,因此attr必须为空指针
2669
2670`rwlock`
2671
2672* 指向所操作的读写锁的指针
2673
2674#### 返回值
2675
2676* 若成功则为 ffrt_success ,否则发生错误
2677
2678#### 描述
2679* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2680* 该功能能够避免pthread传统的pthread_rwlock_t 在抢不到锁时陷入内核的问题,在使用得当的条件下将会有更好的性能
2681* **注意:目前暂不支持递归和定时功能**
2682* **注意:C API中的ffrt_rwlock_t需要用户调用`ffrt_rwlock_init`和`ffrt_rwlock_destroy`显式创建和销毁,而C++ API无需该操作**
2683* **注意:C API中的ffrt_rwlock_t对象的置空和销毁由用户完成,对同一个ffrt_rwlock_t仅能调用一次`ffrt_rwlock_destroy`,重复对同一个ffrt_rwlock_t调用`ffrt_rwlock_destroy`,其行为是未定义的**
2684* **注意:在`ffrt_rwlock_destroy`之后再对ffrt_rwlock_t进行访问,其行为是未定义的**
2685
2686#### 样例
2687
2688```{.c}
2689#include <stdio.h>
2690#include "ffrt_inner.h"
2691
2692typedef struct {
2693    int* sum;
2694    ffrt_rwlock_t* mtx;
2695} tuple;
2696
2697void func1(void* arg)
2698{
2699    tuple* t = (tuple*)arg;
2700
2701    int ret = ffrt_rwlock_wrlock(t->mtx);
2702    if (ret != ffrt_success) {
2703        printf("error\n");
2704    }
2705    (*t->sum)++;
2706    ret = ffrt_rwlock_unlock(t->mtx);
2707    if (ret != ffrt_success) {
2708        printf("error\n");
2709    }
2710}
2711
2712void func2(void* arg)
2713{
2714    tuple* t = (tuple*)arg;
2715
2716    int ret = ffrt_rwlock_rdlock(t->mtx);
2717    if (ret != ffrt_success) {
2718        printf("error\n");
2719    }
2720    printf("sum is %d\n", *t->sum);
2721    ret = ffrt_rwlock_unlock(t->mtx);
2722    if (ret != ffrt_success) {
2723        printf("error\n");
2724    }
2725}
2726
2727typedef struct {
2728    ffrt_function_header_t header;
2729    ffrt_function_t func;
2730    ffrt_function_t after_func;
2731    void* arg;
2732} c_function;
2733
2734static void ffrt_exec_function_wrapper(void* t)
2735{
2736    c_function* f = (c_function*)t;
2737    if (f->func) {
2738        f->func(f->arg);
2739    }
2740}
2741
2742static void ffrt_destroy_function_wrapper(void* t)
2743{
2744    c_function* f = (c_function*)t;
2745    if (f->after_func) {
2746        f->after_func(f->arg);
2747    }
2748}
2749
2750#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
2751static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
2752    const ffrt_function_t after_func, void* arg)
2753{
2754    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
2755        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
2756    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
2757    f->header.exec = ffrt_exec_function_wrapper;
2758    f->header.destroy = ffrt_destroy_function_wrapper;
2759    f->func = func;
2760    f->after_func = after_func;
2761    f->arg = arg;
2762    return (ffrt_function_header_t*)f;
2763}
2764
2765static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
2766    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
2767{
2768    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
2769}
2770
2771void ffrt_rwlock_task(void* arg)
2772{
2773    int sum = 0;
2774    ffrt_rwlock_t mtx;
2775    tuple t = {&sum, &mtx};
2776    int ret = ffrt_rwlock_init(&mtx, NULL);
2777    if (ret != ffrt_success) {
2778        printf("error\n");
2779    }
2780    for (int i = 0; i < 10; i++) {
2781        ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL);
2782        for (int j = 0; j < 5; j++) {
2783            ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL);
2784        }
2785    }
2786    ffrt_rwlock_destroy(&mtx);
2787    ffrt_wait();
2788    printf("sum = %d", sum);
2789}
2790
2791int main(int narg, char** argv)
2792{
2793    int r;
2794    ffrt_submit_c(ffrt_rwlock_task, NULL, NULL, NULL, NULL, NULL);
2795    ffrt_wait();
2796    return 0;
2797}
2798```
2799
2800预期输出为
2801
2802```
2803sum=10
2804```
2805
2806* 该例子为功能示例,实际中并不鼓励这样使用
2807
2808
2809### ffrt_cond_t
2810<hr/>
2811
2812* FFRT提供的类似pthread 信号量的性能实现
2813
2814#### 声明
2815
2816```{.c}
2817typedef enum {
2818    ffrt_error = -1,
2819    ffrt_success = 0,
2820    ffrt_error_nomem = ENOMEM,
2821    ffrt_error_timedout = ETIMEDOUT,
2822    ffrt_error_busy = EBUSY,
2823    ffrt_error_inval = EINVAL
2824} ffrt_error_t;
2825
2826struct ffrt_cond_t;
2827typedef enum {
2828    ffrt_clock_realtime = CLOCK_REALTIME,
2829    ffrt_clock_monotonic = CLOCK_MONOTONIC
2830} ffrt_clockid_t;
2831
2832int ffrt_condattr_init(ffrt_condattr_t* attr);
2833int ffrt_condattr_destroy(ffrt_condattr_t* attr);
2834int ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock);
2835int ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock);
2836
2837int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr);
2838int ffrt_cond_signal(ffrt_cond_t* cond);
2839int ffrt_cond_broadcast(ffrt_cond_t* cond);
2840int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex);
2841int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point);
2842int ffrt_cond_destroy(ffrt_cond_t* cond);
2843```
2844
2845#### 参数
2846
2847`cond`
2848
2849* 指向所操作的信号量的指针
2850
2851`attr`
2852
2853* 属性设定,空指针表示使用默认属性
2854
2855`mutex`
2856
2857* 指向要在阻塞期间解锁的互斥锁的指针
2858
2859`time_point`
2860
2861* 指向指定等待时限时间的对象的指针
2862
2863
2864#### 返回值
2865
2866* 若成功则为 ffrt_success,若在锁定互斥前抵达时限则为 ffrt_error_timedout
2867
2868#### 描述
2869* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
2870* 该功能能够避免传统的pthread_cond_t在条件不满足时陷入内核的问题,在使用得当的条件下将会有更好的性能
2871* **注意:C API中的ffrt_cond_t需要用户调用`ffrt_cond_init`和`ffrt_cond_destroy`显式创建和销毁,而C++ API中依赖构造和析构自动完成**
2872* **注意:C API中的ffrt_cond_t对象的置空和销毁由用户完成,对同一个ffrt_cond_t仅能调用一次`ffrt_cond_destroy`,重复对同一个ffrt_cond_t调用`ffrt_cond_destroy`,其行为是未定义的**
2873* **注意:在`ffrt_cond_destroy`之后再对ffrt_cond_t进行访问,其行为是未定义的**
2874
2875#### 样例
2876
2877```{.c}
2878#include <stdio.h>
2879#include "ffrt.h"
2880
2881typedef struct {
2882    ffrt_cond_t* cond;
2883    int* a;
2884    ffrt_mutex_t* lock_;
2885} tuple;
2886
2887void func1(void* arg)
2888{
2889    tuple* t = (tuple*)arg;
2890    int ret = ffrt_mutex_lock(t->lock_);
2891    if (ret != ffrt_success) {
2892        printf("error\n");
2893    }
2894    while (*t->a != 1) {
2895        ret = ffrt_cond_wait(t->cond, t->lock_);
2896        if (ret != ffrt_success) {
2897            printf("error\n");
2898        }
2899    }
2900    ret = ffrt_mutex_unlock(t->lock_);
2901    if (ret != ffrt_success) {
2902        printf("error\n");
2903    }
2904    printf("a = %d", *(t->a));
2905}
2906
2907void func2(void* arg)
2908{
2909    tuple* t = (tuple*)arg;
2910    int ret = ffrt_mutex_lock(t->lock_);
2911    if (ret != ffrt_success) {
2912        printf("error\n");
2913    }
2914    *(t->a) = 1;
2915    ret = ffrt_cond_signal(t->cond);
2916    if (ret != ffrt_success) {
2917        printf("error\n");
2918    }
2919    ret = ffrt_mutex_unlock(t->lock_);
2920    if (ret != ffrt_success) {
2921        printf("error\n");
2922    }
2923}
2924
2925typedef struct {
2926    ffrt_function_header_t header;
2927    ffrt_function_t func;
2928    ffrt_function_t after_func;
2929    void* arg;
2930} c_function;
2931
2932static void ffrt_exec_function_wrapper(void* t)
2933{
2934    c_function* f = (c_function*)t;
2935    if (f->func) {
2936        f->func(f->arg);
2937    }
2938}
2939
2940static void ffrt_destroy_function_wrapper(void* t)
2941{
2942    c_function* f = (c_function*)t;
2943    if (f->after_func) {
2944        f->after_func(f->arg);
2945    }
2946}
2947
2948#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
2949static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
2950    const ffrt_function_t after_func, void* arg)
2951{
2952    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
2953        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
2954    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
2955    f->header.exec = ffrt_exec_function_wrapper;
2956    f->header.destroy = ffrt_destroy_function_wrapper;
2957    f->func = func;
2958    f->after_func = after_func;
2959    f->arg = arg;
2960    return (ffrt_function_header_t*)f;
2961}
2962
2963static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
2964    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
2965{
2966    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
2967}
2968
2969void ffrt_cv_task()
2970{
2971    ffrt_cond_t cond;
2972    int ret = ffrt_cond_init(&cond, NULL);
2973    if (ret != ffrt_success) {
2974        printf("error\n");
2975    }
2976    int a = 0;
2977    ffrt_mutex_t lock_;
2978    tuple t = {&cond, &a, &lock_};
2979    ret = ffrt_mutex_init(&lock_, NULL);
2980    if (ret != ffrt_success) {
2981        printf("error\n");
2982    }
2983    ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL);
2984    ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL);
2985    ffrt_wait();
2986    ffrt_cond_destroy(&cond);
2987    ffrt_mutex_destroy(&lock_);
2988}
2989
2990int main(int narg, char** argv)
2991{
2992    ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL);
2993    ffrt_wait();
2994    return 0;
2995}
2996```
2997
2998预期输出为:
2999
3000```
3001a=1
3002```
3003
3004* 该例子为功能示例,实际中并不鼓励这样使用
3005
3006## 杂项
3007
3008### ffrt_usleep
3009
3010<hr/>
3011
3012* FFRT提供的类似C11 sleep和linux usleep的性能实现
3013
3014#### 声明
3015
3016```{.c}
3017int ffrt_usleep(uint64_t usec);
3018```
3019
3020#### 参数
3021
3022`usec`
3023
3024* 睡眠的us数
3025
3026#### 返回值
3027
3028* 不涉及
3029
3030#### 描述
3031* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
3032* 该功能能够避免传统的sleep 睡眠时陷入内核的问题,在使用得当的条件下将会有更好的性能
3033
3034#### 样例
3035
3036```{.c}
3037#include <time.h>
3038#include <stdio.h>
3039#include "ffrt.h"
3040
3041void func(void* arg)
3042{
3043    printf("Time: %s", ctime(&(time_t){time(NULL)}));
3044    ffrt_usleep(2000000); // 睡眠 2 秒
3045    printf("Time: %s", ctime(&(time_t){time(NULL)}));
3046}
3047
3048typedef struct {
3049    ffrt_function_header_t header;
3050    ffrt_function_t func;
3051    ffrt_function_t after_func;
3052    void* arg;
3053} c_function;
3054
3055static void ffrt_exec_function_wrapper(void* t)
3056{
3057    c_function* f = (c_function*)t;
3058    if (f->func) {
3059        f->func(f->arg);
3060    }
3061}
3062
3063static void ffrt_destroy_function_wrapper(void* t)
3064{
3065    c_function* f = (c_function*)t;
3066    if (f->after_func) {
3067        f->after_func(f->arg);
3068    }
3069}
3070
3071#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
3072static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
3073    const ffrt_function_t after_func, void* arg)
3074{
3075    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
3076        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
3077    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
3078    f->header.exec = ffrt_exec_function_wrapper;
3079    f->header.destroy = ffrt_destroy_function_wrapper;
3080    f->func = func;
3081    f->after_func = after_func;
3082    f->arg = arg;
3083    return (ffrt_function_header_t*)f;
3084}
3085
3086static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
3087    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
3088{
3089    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
3090}
3091
3092int main(int narg, char** argv)
3093{
3094    ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL);
3095    ffrt_wait();
3096    return 0;
3097}
3098```
3099
3100### ffrt_yield
3101<hr/>
3102
3103* 当前task 主动让出CPU 执行资源,让其他可以被执行的task 先执行,如果没有其他可被执行的task,yield 无效
3104
3105#### 声明
3106
3107```{.cpp}
3108void ffrt_yield();
3109```
3110
3111#### 参数
3112
3113* 不涉及
3114
3115#### 返回值
3116
3117* 不涉及
3118
3119#### 描述
3120* 该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未定义的行为
3121* 此函数的确切行为取决于实现,特别是使用中的FFRT 调度程序的机制和系统状态
3122
3123#### 样例
3124
3125* 省略
3126
3127## 维测
3128
3129### 长耗时任务监测
3130
3131#### 描述
3132* 长耗时任务打印机制
3133  当任务执行时间超过一秒时,会触发一次堆栈打印,后续该任务堆栈打印频率调整为一分钟。连续打印十次后,打印频率调整为十分钟。再触发十次打印后,打印频率固定为三十分钟。
3134* 该机制的堆栈打印调用的是DFX的 `GetBacktraceStringByTid` 接口,该接口会向阻塞线程发送抓栈信号,触发中断并抓取调用栈返回。
3135
3136#### 样例
3137在对应进程日志中搜索 `RecordSymbolAndBacktrace` 关键字,对应的日志示例如下:
3138
3139```
3140W C01719/ffrt: 60500:RecordSymbolAndBacktrace:159 Tid[16579] function occupies worker for more than [1]s.
3141W C01719/ffrt: 60501:RecordSymbolAndBacktrace:164 Backtrace:
3142W C01719/ffrt: #00 pc 00000000000075f0 /system/lib64/module/file/libhash.z.so
3143W C01719/ffrt: #01 pc 0000000000008758 /system/lib64/module/file/libhash.z.so
3144W C01719/ffrt: #02 pc 0000000000012b98 /system/lib64/module/file/libhash.z.so
3145W C01719/ffrt: #03 pc 000000000002aaa0 /system/lib64/platformsdk/libfilemgmt_libn.z.so
3146W C01719/ffrt: #04 pc 0000000000054b2c /system/lib64/platformsdk/libace_napi.z.so
3147W C01719/ffrt: #05 pc 00000000000133a8 /system/lib64/platformsdk/libuv.so
3148W C01719/ffrt: #06 pc 00000000000461a0 /system/lib64/chipset-sdk/libffrt.so
3149W C01719/ffrt: #07 pc 0000000000046d44 /system/lib64/chipset-sdk/libffrt.so
3150W C01719/ffrt: #08 pc 0000000000046a6c /system/lib64/chipset-sdk/libffrt.so
3151W C01719/ffrt: #09 pc 00000000000467b0 /system/lib64/chipset-sdk/libffrt.so
3152```
3153该维测会打印出worker上执行时间超过阈值的任务堆栈、worker线程号、执行时间,请自行根据堆栈找对应组件确认阻塞原因。
3154
3155
3156#### 注意事项
3157如果代码中存在 `sleep` 等会被中断唤醒的阻塞,用户需主动接收该阻塞的返回值,并重新调用。
3158示例如下:
3159```
3160unsigned int leftTime = sleep(10);
3161while (leftTime != 0) {
3162    leftTime = sleep(leftTime);
3163}
3164```
3165
3166# 部署
3167
3168## 部署方式
3169<img src="images/image-20230120153923679.png" alt="image-20230120153923679" style="zoom:67%;" />
3170
3171* FFRT的部署依赖FFRT动态库libffrt.so和一组header头文件
3172
3173* FFRT的头文件为`ffrt.h`,内部包含了C++ API,C API和C base API
3174  * ffrt.h 定义为:
3175  ```{.cpp}
3176  #ifndef FFRT_API_FFRT_H
3177  #define FFRT_API_FFRT_H
3178  #ifdef __cplusplus
3179  #include "cpp/task.h"
3180  #include "cpp/deadline.h"
3181  #include "cpp/sys_event.h"
3182  #include "cpp/mutex.h"
3183  #include "cpp/condition_variable.h"
3184  #include "cpp/sleep.h"
3185  #include "cpp/thread.h"
3186  #include "cpp/config.h"
3187  #include "cpp/future.h"
3188  #else
3189  #include "c/task.h"
3190  #include "c/deadline.h"
3191  #include "c/sys_event.h"
3192  #include "c/mutex.h"
3193  #include "c/condition_variable.h"
3194  #include "c/sleep.h"
3195  #include "c/thread.h"
3196  #include "c/config.h"
3197  #endif
3198  #endif
3199  ```
3200  * C base API定义示例:
3201  ```{.cpp}
3202  void ffrt_submit_base(ffrt_function_header_t* func, ...);
3203  int ffrt_mutex_init(...);
3204  ```
3205  * C API定义示例:
3206  ```{.cpp}
3207  static inline void ffrt_submit(ffrt_function_t func, void* arg, ...)
3208  {
3209      ffrt_submit_base(ffrt_create_function_wrapper(func, arg), ...);
3210  }
3211  ```
3212  * C++ API定义示例:
3213  ```{.cpp}
3214  namespace ffrt {
3215  static inline void submit(std::function& func, ...)
3216  {
3217      ffrt_submit_base(ffrt_create_function_wrapper(func), ...);
3218  }
3219  struct mutex {
3220      mutex() {
3221          ffrt_mutex_init(...);
3222          ...
3223      };
3224  }
3225  ```
3226
3227* **出于易用性方面的考虑,除非必要,强烈建议你使用C++ API,调用C API将会使你的代码非常臃肿或者更容易产生资源未释放问题**
3228
3229| 需求列表                                                     |
3230| ------------------------------------------------------------ |
3231| 需求1:ABI兼容性,在NDK场景中由于用户的编译环境与FFRT的编译环境不同,使用C++接口可能存在ABI兼容性问题,要有解决方案 |
3232| 需求2:用户的编译环境为纯C编译环境,不想因为引入FFRT而引入C++元素的场景,要有解决方案 |
3233| 需求3:易用性,尽可能让接口简单易用,用户少出错              |
3234
3235* 对于需求1,通过在用户调用的C++接口和FFRT的实现之间增加一个C base API层,并基于头文件方式将API中的C++的元素编译到用户的so,从而解决ABI兼容的问题
3236* 对于需求2,可以通过C Base API解决
3237* 对于需求3,建议用户尽可能使用C++ API,以避免C API固有的资源未初始化/释放、参数冗长等问题,对于不得不使用C API的场景,FFRT仍然支持用户使用C API和C base API
3238
3239
3240
3241<br/>
3242<br/>
3243
3244<hr/>
3245# 实战指南
3246
3247## 步骤1: 分析应用
3248
3249使用 FFRT 并行编程的第一步便是您需要了解你的应用。
3250
3251【建议1】:使用 Task 梳理应用的流程。
3252
3253使用 Task 梳理应用的流程,并且尽可能使用数据来表达 Task 之间的依赖。当然如果两个 Task 之间如无数据依赖,仅存在控制依赖,您也可以创建一个虚拟的(或者逻辑上的)数据依赖。
3254
3255<img src="images/image-20220926152831526.png" style="zoom:70%" />
3256
3257<center>AIRAW 的数据流图</center>
3258
3259基于数据流图,可以很容易判定出哪些 Task 是可以并发的,比如,Slice0 的 NPU Task 和 Slice1 的 GPU Pre Task 是可以并发的,因为它们没有任何依赖。
3260
3261反过来,如果并发的效果不理想,也可以通过调整数据流图来优化并发。例如,假如上图中GPU Pre Task 执行时间有很大波动,但平均耗时略小于 NPU Task,会出现某些时刻 GPU Pre Task 拖慢整个执行时间。此时,如果将 GPU Pre Task 的输出 Buffer 改成3个(或者更多)的 Buffer ,可以增加 GPU Pre Task 和 NPU Task 的并发机会,将降低波动对总执行时间的影响。
3262
3263
3264
3265【建议2】:这里不用太担心 Task 大或小的问题,因为 FFRT 允许你在 Task 内部继续拆分 SubTask,可以逐步细化。
3266
3267下图中,第一次画数据流图时,可以不将 FaceDirection 和 UpdateExistFaceImageInfo 两个 Task 展开,可以逐步细化。
3268
3269<img src="images/image-20220926153003884.png" style="zoom:70%" />
3270
3271<center>某拍照业务的数据流图</center>
3272
3273【建议3】:上述流程图或者数据流图不要求是静态图(即 Task 数量和 Task 依赖关系是固定的)
3274
3275FFRT 允许动态提交 Task ,在编程界面上不体现图的概念,FFRT 内部会根据Task 之间的依赖关系动态调整数据流图的节点。
3276
3277
3278【建议4】:尽可能对应用做热点分析
3279
3280如果是对存量代码的 FFRT 化改造,那么,使用 System Trace 这类工具能帮助您聚焦在性能热点上,比如下图可以很容易知道当前的性能Bound,在分析数据流图时,可以重点关注这些热点任务。
3281
3282<img src="images/image-20220926153030993.png" style="zoom:70%" />
3283
3284<center>某业务的System Trace</center>
3285
3286## 步骤2: 并行化应用
3287
3288【建议1】:不要直接使用线程,使用 FFRT 提交Task。
3289
3290如果应用中有明显的数据依赖关系,那么 FFRT 将会非常适合;最差的情况是应用没有数据依赖或难以并行(如果真的存在),您仍然可以把 FFRT 当做一个高效的进程级线程池、或者协程库去使用它,但非常不建议你继续创建线程。
3291
3292
3293
3294【建议2】:Task 最好被建模为纯函数。
3295
3296纯函数是指其执行没有副作用,例如更新全局数据结构。每个任务都依赖于其输入/输出签名来连接到其他任务。
3297
3298请注意,即使 Task 不是"纯"的,FFRT 仍然适用。只要任务使用的数据依赖或者锁足以保证正确执行,FFRT 就能正常工作。
3299
3300
3301
3302【建议3】:尽可能尝试通过 inDeps/outDeps 表达依赖,而不是使用 ffrt::wait()。
3303
3304这是因为 FFRT 跟踪和处理 inDeps/outDeps 比调用显式 ffrt::wait() 函数更自然、更便宜。
3305
3306
3307
3308【建议4】:注意 Task 粒度
3309
3310以适当的粒度提交任务至关重要:目前每个任务的调度开销约为 10 us。如果 Task 的粒度非常小,那么开销的百分比将会很高。FFRT 会继续基于软硬件的方式优化调度开销。
3311
3312
3313
3314【建议5】:尽可能使用 FFRT 原语
3315
3316如果需要mutex、sleep、异步 I/O,请使用 FFRT 原语,而不是使用OS 提供的版本。因为这些 FFRT 提供的实现在与 FFRT 配合时开销将会更小。
3317
3318
3319
3320【建议6】:在需要时,使用 ffrt::wait() 确保栈变量的生命周期。
3321
3322如果子任务使用驻留在父任务栈上的数据,则父任务应避免在子任务执行完成前返回。在父任务的末尾添加 ffrt::wait() 可以解决这个问题。
3323
3324
3325
3326## 步骤3: 优化应用
3327
3328【建议1】:基于System Trace,分析并行是否符合预期
3329
3330FFRT 已经内置 SysTrace 支持,默认以txx.xx表示,非常有利于分析 Task 粒度和并发度。未来,在性能分析和维测方面将继续增强。
3331
3332<img src="images/image-20220926153209875.png" style="zoom:70%" />
3333
3334【建议2】:对于耗时的 Task,尝试提交 SubTask,提升应用的并行度
3335
3336
3337【建议3】:在合适的场景,使用 Deadline 调度,实现能效和性能的平衡
3338
3339方案正在验证中,待更新。
3340
3341
3342
3343## 样例: CameraHal QuickThumb
3344
3345### 步骤1: 分析应用
3346
3347<img src="images/image-20220926153255824.png" style="zoom:70%" />
3348
33491)   QuickThumb 是 CameraHal 中实现的对一张图片进行缩小的功能,整体运行时间约30 us;
3350
33512)   在实现上分为两层循环,外层的一次循环输出1行,内层的1次循环输出该行的m列;
3352
33533)   在划分 Task 时,一种简单的做法是1行的处理就是1个Task。
3354
3355### 步骤2: 并行化应用
3356<img src="images/image-20220926153509205.png" style="zoom:100%" />
3357
3358 1)   根据纯函数的定义,该 Task 的输入输出的数据是非常之多的,因此,这个场景下使用更宽泛的纯函数的定义,只需要考虑在 Task 内部会被写,但是却被定义在 Task 外部的变量即可;
3359
33602)   按照上面的原则,将 py/puv 的定义移到 Task 内部可避免多个 Task 同时写 py/puv 的问题;
3361
33623)   s32r 的处理可以有两种方式,都能得到正确的功能:a. 保持定义在Task 外部,但作为Task 的输出依赖;b. 将s32r定义在Task 内部,作为Task的私有变量。显然,b 方案能够获得更好的性能
3363
3364
3365
3366### 步骤3: 优化应用
3367
3368通过System Trace,会发现上述改造方案的Task 粒度较小,大约单个Task 耗时在5us左右,因此,扩大Task的粒度为32行处理,得到最终的并行结果,下图为使用4小核和3中核的结果。
3369
3370<img src="images/image-20220926153603572.png" style="zoom:100%" />
3371
3372
3373
3374## 样例: Camera AIRAW
3375
3376### 步骤1: 分析应用
3377<img src="images/image-20220926153611121.png" style="zoom:70%" />
3378
3379AIRAW 的处理包括了3个处理步骤,在数据面上,可以按slice 进行切分,在不考虑pre_outbuf和npu_outbuf 在slice间复用的情况下,数据流图如上图所示。
3380
3381<img src="images/image-20220926152831526.png" style="zoom:70%" />
3382
3383为了节省运行过程中的内存占用,但不影响整体性能,可以只保留2个pre_outbuf和2个npu_outbuf。
3384
3385`为此付出的代价是:Buffer 的复用产生了slice3 的GPU Pre Task 依赖slice1 的NPU Task 完成,俗称反压,又称生产者依赖关系。但是,如果您使用 FFRT 来实现,将会是非常自然而高效的`
3386
3387### 步骤2: 并行化应用
3388
3389```{.cpp}
3390constexpr uint32_t SLICE_NUM = 24;
3391constexpr uint32_t BUFFER_NUM = 2;
3392
3393int input[SLICE_NUM]; // input is split into SLICE_NUM slices
3394int pre_outbuf[BUFFER_NUM]; // gpu pre task output buffers
3395int npu_outbuf[BUFFER_NUM]; // npu output buffers
3396int output[SLICE_NUM]; // output is split into SLICE_NUM slices
3397
3398for (uint32_t i = 0; i < SLICE_NUM; i++) {
3399  uint32_t buf_id = i % BUFFER_NUM;
3400  ffrt::submit(gpuPreTask, {input + i}, {pre_outbuf + buf_id});
3401  ffrt::submit(npuTask, {pre_outbuf + buf_id}, {npu_outbuf + buf_id});
3402  ffrt::submit(gpuPostTask, {npu_outbuf + buf_id}, {output + i});
3403}
3404
3405ffrt::wait();
3406```
3407
3408### 步骤3: 优化应用
3409
3410<img src="images/image-20220926153825527.png" style="zoom:100%" />
3411
3412基于以上实现,从上面的Trace中我们看到,NPU 的硬件时间被完全用满,系统端到端性能达到最优,而付出的开发代价将会比GCD 或多线程小的多。
3413
3414
3415
3416## 样例: Camera FaceStory
3417
3418### 步骤1: 分析应用
3419
3420<img src="images/image-20220926153003884.png" style="zoom:70%" />
3421
3422
3423
3424### 步骤2: 并行化应用
3425
3426<img src="images/image-20220926153906692.png" style="zoom:100%" />
3427
3428代码改造样例
3429
34301)     该场景输出存量代码迁移,只需将原先串行的代码以Task的方式提交即可;
3431
34322)     过程中需要考虑Data Race和数据生命周期;
3433
34343)     先提交大的Task,根据需要逐步拆分SubTask。
3435
3436
3437
3438### 步骤3: 优化应用
3439
3440<img src="images/image-20220926153030993.png" style="zoom:100%" />
3441
3442<center>原始System Trace</center>
3443
3444<img src="images/image-20220926153925963.png" style="zoom:100%" />
3445
3446
3447
3448
3449<center>改造后System Trace</center>
3450
3451并行化的收益来自于:
3452
34531)    多分支或循环并发,实现CPU前后处理和NPU的并发
3454
34552)    子任务拆分,进一步提升并行度
3456
34573)    基于数据流图优化CPU L2 Cache Flush频次
3458
34594)    NPU Worker Thread实时优先级调整,后续FFRT中考虑独立出XPU调度Worker来保证实时性
3460
34615)    在未来,模型加载使用FFRT submit,模型加载内部也可以使用submit来继续拆分,能够优化整个业务的启动耗时。
3462
3463<br/>
3464<br/>
3465<hr/>
3466
3467
3468
3469# 使用建议
3470
3471## 建议1: 函数化
3472
3473**基本思想:计算过程函数化**
3474
3475* 程序过程各步骤以函数封装表达,函数满足类纯函数特性
3476* 无全局数据访问
3477* 无内部状态保留
3478* 通过ffrt::submit()接口以异步任务方式提交函数执行
3479* 将函数访问的数据对象以及访问方式在ffrt::submit()接口中的in_deps/out_deps参数表达
3480* 程序员通过inDeps/outDeps参数表达任务间依赖关系以保证程序执行的正确性
3481
3482> 做到纯函数的好处在于:1. 能够最大化挖掘并行度,2.避免DataRace和锁的问题
3483
3484
3485
3486**在实际中,可以根据场景放松纯函数的约束,但前提是:**
3487
3488* 确定添加的in_deps/out_deps可确保程序正确执行
3489* 通过FFRT提供的锁机制保护对全局变量的访问
3490
3491
3492
3493## 建议2: 注意任务粒度
3494
3495* FFRT管理和调度异步任务执行有调度开销,任务粒度(执行时间)需匹配调度开销
3496* 大量小粒度任务造成FFRT调度开销占比增加,性能下降,解决方法:
3497  * 将多个小粒度任务聚合为大粒度任务一次发送给FFRT异步执行
3498  * 同步方式执行小粒度任务,不发送给FFRT异步执行。需注意和异步任务之间的数据同步问题,在需要同步的地方插入ffrt::wait()
3499  * 下面的例子中,fib_ffrt2会比fib_ffrt1拥有更好的性能
3500
3501  ```{.cpp}
3502  #include "ffrt.h"
3503  void fib_ffrt1(int x, int& y)
3504  {
3505      if (x <= 1) {
3506          y = x;
3507      } else {
3508          int y1, y2;
3509          ffrt::submit([&] {fib_ffrt1(x - 1, y1);}, {&x}, {&y1} );
3510          ffrt::submit([&] {fib_ffrt1(x - 2, y2);}, {&x}, {&y2} );
3511          ffrt::submit([&] {y = y1 + y2;}, {&y1, &y2}, {} );
3512          ffrt::wait();
3513      }
3514  }
3515
3516  void fib_ffrt2(int x, int& y)
3517  {
3518      if (x <= 1) {
3519          y = x;
3520      } else {
3521          int y1, y2;
3522          ffrt::submit([&] {fib_ffrt2(x - 1, y1);}, {&x}, {&y1} );
3523          ffrt::submit([&] {fib_ffrt2(x - 2, y2);}, {&x}, {&y2} );
3524          ffrt::wait({&y1, &y2});
3525          y = y1 + y2;
3526      }
3527  }
3528  ```
3529
3530
3531
3532## 建议3: 数据生命周期
3533
3534* FFRT的任务提交和执行是异步的,因此需要确保任务执行时,对任务中涉及的数据的访问是有效的
3535* 常见问题:子任务使用父任务栈数据,当父任务先于子任务执行完成时释放栈数据,子任务产生数据访问错误
3536* 解决方法1:父任务中增加ffrt::wait()等待子任务完成
3537
3538```{.cpp}
3539#include "ffrt.h"
3540void fib_ffrt(int x, int& y)
3541{
3542    if (x <= 1) {
3543        y = x;
3544    } else {
3545        int y1, y2;
3546        ffrt::submit([&] {fib_ffrt(x - 1, y1);}, {&x}, {&y1} );
3547        ffrt::submit([&] {fib_ffrt(x - 2, y2);}, {&x}, {&y2} );
3548        ffrt::submit([&] {y = y1 + y2;}, {&y1, &y2}, {} );
3549        ffrt::wait(); // 用于保证y1 y2的生命周期
3550    }
3551}
3552```
3553
3554* 解决方法2:将数据由栈移到堆,手动管理生命周期
3555
3556```{.cpp}
3557#include "ffrt.h"
3558void fib_ffrt(int x, int* y)
3559{
3560    if (x <= 1) {
3561        *y = x;
3562    } else {
3563        int *y1 = (int*)malloc(sizeof(int));
3564        int *y2 = (int*)malloc(sizeof(int));
3565
3566        ffrt::submit([=] {fib_ffrt(x - 1, y1);}, {}, {y1} );
3567        ffrt::submit([=] {fib_ffrt(x - 2, y2);}, {}, {y2} );
3568        ffrt::submit([=] {*y = *y1 + *y2; }, {y1, y2}, {} );
3569		ffrt::wait();
3570    }
3571}
3572```
3573
3574
3575
3576## 建议4: 使用FFRT提供的替代API
3577
3578* 禁止在FFRT任务中使用系统线程库API创建线程,使用submit提交任务
3579* 使用FFRT提供的锁,条件变量,睡眠,IO等API代替系统线程库API
3580  * 使用系统线程库API可能造成工作线程阻塞,引起额外性能开销
3581
3582
3583
3584## 建议5: Deadline机制
3585
3586* **必须用于具备周期/重复执行特征的处理流程**
3587* 在有明确时间约束和性能关键的处理流程中使用,避免滥用
3588* 在相对大颗粒度的处理流程中使用,例如具有16.6ms时间约束的帧处理流程
3589
3590
3591
3592## 建议6: 从线程模型迁移
3593
3594* 创建线程替代为创建FFRT任务
3595  * 线程从逻辑上类似无in_deps的任务
3596* 识别线程间的依赖关系,并将其表达在任务的依赖关系in_deps/out_deps3597* 线程内计算过程分解为异步任务调用
3598* 通过任务依赖关系和锁机制避免并发任务数据竞争问题
3599
3600
3601
3602# 已知限制
3603
3604## thread local使用约束
3605* FFRT Task中使用thread local存在风险,说明如下:
3606* thread local变量包括C/C++语言提供的thread_local定义的变量,使用pthread_key_create创建的变量
3607* FFRT支持Task调度,Task调度到哪个线程是随机的,使用thread local是有风险的,这一点和所有支持Task并发调度的框架一样
3608* FFRT的Task默认以协程的方式运行,Task执行过程中可能发生协程退出,恢复执行时,执行该任务的线程可能发生变更
3609
3610## thread绑定类使用约束
3611* FFRT支持Task调度,Task调度到哪个线程是随机的,thread_idx/线程优先级/线程亲和性等与thread绑定的行为禁止在task中使用
3612
3613## recursive mutex使用约束
3614* FFRT Task中使用标准库的recursive mutex可能发生死锁,需要更换为FFRT提供的recursive mutex,说明如下:
3615* recursive mutex在lock()成功时记录调用者"执行栈"作为锁的owner,在后续lock()时会判断调用者是否为当前执行栈,如果是则返回成功,以支持在同一个执行栈中嵌套获取锁。在标准库的实现中,"执行栈"以线程标识表示。
3616* 在FFRT Task中使用标准库的recursive mutex,如果在外层和内层lock()之间,发生Task(协程)退出,Task恢复执行时在不同于首次调用lock()的FFRT Worker上,则判断当前线程不是owner,lock()失败,FFRT Worker挂起,后面的unlock()不会被执行,从而出现死锁。
3617
3618## 不支持用户在fork出的子进程内使用ffrt
3619
3620## 以动态库方式部署FFRT
3621
3622* 只能以动态库方式部署FFRT,静态库部署可能有多实例问题,例如:当多个被同一进程加载的so都以静态库的方式使用FFRT时,FFRT会被实例化成多份,其行为是未知的,这也不是FFRT设计的初衷
3623
3624## C API中初始化ffrt对象后,对象的置空与销毁由用户负责
3625
3626* 为保证较高的性能,ffrt的C API中内部不包含对对象的销毁状态的标记,用户需要合理地进行资源的释放,重复调用各个对象的destroy操作,其结果是未定义的
3627* 错误示例1,重复调用destroy可能造成不可预知的数据损坏
3628
3629```{.cpp}
3630#include "ffrt.h"
3631void abnormal_case_1()
3632{
3633    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
3634    ...
3635    ffrt_task_handle_destroy(h);
3636    ffrt_task_handle_destroy(h); // double free
3637}
3638```
3639
3640* 错误示例2,未调用destroy会造成内存泄漏
3641
3642```{.cpp}
3643#include "ffrt.h"
3644void abnormal_case_2()
3645{
3646    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
3647    ...
3648    // memory leak
3649}
3650```
3651
3652* 建议示例,仅调用一次destroy,如有必要可进行置空
3653
3654```{.cpp}
3655#include "ffrt.h"
3656void normal_case()
3657{
3658    ffrt_task_handle_t h = ffrt_submit_h([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
3659    ...
3660    ffrt_task_handle_destroy(h);
3661    h = nullptr; // if necessary
3662}
3663```
3664
3665## 输入输出依赖数量的限制
3666
3667* 使用submit接口进行任务提交时,每个任务的输入依赖和输出依赖的数量之和不能超过8个。
3668* 使用submit_h接口进行任务提交时,每个任务的输入依赖和输出依赖的数量之和不能超过7个。
3669* 参数既作为输入依赖又作为输出依赖的时候,统计依赖数量时只统计一次,如输入依赖是{&x},输出依赖也是{&x},实际依赖的数量是1。
3670