簡體   English   中英

IPC瓶頸?

[英]IPC bottleneck?

我有兩個過程,生產者和消費者。 IPC通過Win32上的OpenFileMapping / MapViewOfFile完成。

制作人從另一個來源接收視頻,然后將其傳遞給消費者,並通過兩個事件完成同步。

對於生產者:

Receive frame
Copy to shared memory using CopyMemory
Trigger DataProduced event
Wait for DataConsumed event

對於消費者

Indefinitely wait for DataProducedEvent
Copy frame to own memory and send for processing
Signal DataConsumed event

沒有這些,視頻平均為5fps。 如果我在兩面都添加事件,但沒有CopyMemory,則它仍約為5fps,盡管速度稍慢。 當我添加CopyMemory操作時,它下降到2.5-2.8fps。 Memcpy甚至更慢。

我很難相信簡單的內存副本會導致這種速度下降。 有什么補救辦法嗎?

這是我創建共享內存的代碼:

HANDLE fileMap = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, fileMapSize, L"foomap");
void* mapView = MapViewOfFile(fileMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, fileMapSize);

尺寸為1024 * 1024 * 3

編輯-添加了實際代碼:

在生產者上:

void OnFrameReceived(...)
{
    // get buffer
    BYTE *buffer = 0;
...

    // copy data to shared memory
    CopyMemory(((BYTE*)mapView) + 1, buffer, length);

    // signal data event
SetEvent(dataProducedEvent);

    // wait for it to be signaled back!
    WaitForSingleObject(dataConsumedEvent, INFINITE);
}

對消費者:

while(WAIT_OBJECT_0 == WaitForSingleObject(dataProducedEvent, INFINITE))
    {   
        SetEvent(dataConsumedEvent);
    }

好吧,似乎從DirectShow緩沖區復制到共享內存畢竟是瓶頸。 我嘗試使用命名管道來傳輸數據,然后猜測-恢復性能。

有誰知道為什么會這樣嗎?

要添加一個我以前認為不相關的細節:注入了生產者,並掛鈎到DirectShow圖上以檢索框架。

復制內存涉及幕后的某些操作,對於視頻而言,這可能意義重大。

我會嘗試另一種方法:為每個幀或幾個幀創建一個共享塊。 因此,給它們命名,即block1,block2,block3等,以便接收者知道接下來要讀取的塊。 現在直接將幀接收到分配的blockX,通知使用者新塊的可用性,然后分配並立即開始使用另一個塊。 消費者映射該塊並且不復制它-該塊現在屬於消費者,並且消費者可以使用原始緩沖區進行進一步處理。 使用者關閉該塊的映射后,該映射將被銷毀。 這樣一來,您就會獲得大量的障礙,並避免阻塞。

如果幀處理花費的時間不多,而創建共享塊所需的時間很長,則可以創建一個共享塊池,該池足夠大,以確保生產者和使用者不會嘗試使用同一塊(您可以通過使用信號量或互斥體來保護每個塊)。

希望我的想法很明確-避免在生產者而不是消費者中使用塊復制

復制3MB內存所花費的時間根本不應該引起注意。 在我的舊筆記本電腦上進行的快速測試能夠在大約10秒內完成10,000個memcpy(buf1, buf2, 1024 * 1024 * 3)操作。 在1/1000秒的時間內,它不應將幀速率減慢很多。

無論如何,似乎可能會有一些優化可以加快速度。 當前,您似乎是處理數據的兩倍或三倍。 重復處理,因為您先“接收到框架”,然后“復制到共享內存”。 如果“將幀復制到自己的內存並發送以進行處理”,則意味着需要進行三重處理,這意味着您可以真正復制到本地緩沖區,然后進行處理,而不僅僅是從緩沖區中進行處理。

另一種選擇是將幀直接接收到共享緩沖區中,然后直接從緩沖區中處理它。 如我所懷疑的,如果您希望能夠在處理另一幀的同時接收一幀,則只需增加內存映射的大小以容納一個以上的幀並將其用作循環數組即可。 在消費者方面,它將看起來像這樣。

char *sharedMemory;
int frameNumber = 0;
...
WaitForSingleObject(...)  // Consume data produced event
frame = &sharedMemory[FRAME_SIZE * (frameNumber % FRAMES_IN_ARRAY_COUNT)
processFrame(frame);
ReleaseSemaphore(...)     // Generate data consumed event

和生產者

char *sharedMemory;
int frameNumber = 0;
...
WaitForSingleObject(...)  // Consume data consumed event
frame = &sharedMemory[FRAME_SIZE * (frameNumber % FRAMES_IN_ARRAY_COUNT)
recieveFrame(frame);
ReleaseSemaphore(...)     // Generate data produced event

只需確保將已消耗數據的信號量的信號量初始化為FRAME_IN_ARRAY_COUNT並將產生的數據量的信號量初始化為0。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM