我正在使用 Buildroot 为 Raspberry PI3 构建自己的嵌入式 Linux 操作系统。该操作系统将用于处理多个应用程序,其中之一基于 OpenCV (v3.3.0) 执行对象检测。
我从 Raspbian Jessy + Python 开始,但结果证明执行一个简单的示例需要很多时间,所以我决定设计我自己的 RTOS 优化特性 + C++ 开发而不是 Python。
我认为通过这些优化,RPI 的 4 个内核 + 1GB RAM 将处理此类应用程序。问题是即使有了这些东西,最简单的计算机视觉程序也需要很多时间。
PC 与 Raspberry PI3 比较
这是我编写的一个简单程序,用于了解程序每个部分的执行时间的数量级。
#include <stdio.h>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <time.h> /* clock_t, clock, CLOCKS_PER_SEC */
using namespace cv;
using namespace std;
int main()
{
setUseOptimized(true);
clock_t t_access, t_proc, t_save, t_total;
// Access time.
t_access = clock();
Mat img0 = imread("img0.jpg", IMREAD_COLOR);// takes ~90ms
t_access = clock() - t_access;
// Processing time
t_proc = clock();
cvtColor(img0, img0, CV_BGR2GRAY);
blur(img0, img0, Size(9,9));// takes ~18ms
t_proc = clock() - t_proc;
// Saving time
t_save = clock();
imwrite("img1.jpg", img0);
t_save = clock() - t_save;
t_total = t_access + t_proc + t_save;
//printf("CLOCKS_PER_SEC = %d\n\n", CLOCKS_PER_SEC);
printf("(TEST 0) Total execution time\t %d cycles \t= %f ms!\n", t_total,((float)t_total)*1000./CLOCKS_PER_SEC);
printf("---->> Accessing in\t %d cycles \t= %f ms.\n", t_access,((float)t_access)*1000./CLOCKS_PER_SEC);
printf("---->> Processing in\t %d cycles \t= %f ms.\n", t_proc,((float)t_proc)*1000./CLOCKS_PER_SEC);
printf("---->> Saving in\t %d cycles \t= %f ms.\n", t_save,((float)t_save)*1000./CLOCKS_PER_SEC);
return 0;
}
Raspberry PI 上的执行结果(从 Buildroot 生成的操作系统)
如您所见,存在巨大差异。我需要的是优化每一个细节,以便这个示例处理步骤以“接近”实时的方式发生,最长为 15 毫秒。
我的问题是:
- 如何优化我的操作系统以使其能够处理密集计算应用程序以及如何控制每个部分的优先级?
- 如何充分利用 RPI3 的 4 核来满足要求?
- 除了 OpenCV,还有其他可能性吗?
- 我应该使用 C 而不是 C++ 吗?
- 您推荐的任何硬件改进?
为了:
对于一般优化,除了常规的东西(例如确保只有在后台运行实际需要的东西)之外,您在操作系统方面无能为力。在最初的 Pi 上,您可以通过 'ing 一个名为 'cofi' 的库来加速
memmove()
和类似的功能,LD_PRELOAD
该库提供这些功能的汇编优化版本,但我不确定它是否对 Pi 3 有帮助。对于优先级,这确实是要查看手册页的内容,但是除非您将事情并行化,否则您通常无法做到这一点(在您的情况下,似乎显而易见的解决方案是运行每个步骤,因为它赢得了进程并使用 IPC(可能出于性能原因共享内存)在它们之间移动数据)。
在您从测试程序中引用的结果的注释中,请特别注意 Pi 上的处理和保存步骤都慢了大约 10 倍,而访问步骤仅慢了大约 5 倍,并且这些数字与粗略估计将 Pi 3 与不到一年的通用 PC 进行比较时的预期。Pi 中的 CPU 几乎可以肯定比你运行 PC 测试的 CPU 慢得多(如果你根本没有并行化事情,那么差距会更大,因为大多数现代 x86 CPU 可以单独运行一个内核满负载比他们在满负载下运行所有内核的速度要快得多),这将产生影响。ARM ISA 也与 x86 ISA 显着不同(与 x86 相比,ARM 倾向于每个周期执行更少的操作,但
我也不知道您使用的是什么相机,但我希望您可以通过降低正在处理的图像的分辨率来获得更好的时间,如果您避免使用,您可能可以减少采集时间压缩格式(并且不使用有损压缩意味着分辨率不会那么重要)。
在您自己的代码中进行并行化。您只需要确保在您的内核中启用了 SMP(如果您使用的是 RPi Foundation 的官方配置,则应该启用),然后尝试并行运行。我不确定 OpenCV 在并行化事物本身方面做了多少,但您可能也想看看 OpenMP(它提供了一种相当简单的方法来并行化不相互依赖的循环中的迭代)。
可能有,但每个人都对 OpenCV 进行了标准化,所以我建议使用它(因为每个人都使用它,所以您将更容易获得实施事物的技术帮助)。
这取决于你如何使用东西。虽然用 C++ 编写慢代码比 C 容易得多,但用这两种语言编写快速代码并不难。两种语言中的许多优化技术都非常相似(例如,在启动时预先分配所有内容,这样您就不会
malloc()
在临界区调用,或者避免调用stat()
)。但是,特别是在 C++ 的情况下,避免std::string
像瘟疫一样,它到处调用malloc()
,因此速度非常慢(我已经看到从std::string
C 风格的字符串转换在某些情况下将性能提高了 40% 以上)。假设您试图保持较低的硬件成本并且空间受限(因此选择了 Raspberry Pi),我真的想不出任何东西。Pi(在它的所有迭代中)使用的 SoC 非常适合该价格范围内的计算机视觉工作。如果您愿意使用更大一些且价格更高的东西,我可能会建议使用 NVIDIA Jetson 板(他们使用 Tegra SoC,它具有集成了 192 个 CUDA 内核的 Quadro 等效 GPU,因此它可能可以运行您的处理工作量更快),但是让 Buildroot 在那里工作比在 Pi 上工作要复杂得多。
针对评论进行编辑:
进程级别的并行化与多线程不同,它完全不同(最大的区别在于资源如何共享,默认情况下线程共享所有内容,进程不共享任何内容)。通常,当涉及大量处理时,您(通常)最好使用基于进程的并行化,因为编写高效代码更容易,而不必担心线程安全。
就选项而言,您提到的两者可能会对系统性能产生重大影响,但它们最终都是吞吐量和延迟之间的权衡。抢占模型控制如何重新安排在内核模式下运行的事物(如系统调用)。有以下三个选项:
相比之下,定时器频率更容易解释。如果有其他东西等待运行,它控制某物可以不间断运行的最长时间。较高的值会导致较短的时间段(较低的延迟和较低的吞吐量),较低的值会导致较长的时间段(较高的延迟和较高的吞吐量)。作为一般开始,我建议将抢占模型设置为自愿,并将计时器频率设置为 300 Hz,然后开始尝试首先更改计时器频率(因为这通常会产生更明显的影响)。
至于 Movidius NCS,它是否值得取决于你需要处理多少数据,因为它会受到 USB 连接的带宽限制(Pi 只有一个 USB 2.0 控制器,所以你不仅限于不到 Movidius 设计带宽的十分之一,您还必须至少与以太网适配器共享总线,这将损害您的延迟和吞吐量)。如果您只以低速率处理 32 位颜色的 1920x1080 单帧,那么它可能是可行的,但如果您需要以全帧速率对同一视频进行流处理,那么您可能会遇到延迟问题。如果您确实选择使用一个,请确保您获得了一个有源集线器(否则您可能会遇到问题,因为它试图汲取比 Pi 所能提供的更多的功率)。