关于RK3588cpu多线程速度慢的问题

项目场景

接到项目需求,需要将一个目标检测模型部署到RK3588上面执行,最对同时要跑4张图像,简单了解了下RK3588的CPU是有A76大核的,速度应该也挺快,于是尝试了CPU和NPU两种部署方式并进行耗时对比,然后问题就来了。

问题描述

cpu部署

我用的是paddle-lite框架部署到cpu上,选择了高负载模式,然后发现竟然是2线程速度最快,4线程速度反而会下降。同时,除了目标检测,还有一部分非深度学习的算法并行执行,在4线程的情况下这部分算法耗时竟然增加了2倍。总的来说,就是线程开多了速度反而慢了。

npu部署

这种部署方式我选择了FastDeploy,为了兼容模型做了一些后处理修改,后处理部分也有一些CPU计算,可能有一些耗时,但不多。这个方式就更离谱了,单线程跑模型也会使得非深度学习部分的算法耗时增加2倍。。有点无语。多线程跑的话模型耗时大大降低,但非深度学习部分算法耗时依然很高。

原因分析:

可能是系统的CPU调度方案,让某些线程跑在小核上面,导致速度变慢。


解决方案:

将线程和大核进行绑定。问了AI,然后写了一个检测大核和绑核的代码,将非深度学习部分线程和大核绑定,结果速度瞬间就上去了,问题部分解决,至于cpu部署4线程比2线程快这个问题不太好弄,暂时搁置了。下面是绑核代码:

#include <sched.h>
#include <vector>
#include <thread>
#include <fstream>
#include <sys/syscall.h>
#include <unistd.h>
// 动态检测高性能核心(大核)
std::vector<int> detect_big_cores() {
    std::vector<int> big_cores;
    const int MAX_CORE = 16; // 最大支持16核
    for(int i=0; i<MAX_CORE; ++i){
        std::string freq_path = "/sys/devices/system/cpu/cpu" 
                              + std::to_string(i) 
                              + "/cpufreq/cpuinfo_max_freq";
        std::ifstream ifs(freq_path);
        if(ifs.good()){
            int freq;
            ifs >> freq;
            if(freq > 2000000) // 2GHz以上判定为大核[1](@ref)
                big_cores.push_back(i);
        }
    }
    return big_cores.empty() ? std::vector<int>{0} : big_cores; // 默认核心0
}

std::vector<int> detect_small_cores()
{
     std::vector<int> big_cores;
    const int MAX_CORE = 16; // 最大支持16核
    for(int i=0; i<MAX_CORE; ++i){
        std::string freq_path = "/sys/devices/system/cpu/cpu" 
                              + std::to_string(i) 
                              + "/cpufreq/cpuinfo_max_freq";
        std::ifstream ifs(freq_path);
        if(ifs.good()){
            int freq;
            ifs >> freq;
            if(freq < 2000000) // 2GHz以上判定为大核[1](@ref)
                big_cores.push_back(i);
        }
    }
    return big_cores.empty() ? std::vector<int>{0} : big_cores; // 默认核心0
}

// 绑核核心函数
int bind_to_cores(const std::vector<int>& core_ids, bool is_process) {
    cpu_set_t mask;
    CPU_ZERO(&mask);
    for(auto id : core_ids) {
        CPU_SET(id, &mask);  // 构建CPU掩码[4,7](@ref)
    }

#if defined(__ANDROID__) || defined(__linux__)
    pid_t pid = is_process ? 0 : syscall(SYS_gettid); // 进程/线程区分处理[1](@ref)
    int ret = sched_setaffinity(pid, sizeof(mask), &mask);
#elif defined(_WIN32)
    // Windows实现略(需使用SetThreadAffinityMask)
#endif
    return ret == 0 ? 0 : -1;
}
#else
std::vector<int> detect_big_cores() 
{
    return std::vector<int> ();
}
int bind_to_cores(const std::vector<int>& core_ids, bool is_process)
{
    return -1;
}
#endif

//使用例子,在线程中执行以下代码,将线程和大核1绑定
auto cores = detect_big_cores();			
if(cores.size() > 0) bind_to_cores({cores[0]});	
### RK3588 芯片多线程编程与性能优化 RK3588 是一款高性能应用处理器,支持复杂的多线程操作和并行计算。为了充分利用其强的硬件资源,开发者可以通过合理的多线程编程策略来提升程序的整体性能。 #### 1. 多调度与亲和性设置 RK3588 提供了多个心用于并发执行任务。通过调整 CPU 的亲和性(CPU affinity),可以将特定的任务绑定到指定的心上运行,减少上下文切换带来的开销。例如,在 Linux 系统下,可以使用 `sched_setaffinity` 函数实现这一目标: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> void *thread_function(void *arg) { printf("Thread running on core %d\n", sched_getcpu()); return NULL; } int main() { pthread_t thread_id; cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(4, &cpuset); // 绑定到之一 (core 4) pthread_create(&thread_id, NULL, thread_function, NULL); pthread_setaffinity_np(thread_id, sizeof(cpu_set_t), &cpuset); pthread_join(thread_id, NULL); return 0; } ``` 此代码片段展示了如何将一个线程绑定到 RK3588 的某个具体心上运行[^3]。 #### 2. 并发控制机制 在多线程环境中,合理管理共享资源至关重要。RK3588 支持标准的 POSIX 线程库 (`pthreads`) 和锁机制(mutexes)。以下是创建互斥锁的一个例子: ```c pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void lock_resource() { pthread_mutex_lock(&mutex); // 访问共享资源 pthread_mutex_unlock(&mutex); } ``` 这种同步技术能够有效防止数据竞争问题,确保多线程间的协作更加安全可靠。 #### 3. 内存分配优化 对于高吞吐量场景,建议采用 NUMA-aware 编程模型以降低跨节点访问延迟。此外,还可以利用 jemalloc 或 tcmalloc 这样的高级内存分配器替代默认 malloc 实现更好的缓存利用率[^2]。 #### 4. I/O 子系统调优 当涉及量文件读写或者网络通信时,异步 I/O 可显著提高效率。下面是一个简单的 AIO 使用案例: ```c struct aiocb aio_read; memset(&aio_read, 0, sizeof(aio_read)); aio_read.aio_fildes = fd; /* 文件描述符 */ aio_read.aio_buf = buffer; /* 数据缓冲区 */ aio_read.aio_nbytes = size; /* 待传输字节数 */ if (aio_read(&aio_read) != 0) perror("Error initiating async read"); /* 后续可通过 lio_listio(), aio_suspend() 等函数监控完成状态 */ ``` 这种方法允许主线程继续处理其他事务而不必等待阻塞型 I/O 完成。 #### 5. 调度参数调节 针对实时性强的工作负载,适当增加进程优先级有助于获得更快响应时间;而对于批处理类作业,则应考虑降低它们的影响权重以便让更重要的服务得到更多关注。这通常涉及到 nice 值设定以及 cgroup 控制组规则定义等内容[^4]。 --- ### 总结 通过对 RK3588 的深入理解及其配套 SDK 工具链的支持,结合以上提到的各种技术和实践技巧,可以在该平台上构建出既稳定又高效的多线程应用程序解决方案[^1]^。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值