基於OpenCV的程序優化嵌入式Linux作業系統
我正在使用 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 傾向於每個週期執行更少的操作,但
我也不知道您使用的是什麼相機,但我希望您可以通過降低正在處理的圖像的解析度來獲得更好的時間,如果您避免使用,您可能可以減少採集時間壓縮格式(並且不使用有損壓縮意味著解析度不會那麼重要)。
如何充分利用 RPI3 的 4 核來滿足要求?
在您自己的程式碼中進行並行化。您只需要確保在您的核心中啟用了 SMP(如果您使用的是 RPi Foundation 的官方配置,則應該啟用),然後嘗試並行執行。我不確定 OpenCV 在並行化事物本身方面做了多少,但您可能也想看看 OpenMP(它提供了一種相當簡單的方法來並行化不相互依賴的循環中的迭代)。
除了 OpenCV,還有其他可能性嗎?
可能有,但每個人都對 OpenCV 進行了標準化,所以我建議使用它(因為每個人都使用它,所以您將更容易獲得實施事物的技術幫助)。
我應該使用 C 而不是 C++ 嗎?
這取決於你如何使用東西。雖然用 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 上工作要復雜得多。
針對評論進行編輯:
程序級別的並行化與多執行緒不同,它完全不同(最大的區別在於資源如何共享,預設情況下執行緒共享所有內容,程序不共享任何內容)。通常,當涉及大量處理時,您(通常)最好使用基於程序的並行化,因為編寫高效程式碼更容易,而不必擔心執行緒安全。
就選項而言,您提到的兩者可能會對系統性能產生重大影響,但它們最終都是吞吐量和延遲之間的權衡。搶占模型控制如何重新安排在核心模式下執行的事物(如係統呼叫)。有以下三個選項:
- 無搶占:這幾乎意味著在核心模式下執行的任何東西都不能被中斷。它與 SVR4 和 4.4BSD 的行為方式以及大多數其他舊 UNIX 系統的工作方式相匹配。它對吞吐量非常有利,但對延遲非常不利,因此它通常僅用於具有大量 CPU 的大型伺服器(更多 CPU 意味著它更有可能執行可以被搶占的東西)。
- 自願搶占:這讓核心中的每個函式都可以定義可以重新調度的位置。這是大多數面向桌面的 Linux 發行版使用的設置,因為它在吞吐量和延遲之間提供了良好的平衡。
- 完全搶占:這意味著(幾乎)核心中的任何程式碼都可以在(幾乎)任何時候被中斷。這對於在輸入和外部事件方面需要非常低延遲的系統很有用,例如用於實時多媒體工作的系統。吞吐量絕對是可怕的,但你無法擊敗延遲。
相比之下,定時器頻率更容易解釋。如果有其他東西等待執行,它控制某物可以不間斷執行的最長時間。較高的值會導致較短的時間段(較低的延遲和較低的吞吐量),較低的值會導致較長的時間段(較高的延遲和較高的吞吐量)。作為一般開始,我建議將搶占模型設置為自願,並將計時器頻率設置為 300 Hz,然後開始嘗試首先更改計時器頻率(因為這通常會產生更明顯的影響)。
至於 Movidius NCS,它是否值得取決於你需要處理多少數據,因為它會受到 USB 連接的頻寬限制(Pi 只有一個 USB 2.0 控制器,所以你不僅限於不到 Movidius 設計頻寬的十分之一,您還必須至少與乙太網適配器共享匯流排,這將損害您的延遲和吞吐量)。如果您只以低速率處理 32 位顏色的 1920x1080 單幀,那麼它可能是可行的,但如果您需要以全幀速率對同一影片進行流處理,那麼您可能會遇到延遲問題。如果您確實選擇使用一個,請確保您獲得了一個有源集線器(否則您可能會遇到問題,因為它試圖汲取比 Pi 所能提供的更多的功率)。