兜兜零元 发表于 2024-11-3 18:48:06

鸿蒙HarmonyOS应用开辟之FFRT 开辟引导

场景介绍

Function Flow编程模子是一种基于任务和数据驱动的并发编程模子,允许开辟者通过任务及其依赖关系形貌的方式进行应用开辟。FFRT(Function Flow运行时)是支持Function Flow编程模子的软件运行时库,用于调度实验开辟者基于Function Flow编程模子开辟的应用。通过Function Flow编程模子和FFRT,开辟者可专注于应勤奋能开辟,由FFRT在运行时根据任务依赖状态和可用实验资源自动并发调度和实验任务。
本文用于引导开辟者基于Function Flow编程模子和FFRT实现并行编程。
两种编程模子

线程编程模子FFRT任务编程模子并行度发掘方式步伐员通过创建多线程并把任务分配到每个线程中实验来发掘运行时的并行度。步伐员(编译器工具或语言特性共同)静态编程时将应用分解成任务及其数据依赖关系,运行时调度器分配任务到工作线程实验。谁负责线程创建步伐员负责创建线程,线程编程模子无法约束线程的创建,滥用可能造成体系中大量线程。由调度器负责工作线程池的创建和管理,步伐员无法直接创建线程。负载均衡步伐员静态编程时将任务映射到线程,映射不公道或任务实验时间不确定造成线程负载不均。FFRT运行时根据线程实验状态调度停当任务到空闲线程实验,减轻了线程负载不均标题。调度开销线程调度由内核态调度器完成,调度开销大。FFRT运行时在用户态以协程方式调度实验,相比内核线程调度机制更为轻量,减小调度的开销,并可通过硬化调度卸载进一步减小调度开销。依赖表达线程创建时即处于可实验状态,实验时与其他线程同步操作,增长线程切换。FFRT运行时根据任务创建时显式表达的输入依赖和输出依赖关系判断任务可实验状态,当输入依赖不满足时,任务不被调度实验。 根本概念

Function Flow 任务编程模子

Function Flow编程模子允许开辟者通过任务及其依赖关系形貌的方式进行应用开辟,其重要特性包罗Task-Based 和 Data-Driven 。
Task-Based 特性

Task-Based 指在Function Flow编程模子中开辟者以任务方式来组织应用步伐表达,运行时以任务粒度实验调度。
任务界说为一种面向开辟者的编程线索和面向运行时的实验对象,通常包罗一组指令序列及其操作的数据上下文情况。
Function Flow编程模子中的任务包罗以下重要特性:


[*]任务之间可指定依赖关系,依赖关系通过Data-Driven方式表达。
[*]任务可支持嵌套,即任务在实验过程中可天生新的任务下发给运行时,形成父子任务关系。
[*]多任务支持互同步操作,例如期待、锁、条件变量等。
   留意
任务颗粒度影响应用实验性能,颗粒度过小增长调度开销,颗粒度过大降低并行度。Function Flow编程模子中任务的目标颗粒度最小为100us量级,开辟者应留意公道控制任务颗粒度。
Data-Driven 特性

Data-Driven指任务之间的依赖关系通过数据依赖表达。
任务实验过程中对其关联的数据对象进行读写操作。在Function Flow编程模子中,数据对象表达抽象为数据签名,每个数据签名唯一对应一个数据对象。
数据依赖抽象为任务所操作的数据对象的数据签名列表,包罗输入数据依赖in_deps和输出数据依赖out_deps。数据对象的签名出现在一个任务的in_deps中时,该任务称为数据对象的消耗者任务,消耗者任务实验不改变其输入数据对象的内容;数据对象的签名出现在任务的out_deps中时,该任务称为数据对象的生产者任务,生产者任务实验改变其输出数据对象的内容,从而天生该数据对象的一个新的版本。
一个数据对象可能存在多个版本,每个版本对应一个生产者任务和零个,一个或多个消耗者任务,根据生产者任务和消耗者任务的下发次序界说数据对象的多个版本的次序以及每个版本所对应的生产者和消耗者任务。
数据依赖清除的任务进入停当状态允许被调度实验,依赖清除状态指任务全部输入数据对象版本的生产者任务实验完成,且全部输出数据对象版本的全部消耗者任务实验完成的状态。
通过上述Data-Driven的数据依赖表达,FFRT在运行时可动态构建任务之间的基于生产者/消耗者的数据依赖关系并遵循任务数据依赖状态实验调度,包罗:


[*] Producer-Consumer 依赖
一个数据对象版本的生产者任务和该数据对象版本的消耗者任务之间形成的依赖关系,也称为Read-after-Write依赖。
[*] Consumer-Producer 依赖
一个数据对象版本的消耗者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Read依赖。
[*] Producer-Producer 依赖
一个数据对象版本的生产者任务和该数据对象的下一个版本的生产者任务之间形成的依赖关系,也称为Write-after-Write依赖。
例如,如果有这么一些任务,与数据A的关系表述为:
task1(OUT A);
task2(IN A);
task3(IN A);
task4(OUT A);
task5(OUT A);
https://i-blog.csdnimg.cn/blog_migrate/26a655bbb3fde7087ad6cac48f2c8f96.png
   为表述方便,本文中的数据流图均以圆圈表示 Task,方块表示数据。
可以得出以下结论:


[*]task1 与task2/task3 构成Producer-Consumer 依赖,即:task2/task3 需要比及task1 写完A之后才能读A。
[*]task2/task3 与task4 构成Consumer-Producer 依赖,即:task4 需要比及task2/task3 读完A之后才能写A。
[*]task4 与task5 构成Producer-Producer 依赖,即:task5 需要比及task4 写完A之后才能写A。
接口说明

接口名形貌ffrt_condattr_init (ffrt_condattr_t* attr)初始化条件变量属性。ffrt_condattr_destroy(ffrt_condattr_t* attr)烧毁条件变量属性。ffrt_condattr_setclock(ffrt_condattr_t* attr, ffrt_clockid_t clock)设置条件变量的时钟属性。ffrt_condattr_getclock(const ffrt_condattr_t* attr, ffrt_clockid_t* clock)获取条件变量的时钟属性。ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr)初始化条件变量。ffrt_cond_signal(ffrt_cond_t* cond)唤醒阻塞在条件变量上的一个任务。ffrt_cond_broadcast(ffrt_cond_t* cond)唤醒阻塞在条件变量上的全部任务。ffrt_cond_wait(ffrt_cond_t* cond, ffrt_mutex_t* mutex)条件变量期待函数,条件变量不满足时阻塞当前任务。ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point)条件变量超时期待函数,条件变量不满足时阻塞当前任务,超时期待返回。ffrt_cond_destroy(ffrt_cond_t* cond)烧毁条件变量。ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr)初始化mutex。ffrt_mutex_lock(ffrt_mutex_t* mutex)获取mutex。ffrt_mutex_unlock(ffrt_mutex_t* mutex)开释mutex。ffrt_mutex_trylock(ffrt_mutex_t* mutex)实验获取mutex。ffrt_mutex_destroy(ffrt_mutex_t* mutex)烧毁mutex。ffrt_queue_attr_init(ffrt_queue_attr_t* attr)初始化串行队列属性。ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr)烧毁串行队列属性。ffrt_queue_attr_set_qos(ffrt_queue_attr_t* attr, ffrt_qos_t qos)设置串行队列qos属性。ffrt_queue_attr_get_qos(const ffrt_queue_attr_t* attr)获取串行队列qos属性。ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr)创建队列。ffrt_queue_destroy(ffrt_queue_t queue)烧毁队列。ffrt_queue_submit(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr)提交一个任务到队列中调度实验。ffrt_queue_submit_h(ffrt_queue_t queue, ffrt_function_header_t* f, const ffrt_task_attr_t* attr)提交一个任务到队列中调度实验,并返回任务句柄。ffrt_queue_wait(ffrt_task_handle_t handle)期待队列中一个任务实验完成。ffrt_queue_cancel(ffrt_task_handle_t handle)取消队列中一个任务。ffrt_usleep(uint64_t usec)延迟usec微秒。ffrt_yield(void)当前任务自动放权,让其他任务有机会调度实验。ffrt_task_attr_init(ffrt_task_attr_t* attr)初始化任务属性。ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name)设置任务名字。ffrt_task_attr_get_name(const ffrt_task_attr_t* attr)获取任务名字。ffrt_task_attr_destroy(ffrt_task_attr_t* attr)烧毁任务属性。ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos)设置任务qos。ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr)获取任务qos。ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us)设置任务延迟时间。ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr)获取任务延迟时间。ffrt_this_task_update_qos(ffrt_qos_t qos)更新任务qos。ffrt_this_task_get_id(void)获取任务id。ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind)申请函数实验布局的内存。ffrt_submit_base(ffrt_function_header_t* f, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)提交任务调度实验。ffrt_submit_h_base(ffrt_function_header_t* f, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)提交任务调度实验并返回任务句柄。ffrt_task_handle_destroy(ffrt_task_handle_t handle)烧毁任务句柄。ffrt_skip(ffrt_task_handle_t handle)跳过指定任务。ffrt_wait_deps(const ffrt_deps_t* deps)期待依赖的任务完成,当前任务开始实验。 函数介绍

任务管理

ffrt_submit_base



[*]该接口为ffrt动态库的导出接口,基于此可以封装出C API ffrt_submit,满足二进制兼容。
声明

const int ffrt_auto_managed_function_storage_size = 64 + sizeof(ffrt_function_header_t);
typedef enum {
    ffrt_function_kind_general,
    ffrt_function_kind_queue
} ffrt_function_kind_t;

void* ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_t kind);

typedef void(*ffrt_function_t)(void*);
typedef struct {
    ffrt_function_t exec;
    ffrt_function_t destroy;
    uint64_t reserve;
} ffrt_function_header_t;

void 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);
参数

kind


[*]function子类型,用于优化内部数据布局,默认利用ffrt_function_kind_general类型。
func


[*]CPU Function的指针,该指针实验的数据布局,按照ffrt_function_header_t界说的形貌了该CPU Task怎样实验和烧毁的函数指针,FFRT通过这两个函数指针完成Task的实验和烧毁。
in_deps


[*]该参数是可选的。
[*]该参数用于形貌该任务的输入依赖,FFRT 通过数据的假造所在作为数据的Signature 来建立依赖。
out_deps


[*]该参数是可选的。
[*]该参数用于形貌该任务的输出依赖。
[*]留意:该依赖值本质上是一个数值,ffrt没办法区分该值是公道的还是不公道的,会假定输入的值是公道的进行处理;但不发起接纳NULL,1, 2 等值来建立依赖关系,发起接纳现实的内存所在,因为前者利用不妥会建立起不须要的依赖,影响并发。
attr


[*]该参数是可选的。
[*]该参数用于形貌Task 的属性,比如qos 等
返回值



[*]不涉及。
形貌



[*]发起用户对ffrt_submit_base进行封装后调用,具体可参考样例。
[*]ffrt_submit_base作为底层本领,利用时需要满足以下限定:

[*]ffrt_submit_base入参中的func指针只能通过ffrt_alloc_auto_managed_function_storage_base申请,且二者的调用需逐一对应。
[*]ffrt_alloc_auto_managed_function_storage_base申请的内存为ffrt_auto_managed_function_storage_size字节,其生命周期归ffrt管理,在该task竣事时,由FFRT自动开释,用户无需开释。

[*]ffrt_function_header_t 中界说了两个函数指针:

[*]exec:用于形貌该Task怎样被实验,当FFRT需要实验该Task时由FFRT调用。
[*]destroy:用于形貌该Task怎样被烧毁,当FFRT需要烧毁该Task时由FFRT调用。

样例

template<class T>
struct function {
    template<class CT>
    function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
    ffrt_function_header_t header;
    T closure;
};

template<class T>
void exec_function_wrapper(void* t)
{
    auto f = (function<std::decay_t<T>>*)t;
    f->closure();
}

template<class T>
void destroy_function_wrapper(void* t)
{
    auto f = (function<std::decay_t<T>>*)t;
    f->closure = nullptr;
}

template<class T>
inline ffrt_function_header_t* create_function_wrapper(T&& func)
{
    using function_type = function<std::decay_t<T>>;
    static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size,
      "size of function must be less than ffrt_auto_managed_function_storage_size");

    auto p = ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    auto f = new (p) function_type(
      {exec_function_wrapper<T>, destroy_function_wrapper<T>},
      std::forward<T>(func));
    return (ffrt_function_header_t*)f;
}

static inline void submit(std::function<void()>&& func)
{
    return ffrt_submit_base(create_function_wrapper(std::move(func)), NULL, NULL, NULL);
}
ffrt_wait



[*]同步期待,与ffrt_submit_base 共同利用。 * 期待指定的数据被生产完成,或期待当前任务的全部子任务完成,在不满足条件之前,当前的实验上下文被suspend,在满足条件后规复实验。
声明

void ffrt_wait_deps(ffrt_deps_t* deps);
void ffrt_wait();
参数

deps


[*]需要期待被生产完成的数据的假造所在,这些所在可能作为某些任务在submit 时的out_deps,该依赖的天生见ffrt_deps_t章节,空指针表示无依赖。
返回值



[*]不涉及。
形貌



[*]ffrt_wait_deps(deps) 用于期待deps指代的数据被生产完成才能实验后面的代码。
[*]ffrt_wait() 用于期待当前上下文提交的全部子任务(留意:不包罗孙任务和下级子任务)都完成才能实验后面的代码。
[*]该接口支持在FFRT task 内部调用,也支持在FFRT task 外部调用。
[*]在FFRT task 外部调用的wait 是OS 可以或许感知的期待,相对于FFRT task 内部调用的wait 是更加昂贵的,因此我们希望尽可能让更多的wait 发生在FFRT task 内部 ,而不是FFRT task 外部。
样例

recursive fibonacci
串行版的fibonacci 可以实现为:
#include <stdio.h>

void fib(int x, int* y) {
    if (x <= 1) {
      *y = x;
    } else {
      int y1, y2;
      fib(x - 1, &y1);
      fib(x - 2, &y2);
      *y = y1 + y2;
    }
}
int main(int narg, char** argv)
{
    int r;
    fib(10, &r);
    printf("fibonacci 10: %d\n", r);
    return 0;
}
若要利用 FFRT 实现并行(注,对于单纯的fibonacci,单个 Task 的计算量极小,不具有并行加快的意义,但这种调用pattern 对并行编程模子的灵活性考验黑白常高的),其中1种可行的实现为:
#include <stdio.h>
#include "ffrt.h"//包含所有ffrt涉及的头文件

typedef struct {
    int x;
    int* y;
} fib_ffrt_s;

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} c_function;

static void ffrt_exec_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->func) {
      f->func(f->arg);
    }
}

static void ffrt_destroy_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg)
{
    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    f->header.exec = ffrt_exec_function_wrapper;
    f->header.destroy = ffrt_destroy_function_wrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return (ffrt_function_header_t*)f;
}

static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}

#define ffrt_deps_define(name, dep1, ...) const void* __v_##name[] = {dep1, ##__VA_ARGS__}; \
    ffrt_deps_t name = {sizeof(__v_##name) / sizeof(void*), __v_##name}

void fib_ffrt(void* arg)
{
    fib_ffrt_s* p = (fib_ffrt_s*)arg;
    int x = p->x;
    int* y = p->y;

    if (x <= 1) {
      *y = x;
    } else {
      int y1, y2;
      fib_ffrt_s s1 = {x - 1, &y1};
      fib_ffrt_s s2 = {x - 2, &y2};
      ffrt_deps_define(dx, &x);
      ffrt_deps_define(dy1, &y1);
      ffrt_deps_define(dy2, &y2);
      ffrt_deps_define(dy12, &y1, &y2);
      ffrt_submit_c(fib_ffrt, NULL, &s1, &dx, &dy1, NULL);
      ffrt_submit_c(fib_ffrt, NULL, &s2, &dx, &dy2, NULL);
      ffrt_wait_deps(&dy12);
      *y = y1 + y2;
    }
}

int main(int narg, char** argv)
{
    int r;
    fib_ffrt_s s = {10, &r};
    ffrt_deps_define(dr, &r);
    ffrt_submit_c(fib_ffrt, NULL, &s, NULL, &dr, NULL);
    ffrt_wait_deps(&dr);
    printf("fibonacci 10: %d\n", r);
    return 0;
}
解析:

[*] 将fibonacci (x-1)和fibonacci (x-2) 作为2个Task 提交给FFRT,在两个Task 完成之后将效果累加;
[*] 虽然单个Task 只能拆分成2个SubTask 但是子Task 可以继承拆分,因此,整个计算图的并行度黑白常高的,Task 之间在FFRT 内部形成了一颗调用树;
https://i-blog.csdnimg.cn/blog_migrate/d13119cd5f6c41a5f80ea5f7c453cda6.png
   以上实现,因为需要用户显式管理数据生命周期和函数入参打包两个因素,所以使得代码实现非常复杂。
ffrt_deps_t



[*]C API中对依赖数组的抽象,逻辑上等同于C++ API中的std::vector<void*>
声明

typedef enum {
    ffrt_dependence_data,
    ffrt_dependence_task,
} ffrt_dependence_type_t;

typedef struct {
    ffrt_dependence_type_t type;
    const void* ptr;
} ffrt_dependence_t;

typedef struct {
    uint32_t len;
    const ffrt_dependence_t* items;
} ffrt_deps_t;
参数

len


[*]所依赖的Signature的个数,取值大于等于0。
item


[*]len个Signature的起始所在指针。
type


[*]当前依赖为数据依赖还是任务依赖。
ptr


[*]所依赖对应Signature内容的现实所在。
返回值



[*]不涉及。
形貌



[*]item为len个Signature的起始指针,该指针可以指向堆空间,也可以指向栈空间,但是要求分配的空间大于等于len * sizeof(ffrt_dependence_t)。
样例



[*]创建数据依赖大概任务依赖:
// 创建数据依赖的ffrt_deps_t
int x = 0;
const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}};
ffrt_deps_t in{static_cast<uint32_t>(in_deps.size()), in_deps.data()};

// 提交某个返回handle任务
ffrt_task_handle_t task = ffrt_submit_h_base(
      ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr);
// 创建任务依赖的ffrt_deps_t
const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}};
ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
ffrt_task_attr_t



[*]界说task 的属性的辅助类,与ffrt_submit_base 共同利用。
声明

typedef enum {
    ffrt_qos_inherent = -1,
    ffrt_qos_background,
    ffrt_qos_utility,
    ffrt_qos_default,
    ffrt_qos_user_initiated,
} ffrt_qos_default_t;

typedef int ffrt_qos_t;

typedef struct {
    uint32_t storage[(ffrt_task_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
} ffrt_task_attr_t;
typedef void* ffrt_task_handle_t;

int ffrt_task_attr_init(ffrt_task_attr_t* attr);
void ffrt_task_attr_destroy(ffrt_task_attr_t* attr);
void ffrt_task_attr_set_qos(ffrt_task_attr_t* attr, ffrt_qos_t qos);
ffrt_qos_t ffrt_task_attr_get_qos(const ffrt_task_attr_t* attr);
void ffrt_task_attr_set_name(ffrt_task_attr_t* attr, const char* name);
const char* ffrt_task_attr_get_name(const ffrt_task_attr_t* attr);
void ffrt_task_attr_set_delay(ffrt_task_attr_t* attr, uint64_t delay_us);
uint64_t ffrt_task_attr_get_delay(const ffrt_task_attr_t* attr);
参数

attr


[*]创建的tasks属性的句柄。
qos


[*]qos 设定的罗列类型。
[*]inherent 是一个qos 设定策略,代表即将ffrt_submit 的task 的qos 继承当前task 的qos。
delay_us


[*]任务延迟实验的时间,单位为us。
返回值



[*]不涉及。
形貌



[*]attr所通报的内容会在ffrt_submit内部完成取存,ffrt_submit返回后用户即可烧毁。
[*]约定:

[*]在submit 时,如果不通过task_attr 设定qos,那么默认该提交的task的qos 为ffrt_qos_default。
[*]在submit 时,如果通过task_attr 设定qos 为ffrt_qos_inherent,表示将该提交的task 的qos 与当前task 的qos 相同,在FFRT task 外部提交的属性为ffrt_qos_inherent 的task,其qos 为ffrt_qos_default。
[*]其他情况下,该提交的task 的qos 被设定为指定的值。

[*]ffrt_task_attr_t对象的置空和烧毁由用户完成,对同一个ffrt_task_attr_t仅能调用一次ffrt_task_attr_destroy,重复对同一个ffrt_task_attr_t调用ffrt_task_attr_destroy,其行为是未界说的。
[*]在ffrt_task_attr_destroy之后再对task_attr进行访问,其行为是未界说的。
样例



[*]提交一个qos 级别为background 的任务:
#include <stdio.h>
#include "ffrt.h"

void my_print(void* arg)
{
    printf("hello ffrt\n");
}

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} c_function;

static void ffrt_exec_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->func) {
      f->func(f->arg);
    }
}

static void ffrt_destroy_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg)
{
    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    f->header.exec = ffrt_exec_function_wrapper;
    f->header.destroy = ffrt_destroy_function_wrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return (ffrt_function_header_t*)f;
}

static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}

int main(int narg, char** argv)
{
    ffrt_task_attr_t attr;
    ffrt_task_attr_init(&attr);
    ffrt_task_attr_set_qos(&attr, ffrt_qos_background);
    ffrt_task_attr_set_delay(&attr, 10000);
    ffrt_submit_c(my_print, NULL, NULL, NULL, NULL, &attr);
    ffrt_task_attr_destroy(&attr);
    ffrt_wait();
    return 0;
}
ffrt_submit_h_base



[*]向调度器提交一个task,与ffrt_submit_base 的差别在于返回task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。
声明

typedef void* ffrt_task_handle_t;

ffrt_task_handle_t ffrt_submit_h_base(ffrt_function_t func, void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr);
void ffrt_task_handle_destroy(ffrt_task_handle_t handle);
参数

func


[*]CPU Function的指针,该指针实验的数据布局,按照ffrt_function_header_t界说的形貌了该CPU Task怎样实验和烧毁的函数指针,FFRT通过这两个函数指针完成Task的实验和烧毁。
in_deps


[*]该参数是可选的。
[*]该参数用于形貌该任务的输入依赖,FFRT 通过数据的假造所在作为数据的Signature 来建立依赖。
out_deps


[*]该参数是可选的。
[*]该参数用于形貌该任务的输出依赖。
[*]留意:该依赖值本质上是一个数值,ffrt没办法区分该值是公道的还是不公道的,会假定输入的值是公道的进行处理;但不发起接纳NULL,1, 2 等值来建立依赖关系,发起接纳现实的内存所在,因为前者利用不妥会建立起不须要的依赖,影响并发。
attr


[*]该参数是可选的。
[*]该参数用于形貌Task 的属性,比如qos 等
返回值



[*]task 的句柄,该句柄可以用于建立task 之间的依赖,或用于在wait 语句中实现同步。
形貌



[*]C API中的ffrt_task_handle_t需要用户调用ffrt_task_handle_destroy显式烧毁。
[*]C API中的task_handle_t对象的置空和烧毁由用户完成,对同一个ffrt_task_handle_t仅能调用一次ffrt_task_handle_destroy,重复对同一个ffrt_task_handle_t调用ffrt_task_handle_destroy,其行为是未界说的。
[*]在ffrt_task_handle_destroy之后再对ffrt_task_handle_t进行访问,其行为是未界说的。
样例

#include <stdio.h>
#include "ffrt.h"

void func0(void* arg)
{
    printf("hello ");
}

void func1(void* arg)
{
    (*(int*)arg)++;
}

void func2(void* arg)
{
    printf("world, x = %d\n", *(int*)arg);
}

void func3(void* arg)
{
    printf("handle wait");
    (*(int*)arg)++;
}

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} c_function;

static void ffrt_exec_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->func) {
      f->func(f->arg);
    }
}

static void ffrt_destroy_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg)
{
    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    f->header.exec = ffrt_exec_function_wrapper;
    f->header.destroy = ffrt_destroy_function_wrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return (ffrt_function_header_t*)f;
}

static inline ffrt_task_handle_t ffrt_submit_h_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    return ffrt_submit_h_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}

static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}


int main(int narg, char** argv)
{
    // handle work with submit
    ffrt_task_handle_t h = ffrt_submit_h_c(func0, NULL, NULL, NULL, NULL, NULL); // not need some data in this task
    int x = 1;
    const std::vector<ffrt_dependence_t> in_deps = {{ffrt_dependence_data, &x}};
    ffrt_deps_t d2{static_cast<uint32_t>(in_deps.size()), in_deps.data()};

    const std::vector<ffrt_dependence_t> out_deps = {{ffrt_dependence_data, &x}};
    ffrt_deps_t d1{static_cast<uint32_t>(out_deps.size()), out_deps.data()};

    ffrt_submit_c(func1, NULL, &x, NULL, &d1, NULL);
    ffrt_submit_c(func2, NULL, &x, &d2, NULL, NULL); // this task depend x and h
    ffrt_task_handle_destroy(h);
   
    // handle work with wait
    ffrt_task_handle_t h2 = ffrt_submit_h_c(func3, NULL, &x, NULL, NULL, NULL);

    const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, h2}};
    ffrt_deps_t d3{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
    ffrt_wait_deps(&d3);
    ffrt_task_handle_destroy(h2);
    printf("x = %d", x);
    ffrt_wait();
    return 0;
}


[*]预期的输出为:
hello world, x = 2
handle wait
x = 3
ffrt_this_task_get_id



[*]返回当前task的id标识,更多利用用于维测(原因是task name可能重名)。
声明

uint64_t ffrt_this_task_get_id();
参数



[*]不涉及。
返回值



[*]当前task的id。
形貌



[*]该接口在task内部调用将返回当前task的id标识,在task外部调用将返回0。
[*]可以基于该接口在task外部调用返回0的特性来区分函数是运行在FFRT 工作线程上还黑白FFRT工作线程上。
[*]task id为从1开始编码,每提交一个task便增长1,被计划成64bit,即便是每秒百万次提交,也需要292471.2年才会发生翻转。
样例



[*]忽略。
ffrt_this_task_update_qos



[*]更新当前正在实验的task的优先级。
声明

int ffrt_this_task_update_qos(ffrt_qos_t qos);
参数



[*]qos 新的优先级。
返回值



[*]0表示成功,非0表示失败。
形貌



[*]该接口对当前task的qos调解会立即见效。
[*]如果新设定的qos与当前的qos不一致,则会block当前task的实验,再按照新的qos规复实验。
[*]如果新设定的qos与当前的qos一致,则接口会立即返回0,不做任那边理。
[*]如果在非task内部调用该接口,则返回非0值,用户可以选择忽略或其他处理。
样例



[*]忽略。
串行队列



[*]FFRT提供queue来实现Andorid中类似WorkQueue本领,且在利用得当的情况下将有更好的性能。
ffrt_queue_attr_t

声明

typedef struct {
    uint32_t storage[(ffrt_queue_attr_storage_size + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
} ffrt_queue_attr_t;

int ffrt_queue_attr_init(ffrt_queue_attr_t* attr);
void ffrt_queue_attr_destroy(ffrt_queue_attr_t* attr);
参数

attr


[*]该参数是指向未初始化的ffrt_queue_attr_t。
返回值



[*]若成功返回0,否则返回-1。
形貌



[*]ffrt_queue_attr_t用于创建ffrt_queue_t且不单独利用,因此必须在创建队列前先创建好队列属性。
[*]ffrt_queue_attr_t对象的置空和烧毁由用户完成,对同一个ffrt_queue_attr_t仅能调用一次ffrt_queue_attr_destroy,重复对同一个ffrt_queue_attr_t调用ffrt_queue_attr_destroy,其行为是未界说的。
[*]在ffrt_queue_attr_destroy之后再对ffrt_queue_attr_t进行访问,其行为是未界说的。
样例

参考ffrt_queue_t章节的样例。
ffrt_queue_t

声明

typedef enum { ffrt_queue_serial, ffrt_queue_max } ffrt_queue_type_t;
typedef void* ffrt_queue_t;

ffrt_queue_t ffrt_queue_create(ffrt_queue_type_t type, const char* name, const ffrt_queue_attr_t* attr)
void ffrt_queue_destroy(ffrt_queue_t queue)
参数

type


[*]该参数用于形貌创建的队列类型。
name


[*]该参数用于形貌创建队列的名字。
attr


[*]该参数用于形貌queue的属性,详见ffrt_queue_attr_t章节。
返回值



[*]若成功则返回新创建的队列,否则返回空指针。
形貌



[*]提交至该队列的任务将按照次序实验,如果某个提交的任务中发生阻塞,则无法包管该任务的实验次序。
[*]ffrt_queue_t对象的置空和烧毁由用户完成,对同一个ffrt_queue_t仅能调用一次ffrt_queue_destroy,重复对同一个ffrt_queue_t调用ffrt_queue_destroy,其行为是未界说的。
[*]在ffrt_queue_destroy之后再对ffrt_queue_t进行访问,其行为是未界说的。
样例

#include <stdio.h>
#include "ffrt.h"

using namespace std;

template<class T>
struct Function {
    template<class CT>
    Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
    ffrt_function_header_t header;
    T closure;
};

template<class T>
void ExecFunctionWrapper(void* t)
{
    auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
    f->closure();
}

template<class T>
void DestroyFunctionWrapper(void* t)
{
    auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
    f->closure = nullptr;
}

template<class T>
static inline ffrt_function_header_t* create_function_wrapper(T&& func,
    ffrt_function_kind_t kind = ffrt_function_kind_general)
{
    using function_type = Function<std::decay_t<T>>;
    auto p = ffrt_alloc_auto_managed_function_storage_base(kind);
    auto f =
      new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func));
    return reinterpret_cast<ffrt_function_header_t*>(f);
}

int main(int narg, char** argv)
{
    ffrt_queue_attr_t queue_attr;
    (void)ffrt_queue_attr_init(&queue_attr);
    ffrt_queue_t queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);

    ffrt_queue_submit(queue_handle, create_function_wrapper([]() {printf("Task done.\n");}, ffrt_function_kind_queue), nullptr);

    ffrt_queue_attr_destroy(&queue_attr);
    ffrt_queue_destroy(queue_handle);
}
同步原语

ffrt_mutex_t



[*]FFRT提供的类似pthread mutex 的性能实现。
声明

typedef enum {
    ffrt_error = -1,
    ffrt_success = 0,
    ffrt_error_nomem = ENOMEM,
    ffrt_error_timedout = ETIMEDOUT,
    ffrt_error_busy = EBUSY,
    ffrt_error_inval = EINVAL
} ffrt_error_t;

struct ffrt_mutex_t;

int ffrt_mutex_init(ffrt_mutex_t* mutex, const ffrt_mutexattr_t* attr);
int ffrt_mutex_lock(ffrt_mutex_t* mutex);
int ffrt_mutex_unlock(ffrt_mutex_t* mutex);
int ffrt_mutex_trylock(ffrt_mutex_t* mutex);
int ffrt_mutex_destroy(ffrt_mutex_t* mutex);
参数

attr


[*]当前FFRT只支持底子类型的mutex,因此attr必须为空指针。
mutex


[*]指向所操作的互斥锁的指针。
返回值



[*]若成功则为 ffrt_success ,否则发生错误。
形貌



[*]该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未界说的行为。
[*]该功能可以或许避免pthread传统的pthread_mutex_t 在抢不到锁时陷入内核的标题,在利用得当的条件下将会有更好的性能。
[*]留意:目前暂不支持递归和定时功能。
[*]留意:C API中的ffrt_mutex_t需要用户调用ffrt_mutex_init和ffrt_mutex_destroy显式创建和烧毁。
[*]留意:C API中的ffrt_mutex_t对象的置空和烧毁由用户完成,对同一个ffrt_mutex_t仅能调用一次ffrt_mutex_destroy,重复对同一个ffrt_mutex_t调用ffrt_mutex_destroy,其行为是未界说的。
[*]留意:在ffrt_mutex_destroy之后再对ffrt_mutex_t进行访问,其行为是未界说的。
样例

#include <stdio.h>
#include "ffrt.h"

typedef struct {
    int* sum;
    ffrt_mutex_t* mtx;
} tuple;

void func(void* arg)
{
    tuple* t = (tuple*)arg;
   
    int ret = ffrt_mutex_lock(t->mtx);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    (*t->sum)++;
    ret = ffrt_mutex_unlock(t->mtx);
    if (ret != ffrt_success) {
      printf("error\n");
    }
}

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} c_function;

static void ffrt_exec_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->func) {
      f->func(f->arg);
    }
}

static void ffrt_destroy_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg)
{
    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    f->header.exec = ffrt_exec_function_wrapper;
    f->header.destroy = ffrt_destroy_function_wrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return (ffrt_function_header_t*)f;
}

static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}

void ffrt_mutex_task()
{
    int sum = 0;
    ffrt_mutex_t mtx;
    tuple t = {&sum, &mtx};
    int ret = ffrt_mutex_init(&mtx, NULL);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    for (int i = 0; i < 10; i++) {
      ffrt_submit_c(func, NULL, &t, NULL, NULL, NULL);
    }
    ffrt_mutex_destroy(&mtx);
    ffrt_wait();
    printf("sum = %d", sum);
}

int main(int narg, char** argv)
{
    int r;
    ffrt_submit_c(ffrt_mutex_task, NULL, NULL, NULL, NULL, NULL);
    ffrt_wait();
    return 0;
}
预期输出为:
sum=10


[*]该例子为功能示例,现实中并不鼓励这样利用。
ffrt_cond_t



[*]FFRT提供的类似pthread 信号量的性能实现。
声明

typedef enum {
    ffrt_error = -1,
    ffrt_success = 0,
    ffrt_error_nomem = ENOMEM,
    ffrt_error_timedout = ETIMEDOUT,
    ffrt_error_busy = EBUSY,
    ffrt_error_inval = EINVAL
} ffrt_error_t;

struct ffrt_cond_t;

int ffrt_cond_init(ffrt_cond_t* cond, const ffrt_condattr_t* attr);
int ffrt_cond_signal(ffrt_cond_t* cond);
int ffrt_cond_broadcast(ffrt_cond_t* cond);
int ffrt_cond_wait(ffrt_cond_t*cond, ffrt_mutex_t* mutex);
int ffrt_cond_timedwait(ffrt_cond_t* cond, ffrt_mutex_t* mutex, const struct timespec* time_point);
int ffrt_cond_destroy(ffrt_cond_t* cond);
参数

cond


[*]指向所操作的信号量的指针。
attr


[*]属性设定,空指针表示利用默认属性。
mutex


[*]指向要在阻塞期间解锁的互斥锁的指针。
time_point


[*]指向指定期待时限时间的对象的指针。
返回值



[*]若成功则为 ffrt_success,若在锁定互斥前抵达时限则为 ffrt_error_timedout。
形貌



[*]该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未界说的行为。
[*]该功能可以或许避免传统的pthread_cond_t在条件不满足时陷入内核的标题,在利用得当的条件下将会有更好的性能。
[*]留意:C API中的ffrt_cond_t需要用户调用ffrt_cond_init和ffrt_cond_destroy显式创建和烧毁。
[*]留意:C API中的ffrt_cond_t对象的置空和烧毁由用户完成,对同一个ffrt_cond_t仅能调用一次ffrt_cond_destroy,重复对同一个ffrt_cond_t调用ffrt_cond_destroy,其行为是未界说的。
[*]留意:在ffrt_cond_destroy之后再对ffrt_cond_t进行访问,其行为是未界说的。
样例

#include <stdio.h>
#include "ffrt.h"

typedef struct {
    ffrt_cond_t* cond;
    int* a;
    ffrt_mutex_t* lock_;
} tuple;

void func1(void* arg)
{
    tuple* t = (tuple*)arg;
    int ret = ffrt_mutex_lock(t->lock_);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    while (*t->a != 1) {
      ret = ffrt_cond_wait(t->cond, t->lock_);
      if (ret != ffrt_success) {
            printf("error\n");
      }
    }
    ret = ffrt_mutex_unlock(t->lock_);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    printf("a = %d", *(t->a));
}

void func2(void* arg)
{
    tuple* t = (tuple*)arg;
    int ret = ffrt_mutex_lock(t->lock_);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    *(t->a) = 1;
    ret = ffrt_cond_signal(t->cond);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    ret = ffrt_mutex_unlock(t->lock_);
    if (ret != ffrt_success) {
      printf("error\n");
    }
}

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} c_function;

static void ffrt_exec_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->func) {
      f->func(f->arg);
    }
}

static void ffrt_destroy_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg)
{
    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    f->header.exec = ffrt_exec_function_wrapper;
    f->header.destroy = ffrt_destroy_function_wrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return (ffrt_function_header_t*)f;
}

static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}

void ffrt_cv_task()
{
    ffrt_cond_t cond;
    int ret = ffrt_cond_init(&cond, NULL);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    int a = 0;
    ffrt_mutex_t lock_;
    tuple t = {&cond, &a, &lock_};
    ret = ffrt_mutex_init(&lock_, NULL);
    if (ret != ffrt_success) {
      printf("error\n");
    }
    ffrt_submit_c(func1, NULL, &t, NULL, NULL, NULL);
    ffrt_submit_c(func2, NULL, &t, NULL, NULL, NULL);
    ffrt_wait();
    ffrt_cond_destroy(&cond);
    ffrt_mutex_destroy(&lock_);
}

int main(int narg, char** argv)
{
    ffrt_submit_c(ffrt_cv_task, NULL, NULL, NULL, NULL, NULL);
    ffrt_wait();
    return 0;
}
预期输出为:
a=1


[*]该例子为功能示例,现实中并不鼓励这样利用。
杂项

ffrt_usleep



[*]FFRT提供的类似C11 sleep和linux usleep的性能实现。
声明

int ffrt_usleep(uint64_t usec);
参数

usec


[*]就寝的us数。
返回值



[*]不涉及。
形貌



[*]该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未界说的行为。
[*]该功能可以或许避免传统的sleep 就寝时陷入内核的标题,在利用得当的条件下将会有更好的性能。
样例

#include <time.h>
#include <stdio.h>
#include "ffrt.h"

void func(void* arg)
{
    printf("Time: %s", ctime(&(time_t){time(NULL)}));
    ffrt_usleep(2000000); // 睡眠 2 秒
    printf("Time: %s", ctime(&(time_t){time(NULL)}));
}

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} c_function;

static void ffrt_exec_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->func) {
      f->func(f->arg);
    }
}

static void ffrt_destroy_function_wrapper(void* t)
{
    c_function* f = (c_function*)t;
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg)
{
    FFRT_STATIC_ASSERT(sizeof(c_function) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    c_function* f = (c_function*)ffrt_alloc_auto_managed_function_storage_base(ffrt_function_kind_general);
    f->header.exec = ffrt_exec_function_wrapper;
    f->header.destroy = ffrt_destroy_function_wrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return (ffrt_function_header_t*)f;
}

static inline void ffrt_submit_c(ffrt_function_t func, const ffrt_function_t after_func,
    void* arg, const ffrt_deps_t* in_deps, const ffrt_deps_t* out_deps, const ffrt_task_attr_t* attr)
{
    ffrt_submit_base(ffrt_create_function_wrapper(func, after_func, arg), in_deps, out_deps, attr);
}

int main(int narg, char** argv)
{
    ffrt_submit_c(func, NULL, NULL, NULL, NULL, NULL);
    ffrt_wait();
    return 0;
}
ffrt_yield



[*]当前task 自动让出CPU 实验资源,让其他可以被实验的task 先实验,如果没有其他可被实验的task,yield 无效。
声明

void ffrt_yield();
参数



[*]不涉及。
返回值



[*]不涉及。
形貌



[*]该接口只能在FFRT task 内部调用,在FFRT task 外部调用存在未界说的行为。
[*]此函数的确切行为取决于实现,特殊是利用中的FFRT 调度步伐的机制和体系状态。
样例



[*]省略。
开辟步调

以下步调形貌了怎样利用FFRT提供的Native API接口,创建并行任务和串行队列任务以及烧毁相应资源。
添加动态链接库
CMakeLists.txt中添加以下lib:
libffrt.z.so
头文件
#include "ffrt/task.h"
#include "ffrt/type_def.h"
#include "ffrt/condition_variable.h"
#include "ffrt/mutex.h"
#include "ffrt/queue.h"
#include "ffrt/sleep.h"

[*]首先需要对实验的函数进行封装。
// 第一种使用模板,支持C++
template<class T>
struct Function {
    template<class CT>
    Function(ffrt_function_header_t h, CT&& c) : header(h), closure(std::forward<CT>(c)) {}
    ffrt_function_header_t header;
    T closure;
};

template<class T>
void ExecFunctionWrapper(void* t)
{
    auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
    f->closure();
}

template<class T>
void DestroyFunctionWrapper(void* t)
{
    auto f = reinterpret_cast<Function<std::decay_t<T>>*>(t);
    f->closure = nullptr;
}

template<class T>
static inline ffrt_function_header_t* create_function_wrapper(T&& func,
    ffrt_function_kind_t kind = ffrt_function_kind_general)
{
    using function_type = Function<std::decay_t<T>>;
    auto p = ffrt_alloc_auto_managed_function_storage_base(kind);
    auto f =
      new (p)function_type({ ExecFunctionWrapper<T>, DestroyFunctionWrapper<T>, { 0 } }, std::forward<T>(func));
    return reinterpret_cast<ffrt_function_header_t*>(f);
}

// 第二种创建方式
typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} CFunction;

static void FfrtExecFunctionWrapper(void* t)
{
    CFunction* f = static_cast<CFunction*>(t);
    if (f->func) {
      f->func(f->arg);
    }
}

static void FfrtDestroyFunctionWrapper(void* t)
{
    CFunction* f = static_cast<CFunction*>(t);
    if (f->after_func) {
      f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* ffrt_create_function_wrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_general)
{
    FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size,
      size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t));
    f->header.exec = FfrtExecFunctionWrapper;
    f->header.destroy = FfrtDestroyFunctionWrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return reinterpret_cast<ffrt_function_header_t*>(f);
}

// 样例:待提交执行的函数
void OnePlusForTest(void* arg)
{
    (*static_cast<int*>(arg)) += 1;
}

[*]设置task属性值。
用户提交任务时可以设置任务属性,包罗qos优先级,名称等,具体可参考接口文档。
// ******初始化并行任务属性******
ffrt_task_attr_t attr;
ffrt_task_attr_init(&attr);

// ******创建串行队列******

// 创建串行队列的属性
ffrt_queue_attr_t queue_attr;
// 创建串行队列的handle
ffrt_queue_t queue_handle;

// 初始化队列属性
(void)ffrt_queue_attr_init(&queue_attr);

// 如有需要,设置指定优先级
ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit));
// 如有需要,设置超时时间(ms)
ffrt_queue_attr_set_timeout(&queue_attr, 10000);
// 如有需要,设置超时回调
int x = 0;
ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x,
    ffrt_function_kind_queue));

// 基于属性,初始化队列
queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);

[*]提交任务。
int a = 0;
// ******并行任务******
// 提交不带handle返回值的并行任务
ffrt_submit_base(ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr);
// 提交带handle返回值的并行任务
ffrt_task_handle_t task = ffrt_submit_h_base(
    ffrt_create_function_wrapper(OnePlusForTest, NULL, &a), NULL, NULL, &attr);

// ******串行任务******
// 提交不返回handle的串行队列任务
ffrt_queue_submit(queue_handle, ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a,
    ffrt_function_kind_queue), nullptr);
// 提交带handle的串行队列任务
ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle,
    ffrt_create_function_wrapper(OnePlusForTest, nullptr, &a, ffrt_function_kind_queue), nullptr);

// 如果需要等待执行结果,则调用wait
const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}};
ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
ffrt_wait_deps(&wait);

ffrt_queue_wait(handle);

[*]任务提交完成后烧毁相应资源。
// ******销毁并行任务******
ffrt_task_attr_destroy(&attr);
ffrt_task_handle_destroy(task);

// ******销毁串行队列任务******
// 先销毁任务handle,再销毁队列
ffrt_queue_attr_destroy(&queue_attr);
ffrt_task_handle_destroy(handle);
ffrt_queue_destroy(queue_handle);
利用发起

发起1: 函数化

根本头脑:计算过程函数化


[*]步伐过程各步调以函数封装表达,函数满足类纯函数特性。
[*]无全局数据访问。
[*]无内部状态保留。
[*]通过ffrt_submit_base()接口以异步任务方式提交函数实验。
[*]将函数访问的数据对象以及访问方式在ffrt_submit_base()接口中的in_deps/out_deps参数表达。
[*]步伐员通过inDeps/outDeps参数表达任务间依赖关系以包管步伐实验的正确性。
   做到纯函数的好处在于:1. 可以或许最大化发掘并行度,2.避免DataRace和锁的标题。
在现实中,可以根据场景放松纯函数的约束,但前提是:


[*]确定添加的in_deps/out_deps可确保步伐正确实验。
[*]通过FFRT提供的锁机制保护对全局变量的访问。
发起2: 利用FFRT提供的替换API



[*]克制在FFRT任务中利用体系线程库API创建线程,利用submit提交任务。
[*]利用FFRT提供的锁,条件变量,就寝,IO等API代替体系线程库API。

[*]利用体系线程库API可能造成工作线程阻塞,引起额外性能开销。

发起3: Deadline机制



[*]必须用于具备周期/重复实验特性的处理流程。
[*]在有明白时间约束和性能关键的处理流程中利用,避免滥用。
[*]在相对大颗粒度的处理流程中利用,例如具有16.6ms时间约束的帧处理流程。
发起4: 从线程模子迁移



[*]创建线程替换为创建FFRT任务。

[*]线程从逻辑上类似无in_deps的任务。

[*]识别线程间的依赖关系,并将其表达在任务的依赖关系in_deps/out_deps上。
[*]线程内计算过程分解为异步任务调用。
[*]通过任务依赖关系和锁机制避免并发任务数据竞争标题。
已知限定

C API中初始化ffrt对象后,对象的置空与烧毁由用户负责



[*]为包管较高的性能,ffrt的C API中内部不包罗对对象的烧毁状态的标志,用户需要公道地进行资源的开释,重复调用各个对象的destroy操作,其效果是未界说的。
[*]错误示例1,重复调用destroy可能造成不可预知的数据破坏。
#include "ffrt.h"
void abnormal_case_1()
{
    ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
    ...
    ffrt_task_handle_destroy(h);
    ffrt_task_handle_destroy(h); // double free
}


[*]错误示例2,未调用destroy会造成内存泄漏
#include "ffrt.h"
void abnormal_case_2()
{
    ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
    ...
    // memory leak
}


[*]发起示例,仅调用一次destroy,如有须要可进行置空
#include "ffrt.h"
void normal_case()
{
    ffrt_task_handle_t h = ffrt_submit_h_base([](){printf("Test task running...\n");}, NULL, NULL, NULL, NULL, NULL);
    ...
    ffrt_task_handle_destroy(h);
    h = nullptr; // if necessary
}
为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开辟技能,这边特意整理了《鸿蒙开辟学习手册》(共计890页),希望对大家有所资助:https://qr21.cn/FV7h05
《鸿蒙开辟学习手册》:

怎样快速入门:https://qr21.cn/FV7h05


[*]根本概念
[*]构建第一个ArkTS应用
[*]……
https://i-blog.csdnimg.cn/blog_migrate/da60b8f42181f7f9d7276a5749b952ba.png
开辟底子知识:https://qr21.cn/FV7h05


[*]应用底子知识
[*]配置文件
[*]应用数据管理
[*]应用安全管理
[*]应用隐私保护
[*]三方应用调用管控机制
[*]资源分类与访问
[*]学习ArkTS语言
[*]……
https://i-blog.csdnimg.cn/blog_migrate/399acf2dd0083550d646e7127303d2e6.png
基于ArkTS 开辟:https://qr21.cn/FV7h05


[*]Ability开辟
[*]UI开辟
[*]公共变乱与通知
[*]窗口管理
[*]媒体
[*]安全
[*]网络与链接
[*]电话服务
[*]数据管理
[*]后台任务(Background Task)管理
[*]装备管理
[*]装备利用信息统计
[*]DFX
[*]国际化开辟
[*]折叠屏系列
[*]……
https://i-blog.csdnimg.cn/blog_migrate/52f2adda9e3bb6ab141e75301e2d7452.png
鸿蒙开辟面试真题(含参考答案):https://qr18.cn/F781PH

https://i-blog.csdnimg.cn/blog_migrate/c1f8cde7842aea94dbfea28766d56bda.png
鸿蒙开辟面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开辟必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开辟体系底层方向
5.鸿蒙音视频开辟方向
6.鸿蒙车载开辟方向
7.鸿蒙南向开辟方向
https://i-blog.csdnimg.cn/blog_migrate/54d2bb79318ad66bb11729cd0e18fa32.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙HarmonyOS应用开辟之FFRT 开辟引导