IPC瓶颈?

Posted

技术标签:

【中文标题】IPC瓶颈?【英文标题】:IPC bottleneck? 【发布时间】:2010-08-24 13:32:27 【问题描述】:

我有两个进程,一个生产者和一个消费者。 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 图以检索帧。

【问题讨论】:

无法调试伪代码。发布您的事件信号/等待代码。 【参考方案1】:

内存复制涉及到某些幕后操作,对于视频来说,这可能很重要。

我会尝试另一条路线:为每个帧或多个帧创建一个共享块。因此命名它们,即块 1、块 2、块 3 等,以便接收者知道接下来要读取什么块。现在直接将帧接收到分配的blockX,通知消费者新块的可用性并立即分配并开始使用另一个块。消费者映射块并且不复制它 - 块现在属于消费者,消费者可以在进一步处理中使用原始缓冲区。一旦消费者关闭块的映射,这个映射就会被销毁。所以你得到一个块流并避免阻塞。

如果帧处理不需要太多时间而创建共享块则需要很多时间,您可以创建一个共享块池,该池足够大以确保生产者和消费者永远不会尝试使用同一个块(您可以通过使用使方案复杂化一个信号量或互斥量来保护每个块)。

希望我的想法很明确 - 避免在生产者中使用块而不是在消费者中进行复制

【讨论】:

你所说的“内存复制涉及某些幕后操作”是什么意思? 我真的没有细节要告诉你,但我的观察表明,额外复制内存块会显着减慢许多操作,尤其是在主要是数字运算或涉及网络的进程中。称之为经验知识。 好吧,目前我没有对框架进行任何处理,而我正试图弄清楚如何解决这个问题 - 你仍然认为它会有帮助吗?我的意思是,复制操作将保留在那里 - 我猜我不必等待数据消耗事件? 不,应该完全不涉及数据复制操作。您将在生产者中收集共享内存块的数据,然后向消费者发出信号并释放该块。消费者将获取块的地址并将其传递给进一步处理而不复制。 就像我在另一个答案的评论中所说的那样,由于缓冲区是由 DirectShow 在内部处理的,因此我无法在没有任何大量工作的情况下将数据直接收集到共享内存中。但是,我按照您的建议使用块而不是单个缓冲区。奇怪的是,最终只使用了一个块,但性能仍然不足。我开始认为瓶颈实际上在于 CopyMemory 本身 - 在我注入自己的进程中触发时,有什么理由会变慢吗?【参考方案2】:

复制 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即可。

【讨论】:

是的,我确实也想过——第一部分的问题是原始图像缓冲区是由 DirectShow 管理的,我还不想深入研究它的内存管理 API。处理是在事件发出信号以在可用时发送更多数据之后完成的,因此它不应该像那样干扰。有意义吗? 是和不是。您仍然可以通过直接从共享缓冲区处理它来省略最后一个副本。

以上是关于IPC瓶颈?的主要内容,如果未能解决你的问题,请参考以下文章

发现瓶颈的顺序及可能存在的瓶颈

怎样查出SQLServer的性能瓶颈

怎样查出SQLServer的性能瓶颈

怎样查出SQLServer的性能瓶颈

怎样查出SQLServer的性能瓶颈

如何突破业务增长的瓶颈