避免在RAM和SWAP内存之间切换的可选实现

c++ Alternative implementation to avoid shifting between RAM and SWAP memory

本文关键字:实现 之间 内存 RAM SWAP      更新时间:2023-10-16

我有一个程序,使用动态规划来计算一些信息。问题是,理论上使用的内存呈指数级增长。我使用的一些过滤器限制了这个空间,但是对于大的输入,它们也不能避免我的程序耗尽RAM -内存

程序在4个线程上运行。当我用一个非常大的输入运行它时,我注意到,在某些时候,程序开始使用交换内存,因为我的RAM不够大。这样做的结果是,我的 cpu使用率从大约380%下降到15%或更低

只有一个变量使用内存,它是以下数据结构:

使用CLN库编辑(添加类型):

class My_Map {
    typedef std::pair<double,short> key;
    typedef cln::cl_I value;
public: 
    tbb::concurrent_hash_map<key,value>* map;
    My_Map()  { map = new tbb::concurrent_hash_map<myType>(); }
    ~My_Map() { delete map; }
    //some functions for operations on the map
};

在我的主程序中,我使用这个数据结构作为全局变量:

My_Map* container = new My_Map();

问题:

是否有一种方法来避免SWAP和RAM之间的内存转移?我以为把所有的内存都推到Heap会有帮助,但似乎没有。所以我不知道是否有可能充分利用交换内存或其他东西。仅仅是这种记忆的转移就花费了很多时间。

如果你有1g的RAM,而你的程序使用了2gb的RAM,那么你将不得不找到其他地方存储多余的数据。很明显。默认的操作系统方式是交换,但另一种选择是通过使用内存映射文件来管理您自己的"交换"。

您打开一个文件并在其中分配一个虚拟内存块,然后将文件的页面放入RAM中进行处理。操作系统在很大程度上为你管理这些,但是你应该考虑你的内存使用,所以如果可以的话,不要试图访问相同的块。

在Windows上使用CreateFileMapping(),在Linux上使用mmap(),在Mac上使用mmap()。

操作系统工作正常-它在交换时不会区分堆栈和堆-它会将您似乎没有使用的内容分页并加载您请求的内容。

有几件事你可以试试:

  • 考虑myType是否可以更小-例如使用int8_t或甚至宽度适当的位域代替int,使用指针池字符串代替最坏情况长度字符数组,使用偏移量到数组中,它们比指针小等。如果你给我们看看型号,也许我们可以提出建议。

    • 考虑您的分页—如果您在一个内存页上有许多对象(可能是4k),如果其中任何一个对象正在使用,它们将需要留在内存中,因此尝试将大约在同一时间使用的对象放在同一内存页上—这可能涉及到相关myType对象的小数组的散列,或者甚至将所有数据移动到一个打包数组中(如果可能的话,二进制搜索可以非常快)。天真地使用哈希表往往会消耗内存,因为相似的对象被放在完全不相关的桶中。

    • 序列化/反序列化与压缩是一种可能性:而不是让操作系统交换出完整的myType内存,您可以主动将它们序列化成更紧凑的形式,然后只在需要时才反序列化

  • 考虑是否需要同时处理所有数据…如果你能批量处理工作,用更少的内存完成所有的"a组",那么你就可以转移到"B组"


更新现在你已经发布了你的实际数据类型…

遗憾的是,使用short可能没有多大帮助,因为sizeof key无论如何都需要16来对齐double;如果不需要精度,可以考虑float ?另一种选择是创建一个单独的映射数组…

tbb::concurrent_hash_map<double,value> map[65536];

你可以索引到map[my_short][my_double]。它可能更好或更差,但很容易尝试,所以您不妨对....

进行基准测试

对于cl_I, 2分钟的挖掘表明数据存储在联合中-大概word用于小值和必要时的一个指针…这个设计看起来不错,很难再改进了。

如果数字倾向于重复很多(一个大的如果),你可以尝试例如保持一个大cl_I的注册表,并将其双向映射到打包的整数id,你将存储在My_Map::map中-虽然很繁琐。为了解释,假设你得到987123498723489 -你在vector<cl_I>push_back它,然后在hash_map<cl_I, int>中将[987123498723489设置为该索引(即vector.size() - 1)。当遇到新的数字时,继续前进。您总是可以使用vector中的直接索引将int id映射回cl_I,另一种方法是使用O(1)平摊哈希表查找。