我目前在一个软件中使用共享内存和两个映射文件(第一个 1.9 GB,第二个 600 MB)。
我正在使用从第一个文件读取数据、处理数据并将结果写入第二个文件的过程。
我注意到在使用 memcpy 函数读取或写入映射 View 时,有时会出现严重的延迟(原因不明)。
映射文件是这样创建的:
m_hFile = ::CreateFileW(SensorFileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
m_hMappedFile = CreateFileMapping(m_hFile,
NULL,
PAGE_READWRITE,
dwFileMapSizeHigh,
dwFileMapSizeLow,
NULL);
内存映射是这样完成的:
m_lpMapView = MapViewOfFile(m_hMappedFile,
FILE_MAP_ALL_ACCESS,
dwOffsetHigh,
dwOffsetLow,
m_i64ViewSize);
dwOffsetHigh/dwOffsetLow 是来自系统信息的“匹配”粒度。
该进程正在读取大约 300KB * N 次,将其存储在缓冲区中,进行处理,然后将前一个缓冲区的已处理内容的 300KB * N 次写入第二个文件。
我有两个不同的内存 View (使用 MapViewOfFile 函数创建/移动),默认大小为 10 MBytes。
对于内存 View 大小,我测试了 10kBytes、100kB、1MB、10MB 和 100MB。
统计上没有区别,80% 的读取过程如下所述(~200 毫秒),但写入过程真的很慢。
通常:
1/读取在约 200 毫秒内完成。
2/过程在 2.9 秒内完成。
3/写入在约 200 毫秒内完成。
我可以看到 80% 的时间,读取或写入(在最坏的情况下两者都很慢)将花费 2 到 10 秒。
例子:为了写作,我使用下面的代码
for (unsigned int i = 0 ; i < N ; i++) // N = 500~3k
{
// Check the position of the memory view for ponderation
if (###)
MoveView(iOffset);
if (m_lpMapView)
{
memcpy((BYTE*)m_lpMapView + iOffset, pANNHeader, uiANNStatus);
// uiSize = ~300 kBytes
memcpy((BYTE*)m_lpMapView + iTemp, pLine[i], uiSize);
}
else
return uiANNStatus;
}
在使用 GetTickCount 函数查明延迟时间后,我发现第二次 memcpy 调用总是占用大部分时间。
所以,到目前为止,我看到 N(为了测试,我使用了 N = 500)次调用 memcpy 在使用这些共享内存时花费了 10 秒。
我制作了一个临时软件,它执行相同数量的 memcpy 调用、相同数量的数据,但看不到问题。
为了测试,我使用了以下条件,它们都显示相同的延迟:
1/我可以在从 Windows 7 到 Windows 10 的 32 位或 64 位计算机上看到这个。
2/使用主线程或多线程(最多 8 个线程用于同步目的的临界区)进行读/写。
3/SATA 或 SSD 上的操作系统,软件的内存映射文件物理存储在 SATA 或 SSD 硬盘上,如果在外部硬盘上,则通过 USB1、USB2 或 USB3 进行测试。
请问您认为我的错误是什么让 memcpy 运行缓慢。
最好的问候。
最佳答案
我找到了适合我的解决方案,但可能不适用于其他人。
根据 Thomas Matthews 的评论,我查看了 MSDN,发现了两个有趣的函数 FlushViewOfFile 和 FlushFileBuffers(但找不到任何关于锁定内存的有趣信息)。
在映射文件的 for 循环强制更新之后调用两者。
我没有更多的“随机”延迟,但不是预期的 200 毫秒,而是平均 400 毫秒,这对我的应用程序来说已经足够了。
在做了一些测试后,我发现过于频繁地调用它们会导致大量的硬盘访问,并且会使延迟更糟(每个 for 循环 10 秒),因此应谨慎使用刷新。
谢谢。
关于c++ - Windows共享内存访问时间慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35702593/