错误的Linux内存映射文件性能与随机访问C + +和Python

当尝试使用内存映射文件来创build一个多GB的文件(大约13GB)时,我遇到了mmap()似乎有问题的地方。 最初的实现是在Windows上使用boost :: iostreams :: mapped_file_sink在c ++上完成的,一切都很好。 然后代码在Linux上运行,在Windows上花费几分钟的时间在Linux上变成几小时。

这两台机器是相同硬件的克隆:戴尔R510 2.4GHz 8Mcaching16GB内存1TB磁盘PERC H200控制器。

Linux是使用3.8内核和g ++ 4.83的Oracle Enterprise Linux 6.5。

有一些担心,boost库可能存在问题,所以实现是使用boost :: interprocess :: file_mapping完成的,再次使用本地mmap()。 所有三个显示相同的行为。 当Linux性能下降严重时,Windows和Linux性能达到一定程度。

完整的源代码和性能数字链接如下。

// C++ code using boost::iostreams void IostreamsMapping(size_t rowCount) { std::string outputFileName = "IoStreamsMapping.out"; boost::iostreams::mapped_file_params params(outputFileName); params.new_file_size = static_cast<boost::iostreams::stream_offset>(sizeof(uint64_t) * rowCount); boost::iostreams::mapped_file_sink fileSink(params); // NOTE: using this form of the constructor will take care of creating and sizing the file. uint64_t* dest = reinterpret_cast<uint64_t*>(fileSink.data()); DoMapping(dest, rowCount); } void DoMapping(uint64_t* dest, size_t rowCount) { inputStream->seekg(0, std::ios::beg); uint32_t index, value; for (size_t i = 0; i<rowCount; ++i) { inputStream->read(reinterpret_cast<char*>(&index), static_cast<std::streamsize>(sizeof(uint32_t))); inputStream->read(reinterpret_cast<char*>(&value), static_cast<std::streamsize>(sizeof(uint32_t))); dest[index] = value; } } 

最后一个testing是在Python中完成的,以另一种语言重现这一点。 脱落发生在同一个地方,所以看起来像是同样的问题。

 # Python code using numpy import numpy as np fpr = np.memmap(inputFile, dtype='uint32', mode='r', shape=(count*2)) out = np.memmap(outputFile, dtype='uint64', mode='w+', shape=(count)) print("writing output") out[fpr[::2]]=fpr[::2] 

对于c ++testing,Windows和Linux的性能差不多,大约有3亿个int64s(Linux看起来速度稍快)。 在C ++和Python上,性能在3Gb(每个int64 = 3.2Gb为4亿* 8字节)的Linux上都下降了。

我知道在32位Linux上,3Gb是一个不可思议的边界,但我不知道64位Linux的类似行为。

结果的要点是1.4分钟的Windows在Linux上的1.7亿小时,在4亿的int64s上。 实际上我试图绘制接近13亿的int64s。

任何人都可以解释为什么Windows和Linux之间的性能脱节?

任何帮助或build议将不胜感激!

LoadTest.cpp

Makefile文件

LoadTest.vcxproj

更新了mmap_test.py

原始的mmap_test.py

更新后的结果使用更新后的Python代码… Python速度现在可以与C ++相媲美

原始结果注意:Python结果是陈旧的

Solutions Collecting From Web of "错误的Linux内存映射文件性能与随机访问C + +和Python"

编辑:升级到“正确答案”。 问题在于“处理脏页”的方式是由Linux处理的。 我仍然希望我的系统一次又一次刷新脏页,所以我不允许它有太多出色的页面。 但同时,我可以证明这是正在发生的事情。

我这样做(与“sudo -i”):

 # echo 80 > /proc/sys/vm/dirty_ratio # echo 60 > /proc/sys/vm/dirty_background_ratio 

这给了这些设置VM脏设置:

 grep ^ /proc/sys/vm/dirty* /proc/sys/vm/dirty_background_bytes:0 /proc/sys/vm/dirty_background_ratio:60 /proc/sys/vm/dirty_bytes:0 /proc/sys/vm/dirty_expire_centisecs:3000 /proc/sys/vm/dirty_ratio:80 /proc/sys/vm/dirty_writeback_centisecs:500 

这使得我的基准测试运行如下:

 $ ./a.out m64 200000000 Setup Duration 33.1042 seconds Linux: mmap64 size=1525 MB Mapping Duration 30.6785 seconds Overall Duration 91.7038 seconds 

与“之前”比较:

 $ ./a.out m64 200000000 Setup Duration 33.7436 seconds Linux: mmap64 size=1525 Mapping Duration 1467.49 seconds Overall Duration 1501.89 seconds 

有这些VM脏设置:

 grep ^ /proc/sys/vm/dirty* /proc/sys/vm/dirty_background_bytes:0 /proc/sys/vm/dirty_background_ratio:10 /proc/sys/vm/dirty_bytes:0 /proc/sys/vm/dirty_expire_centisecs:3000 /proc/sys/vm/dirty_ratio:20 /proc/sys/vm/dirty_writeback_centisecs:500 

我不确定我应该用什么设置来获得IDEAL的性能,同时不会让所有的脏页面永远留在内存中(这意味着如果系统崩溃,写入磁盘需要更长的时间)。

对于历史:这是我最初写为“无答案” – 这里的一些意见仍然适用…

不是一个真正的答案,但是我觉得这很有趣,如果我改变代码来首先读取整个数组,然后把它写出来,那么比在同一个循环中执行代码要快得多。 我明白,如果你需要处理真正庞大的数据集(比内存大),这是完全没有用的。 在发布原始代码的情况下,100M uint64值的时间是134s。 当我分裂阅读和写周期,它是43秒。

这是修改后的DoMapping函数[只是我改变的代码]:

 struct VI { uint32_t value; uint32_t index; }; void DoMapping(uint64_t* dest, size_t rowCount) { inputStream->seekg(0, std::ios::beg); std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now(); uint32_t index, value; std::vector<VI> data; for(size_t i = 0; i < rowCount; i++) { inputStream->read(reinterpret_cast<char*>(&index), static_cast<std::streamsize>(sizeof(uint32_t))); inputStream->read(reinterpret_cast<char*>(&value), static_cast<std::streamsize>(sizeof(uint32_t))); VI d = {index, value}; data.push_back(d); } for (size_t i = 0; i<rowCount; ++i) { value = data[i].value; index = data[i].index; dest[index] = value; } std::chrono::duration<double> mappingTime = std::chrono::system_clock::now() - startTime; std::cout << "Mapping Duration " << mappingTime.count() << " seconds" << std::endl; inputStream.reset(); } 

我目前正在运行一个200M记录的测试,在我的机器上需要大量的时间(2000+秒没有代码变化)。 很明显,所花费的时间来自于磁盘I / O,而且我看到IO-50-70MB / s的速率,这是相当不错的,因为我真的不希望我的相当不成熟的设置提供很多比那更多的。 改进不如规模较大,但仍然有一个不错的改进:总共1502s,而“同一回路中读写”则为2021s。

另外,我想指出的是,对于任何系统来说,这都是一个相当可怕的测试 – 事实上,Linux比Windows更糟糕 – 你并不是真的想映射一个大文件并写8个字节必须随机读入4KB页面到每个页面。 如果这反映了您的REAL应用程序,那么您应该认真地以某种方式重新考虑您的方法。 当你有足够的可用内存,整个内存映射区域适合内存时,它会运行正常。

我的系统中有很多内存,所以我认为问题是Linux不喜欢太多的“脏”的映射页面。

我有一种感觉,这可能与它有关: https : //serverfault.com/questions/126413/limit-linux-background-flush-dirty-pages更多解释: http : //www.westnet.com/ 〜gsmith /内容/ Linux的pdflush.htm

不幸的是,我也很累,需要睡觉。 我会看看我明天能不能试验这些,但不要屏住呼吸。 就像我说的,这不是一个真正的答案,而是一个很长的评论,它并不真正适合评论(并且包含代码,在评论中是完全垃圾的)