RISC-V Rocket核多线程加速器并发执行时间测量问题
问题定位与解决步骤
一、线程串行启动的核心原因排查
- 核间线程调度逻辑缺陷:如果是单核依次创建所有线程,会导致线程串行初始化,甚至全部绑定到同一个核,完全没用到多核并行能力。
- 全局同步逻辑冗余:线程启动后若等待同一个全局信号触发加速器,会导致所有加速器无法同时启动,变成串行执行。
- 核内加速器资源竞争:同一核上的4个加速器若共享总线、寄存器等资源,会导致核内加速器也无法并发运行。
二、多线程并发启动的实现方案
1. 多核线程绑定与并行触发
要让16个线程均匀分配到4个核(每个核4个线程),且所有线程几乎同时启动任务:
- 用RISC-V的
mhartidCSR读取当前核编号,每个核仅负责启动属于自己的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)读取
cycleCSR,存入共享内存全局变量。 - 全局结束时间:每个线程完成任务后,读取
cycleCSR,通过原子操作更新全局最大结束时间(保证多线程下的正确性)。
// 全局计时变量(共享内存) 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
相关产品推荐
相关产品推荐

