随机访问C+;时Linux内存映射文件性能不佳+&;python

随机访问C+;时Linux内存映射文件性能不佳+&;python,python,c++,linux,mmap,Python,C++,Linux,Mmap,在尝试使用内存映射文件创建一个多GB的文件(大约13gb)时,我遇到了mmap()的问题。最初的实现是在C++中使用Boosi::IoSoo::MaPdDFFieleSink进行的,所有的都很好。代码随后在Linux上运行,在Windows上运行几分钟,在Linux上运行几小时 这两台机器是相同硬件的克隆:Dell R510 2.4GHz 8M高速缓存16GB Ram 1TB Disk PERC H200控制器 Linux是使用3.8内核和g++4.83的Oracle Enterprise Li

在尝试使用内存映射文件创建一个多GB的文件(大约13gb)时,我遇到了mmap()的问题。最初的实现是在C++中使用Boosi::IoSoo::MaPdDFFieleSink进行的,所有的都很好。代码随后在Linux上运行,在Windows上运行几分钟,在Linux上运行几小时

这两台机器是相同硬件的克隆:Dell R510 2.4GHz 8M高速缓存16GB Ram 1TB Disk PERC H200控制器

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

有人担心boost库可能有问题,因此使用boost::interprocess::file_映射和本机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;
   }
}
对于C++测试,Windows和Linux的性能类似于3亿左右的INT64(Linux看起来稍快)。看来,在Linux上性能下降,在3GB(4亿×8字节每INT64=3.2GB)的C++和Python。 我知道在32位Linux上3Gb是一个神奇的边界,但我不知道64位Linux有类似的行为

结果的要点是,Windows在Linux上以4亿Int64的速度变为1.7小时只需1.4分钟。实际上,我正试图绘制接近13亿Int64的地图

有人能解释一下为什么Windows和Linux之间的性能会如此脱节吗

任何帮助或建议都将不胜感激

有了更新的Python代码…Python速度现在可以与C媲美++

注意:Python结果过时了

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

我这样做了(用“sudo-I”):

这将为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
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
这使我的基准测试运行如下:

$ ./a.out m64 200000000
Setup Duration 33.1042 seconds
Linux: mmap64
size=1525 MB
Mapping Duration 30.6785 seconds
Overall Duration 91.7038 seconds
与“之前”相比:

哪些具有以下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
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
我不确定应该使用什么设置来获得理想的性能,同时仍然不会将所有脏页永远留在内存中(这意味着如果系统崩溃,写入磁盘的时间会更长)

历史:这是我最初写的“非答案”-这里的一些评论仍然适用

这并不是一个真正的答案,但我发现很有趣的一点是,如果我将代码更改为首先读取整个数组,然后将其写出,那么速度要比在同一个循环中同时执行这两个操作快得多。我明白,如果您需要处理非常庞大的数据集(比内存大),这是完全无用的。按照发布的原始代码,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();
}
struct VI
{
uint32_t值;
uint32_t指数;
};
无效域映射(uint64\u t*dest,大小\u t行计数)
{
inputStream->seekg(0,std::ios::beg);
std::chrono::system_clock::time_point startTime=std::chrono::system_clock::now();
uint32_t指数,数值;
std::矢量数据;
对于(大小i=0;iread(重新解释强制转换和索引)、静态强制转换(sizeof(uint32\u t));
inputStream->read(重新解释转换(&value),static转换(sizeof(uint32\u t));
VI d={index,value};
数据。推回(d);
}

对于(size_t i=0;i您的Linux机器中有多少内存?@matstpeterson 16GB ram您可以尝试使用madvise()吗看看它是否改变了什么?您可能需要尝试各种建议参数:我建议检查64位Linux上的行为并查看结果。仅供参考,在numpy数组中,dtype
uint32
的整数正好占用4个字节。一个新的Python整数(无限精度,超过4个字节)每次使用标量索引访问数组时都会创建一个数组(装箱/取消装箱很昂贵,这就是为什么应该使用向量操作:
out[fpr[::2]]=fpr[::2]
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();
}