You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RISC-V Rocket核多线程加速器并发执行时间测量问题

问题定位与解决步骤

一、线程串行启动的核心原因排查

  • 核间线程调度逻辑缺陷:如果是单核依次创建所有线程,会导致线程串行初始化,甚至全部绑定到同一个核,完全没用到多核并行能力。
  • 全局同步逻辑冗余:线程启动后若等待同一个全局信号触发加速器,会导致所有加速器无法同时启动,变成串行执行。
  • 核内加速器资源竞争:同一核上的4个加速器若共享总线、寄存器等资源,会导致核内加速器也无法并发运行。

二、多线程并发启动的实现方案

1. 多核线程绑定与并行触发

要让16个线程均匀分配到4个核(每个核4个线程),且所有线程几乎同时启动任务:

  • 用RISC-V的mhartid CSR读取当前核编号,每个核仅负责启动属于自己的4个线程,并绑定到本地核。
  • 设计共享内存原子同步屏障:所有核完成线程创建后,通过原子操作计数,当计数达到4(所有核就绪),所有核同时触发本地线程的加速器启动指令。
// 全局同步变量(需放在共享内存区域)
volatile uint32_t ready_count = 0;
const uint32_t total_harts = 4;

void hart_main() {
    uint32_t hartid = read_csr(mhartid);
    // 启动当前核负责的4个线程,绑定到本地hart
    for (int i = hartid * 4; i < (hartid + 1) * 4; i++) {
        bind_thread_to_hart(accelerator_task, i, hartid);
    }
    // 原子操作增加就绪计数
    __sync_fetch_and_add(&ready_count, 1);
    // 等待所有核完成线程初始化
    while (ready_count < total_harts);
    // 触发当前核内所有线程启动加速器任务
    wake_all_local_threads();
}

2. 加速器无锁独立控制

每个加速器对应独立的控制寄存器空间,线程直接操作专属寄存器,避免全局锁或同步等待:

#define ACCEL_BASE 0x10000000
#define ACCEL_STRIDE 0x100

void accelerator_task(int accel_id) {
    volatile uint32_t* accel_ctrl = (uint32_t*)(ACCEL_BASE + accel_id * ACCEL_STRIDE);
    // 写入任务参数
    accel_ctrl[0] = TASK_PARAM;
    // 直接启动加速器(无锁写启动位)
    accel_ctrl[1] |= (1 << 0);
    // 等待加速器完成
    while (!(accel_ctrl[2] & (1 << 1)));
}

三、CSR计时的准确实现

要精准测量首个线程启动任务到最后线程完成的时间,需规避调度和同步逻辑的干扰:

1. 全局计时点的线程安全设计

  • 全局起始时间:所有核就绪后,由主核(hartid=0)读取cycle CSR,存入共享内存全局变量。
  • 全局结束时间:每个线程完成任务后,读取cycle CSR,通过原子操作更新全局最大结束时间(保证多线程下的正确性)。
// 全局计时变量(共享内存)
volatile uint64_t global_start_cycle = 0;
volatile uint64_t global_end_cycle = 0;

// 主核入口逻辑
void main() {
    // 初始化共享内存变量
    ready_count = 0;
    global_start_cycle = 0;
    global_end_cycle = 0;
    // 启动所有核的hart_main
    start_all_harts();
    // 等待16个线程全部完成
    while (get_completed_thread_num() < 16);
    // 计算总耗时
    uint64_t total_cycles = global_end_cycle - global_start_cycle;
    printf("Total execution cycles: %llu\n", total_cycles);
}

// 线程任务中的计时逻辑
void accelerator_task(int accel_id) {
    // ... 加速器启动与等待完成 ...
    uint64_t end_cycle = read_csr(cycle);
    // 原子更新全局最大结束时间
    uint64_t old_end;
    do {
        old_end = global_end_cycle;
    } while (__sync_val_compare_and_swap(&global_end_cycle, old_end, old_end > end_cycle ? old_end : end_cycle) != old_end);
}

// 核内同步后的起始计时
void hart_main() {
    // ... 线程创建、同步屏障 ...
    uint32_t hartid = read_csr(mhartid);
    if (hartid == 0) {
        global_start_cycle = read_csr(cycle);
    }
    wake_all_local_threads();
}

2. 规避核间CSR计数差异

RISC-V的cycle CSR为核独立计数,需确保硬件层面所有核时钟同步;若硬件支持,可改用time CSR(全局实时时钟)替代,避免核间周期计数不一致的问题。

四、并发有效性验证

在线程任务中打印每个加速器的启动、结束周期,直观确认是否存在执行重叠:

void accelerator_task(int accel_id) {
    uint64_t start_cycle = read_csr(cycle);
    // ... 加速器启动与等待 ...
    uint64_t end_cycle = read_csr(cycle);
    printf("Accel %d: start=%llu, end=%llu\n", accel_id, start_cycle, end_cycle);
}

若输出中多个加速器的start时间接近,且单个加速器的耗时远小于总耗时,说明并发执行成功。

五、硬件层面排查方向

  • 确认加速器硬件支持并发:检查每个加速器是否有独立执行单元,是否因共享核内DMA、总线导致串行阻塞。
  • 验证多核SMP功能:确认RISC-V核已启用对称多处理,线程调度器可将线程分配到不同核执行。

内容的提问来源于stack exchange,提问作者student_11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:52:45