4. NPU任务队列基础实现
任务目标
在RISC-V多核处理器环境中,NPU(神经网络处理单元)任务调度需要先解决任务的组织与管理问题。本案例实现一个基础的NPU任务队列,提供任务入队(enqueue)和出队(dequeue)操作,为后续多核调度优化奠定数据结构基础。
数据结构设计
任务队列采用环形缓冲区实现,避免元素搬移开销,适合NPU任务这种固定大小的描述符场景。每个任务描述符包含任务ID、优先级和状态字段,队列本身记录头尾索引及当前深度。
完整实现代码
// npu_task_queue.h
#ifndef NPU_TASK_QUEUE_H
#define NPU_TASK_QUEUE_H
#include <stdint.h>
#include <stdbool.h>
#define NPU_QUEUE_CAPACITY 16
typedef enum {
TASK_PENDING = 0,
TASK_RUNNING,
TASK_DONE
} task_state_t;
typedef struct {
uint32_t task_id;
uint8_t priority; // 0~255,数值越大优先级越高
task_state_t state;
} npu_task_t;
typedef struct {
npu_task_t buffer[NPU_QUEUE_CAPACITY];
uint8_t head; // 出队位置
uint8_t tail; // 入队位置
uint8_t count; // 当前队列中的任务数
} npu_task_queue_t;
// 初始化队列
void npu_queue_init(npu_task_queue_t *q);
// 入队:成功返回true,队列满返回false
bool npu_queue_enqueue(npu_task_queue_t *q, const npu_task_t *task);
// 出队:成功返回true,队列空返回false
bool npu_queue_dequeue(npu_task_queue_t *q, npu_task_t *out_task);
// 查询队列是否为空
bool npu_queue_is_empty(const npu_task_queue_t *q);
// 查询队列是否已满
bool npu_queue_is_full(const npu_task_queue_t *q);
#endif // NPU_TASK_QUEUE_H
// npu_task_queue.c
#include "npu_task_queue.h"
void npu_queue_init(npu_task_queue_t *q) {
q->head = 0;
q->tail = 0;
q->count = 0;
}
bool npu_queue_enqueue(npu_task_queue_t *q, const npu_task_t *task) {
if (npu_queue_is_full(q)) {
return false;
}
q->buffer[q->tail] = *task;
q->tail = (q->tail + 1) % NPU_QUEUE_CAPACITY;
q->count++;
return true;
}
bool npu_queue_dequeue(npu_task_queue_t *q, npu_task_t *out_task) {
if (npu_queue_is_empty(q)) {
return false;
}
*out_task = q->buffer[q->head];
q->head = (q->head + 1) % NPU_QUEUE_CAPACITY;
q->count--;
return true;
}
bool npu_queue_is_empty(const npu_task_queue_t *q) {
return q->count == 0;
}
bool npu_queue_is_full(const npu_task_queue_t *q) {
return q->count == NPU_QUEUE_CAPACITY;
}
// main.c — 最小验证程序
#include <stdio.h>
#include <assert.h>
#include "npu_task_queue.h"
int main(void) {
npu_task_queue_t q;
npu_queue_init(&q);
// 构造测试任务
npu_task_t t1 = {.task_id = 1, .priority = 10, .state = TASK_PENDING};
npu_task_t t2 = {.task_id = 2, .priority = 20, .state = TASK_PENDING};
// 入队两个任务
assert(npu_queue_enqueue(&q, &t1) == true);
assert(npu_queue_enqueue(&q, &t2) == true);
assert(npu_queue_is_empty(&q) == false);
// 出队并验证FIFO顺序
npu_task_t out;
assert(npu_queue_dequeue(&q, &out) == true);
assert(out.task_id == 1);
assert(npu_queue_dequeue(&q, &out) == true);
assert(out.task_id == 2);
// 队列空时出队应失败
assert(npu_queue_dequeue(&q, &out) == false);
assert(npu_queue_is_empty(&q) == true);
// 填满队列测试满状态
for (int i = 0; i < NPU_QUEUE_CAPACITY; i++) {
npu_task_t t = {.task_id = i, .priority = 1, .state = TASK_PENDING};
assert(npu_queue_enqueue(&q, &t) == true);
}
npu_task_t overflow;
assert(npu_queue_enqueue(&q, &overflow) == false); // 队列已满
assert(npu_queue_is_full(&q) == true);
printf("All NPU queue tests passed.\n");
return 0;
}
编译与运行
# 在RISC-V目标环境或本地主机交叉编译
riscv64-unknown-elf-gcc -Wall -Wextra -o npu_queue_test main.c npu_task_queue.c
# 若在x86主机上验证逻辑,可直接使用gcc
gcc -Wall -Wextra -o npu_queue_test main.c npu_task_queue.c
# 运行
./npu_queue_test
验证方法
程序通过assert断言自动验证以下行为,全部通过时输出All NPU queue tests passed.:
- 入队后队列非空,出队顺序符合FIFO(先入先出);
- 空队列出队返回
false,满队列入队返回false; - 环形缓冲区在填满
NPU_QUEUE_CAPACITY(16)个任务后正确报告满状态。
该实现使用uint8_t索引,容量固定为16,避免索引回绕时的类型溢出问题。任务描述符按值拷贝入队,调用方可在入队后复用或释放原任务变量。