IPC瓶颈?

时间:2010-08-24 13:32:28

标签: c windows winapi ipc shared-memory

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

2 个答案:

答案 0 :(得分:1)

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

我会尝试另一条路线:为每个帧或几个帧创建一个共享块。因此命名它们,即block1,block2,block3等,以便接收者知道下一个要读取的块。现在直接将帧接收到已分配的blockX,通知消费者新块的可用性,并立即分配并开始使用另一个块。消费者映射块并且不复制它 - 块现在属于消费者,消费者可以使用原始缓冲区进行进一步处理。一旦消费者关闭了块的映射,就会破坏该映射。所以你得到一个块流并避免阻塞。

如果帧处理没有花费太多时间并且创建共享块,则可以创建一个共享块池,其大小足以确保生产者和消费者永远不会尝试使用相同的块(您可以通过使用复杂的方案)一个信号量或mutx来保护每个块)。

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

答案 1 :(得分:0)

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