繁体   English   中英

如何减少C ++中数据的内存大小?

[英]How to reduce memory size for data in C++?

我正在研究C ++并使用multimap存储数据。

 struct data
 {
      char* value1;
      char* value2;

      data(char* _value1, char* _value2)
      {
           int len1 = strlen(_value1);
           value1 = new char[len1+1];
           strcpy(value1,_value1);

           int len2 = strlen(_value2);
           value2 = new char[len2+2];
           strcpy(value2,_value2);
      }
      ~data()
      {
           delete[] value1;
           delete[] value2;
      }
 }

 struct ltstr
 {
     bool operator()(const char* s1, const char* s2) const
     {
          return strcmp(s1, s2) < 0;
     }
 };


 multimap <char*, data*, ltstr> m;

样本输入:

  Key               Value
  ABCD123456        Data_Mining Indent Test Fast Might Must Favor List Myself Janki Jyoti Sepal Petal Catel Katlina Katrina Tesing Must Motor blah blah.
  ABCD123456        Datfassaa_Minifasfngf Indesfsant Tfdasest Fast Might Must Favor List My\\fsad\\\self Jfasfsa Katrifasdna Tesinfasfg Must Motor blah blah.
  tretD152456       fasdfa fasfsaDfasdfsafata_Mafsfining Infdsdent Tdfsest Fast Might Must Favor List Myself Janki

输入中有2700万个条目。 输入大小= 14GB

但我注意到内存消耗达到56 GB。 我可以知道如何减少内存大小?

如果你不能减少实际存储的数据量,你可能想尝试使用一个开销较小的不同容器(map和multimap有很多)或找到一种方法只保留部分数据记忆。

您可能想看看这些库:

一种可能性是使用std::map<char *, std::vector<data> >而不是multimap。 在多图中,您将密钥字符串存储在每个条目中。 使用地图,您只有一个密钥字符串副本,并附加了多个data项。

第一个优化是存储data对象而不是指针

std::multimap <char*, data, ltstr> m;

因为使用data*会为分配增加额外的内存开销。

另一个是使用池分配器/内存池来减少动态内存分配的占用空间。

如果您有许多相同的密钥字符串,那么您可以改进它,如果您可以重用密钥。

如果没有看到您的某些数据,有几件事可以改善您项目的内存使用情况。

首先,正如Olaf建议的那样,将数据对象存储在multimap中而不​​是指向它的指针。 我不建议为您的数据结构使用池,与直接将其存储在地图中相比,它只会使没有内存节省的事情复杂化。

你可以做的是你的地图的一个专门的分配器,它分配std::pair<char*, data>对象。 这可以节省一些开销和堆碎片。

接下来,您应该关注的主要事情是尝试摆脱数据中的两个char*指针。 有14个数据,必须有一些重叠。 根据它是什么数据,您可以存储它有点不同。

例如,如果数据是名称或关键字,那么将它们存储在中央散列中是有意义的。 是的,如上所述,有更复杂的解决方案,如DAWG,但我认为应首先尝试简单的解决方案。

通过简单地将它存储在std::set<std::string>并将迭代器存储到它,您将压缩所有重复项,这将节省大量数据。 这假设你没有删除字符串。 删除字符串将需要您进行一些引用计数,因此您将使用类似std::map<std::string, unsinged long> 我建议您编写一个继承自/包含此哈希的类,而不是将引用计数逻辑放入您的数据类中。

如果您存储的数据没有很多重叠,例如因为它是二进制数据,那么我建议您将其存储在std::stringstd::vector<char> 原因是因为现在你可以摆脱数据结构中的逻辑,甚至用std::pair替换它。

我还假设您的密钥不是您存储在数据结构中的指针之一。 如果是,绝对摆脱它并在你的multimap中使用std::pairfirst属性。

根据您存储的数据类型,可能会进一步改进。

因此,有很多假设可能不适用于您的数据,您可以尽可能少:

typedef std::set<std:string> StringMap;
typedef StringMap::const_iterator StringRef;
typedef std::multimap<StringRef, std::pair<StringRef, StringRef>> DataMap;

我怀疑你是否泄漏或不必要地重复密钥中的内存。 关键char *字符串来自何处以及如何管理其内存?

如果它们与数据对象中的字符串相同,请考虑使用multiset<data *, ltdata>而不是multimap

如果有许多重复的字符串,请考虑在set<char *,ltstr>汇集字符串以消除重复。

我仍然不完全确定这里发生了什么,但似乎内存开销至少是问题的一部分。 但是,总体内存消耗约为data结构所需的4倍。 如果有27M记录占用14GB,则每条记录大约有500个字节,但占用的空间为56GB。 对我来说,这表明存储的数据比我们在这里显示的要多,或者至少有一些数据被存储多次。

而“堆存储的额外数据”并没有真正为我做这件事。 在linux中,内存分配大约需要32字节的数据。 16字节的开销,分配的内存本身占用16个字节的倍数。

因此,对于存储在multimap中的一个data *记录,我们需要:

 16 bytes of header for the memory allocation
 8 bytes for pointer of `value1`
 8 bytes for pointer of `value2`
 16 bytes of header for the string in value1
 16 bytes of header for the string in value2
 8 bytes (on average) "size rounding" for string in value 1
 8 bytes (on average) "size rounding" for string in value 2

 ?? bytes from the file. (X)

 80 + X bytes total. 

然后我们在multimap中有char *

 16 bytes of header for the memory allocation. 
 8 bytes of rounding on average. 

 ?? bytes from the file. (Y)

 24 + Y bytes total. 

多图的每个节点都有两个指针(我假设它是某种二叉树):

 16 bytes of header for the memory allocation of the node. 
 8 bytes of pointer to "left"
 8 bytes of pointer to "right"

 32 bytes total. 

因此,在文件中每个条目产生136个字节的“开销”。 对于27M记录,这仅仅超过4GB。

正如我所说,该文件每个条目包含500个字节,因此产生14GB。

总共18GB。

所以,在某个地方,某些事情要么是泄漏,要么数学是错误的。 我可能会在这里进行计算,但即使上面的所有内容都占据了我计算的空间的两倍,但仍有20GB的空间不足。

我们当然可以采取一些措施来节省内存:

1)不要在data分配两个字符串。 首先计算两个长度,分配一个内存块,然后立即存储字符串:

  data(char* _value1, char* _value2)
  {
       int len1 = strlen(_value1);
       int len2 = strlen(_value2);
       value1 = new char[len1 + len2 +2];
       strcpy(value1,_value1);

       value2 = value1 + len1 + 1; 
       strcpy(value2,_value2);
  }

这样每个条目平均可节省24个字节。 通过聪明地为数据,value1和value2分配内存,我们可以节省更多。 但这可能有点“太聪明”。

2)分配一大块data项,并一次一个地输出data项也会有所帮助。 为此,我们需要一个空构造函数和一个“setvalues”方法:

struct data
{
    ...
    data() {};
    ... 
    set_values(char* _value1, char* _value2)
    {
         int len1 = strlen(_value1);
         int len2 = strlen(_value2);
         value1 = new char[len1 + len2 +2];
         strcpy(value1,_value1);

         value2 = value1 + len1 + 1; 
         strcpy(value2,_value2);
    }
}

std::string v1[100], v2[100], key[100];

for(i = 0; i < 100; i++)
{
    if (!read_line_from_file(key[i], v1[i], v2[i]))
    {
        break;
    }
}    

data* data_block = new data[i]; 

for(j = 0; j < i; j++)
{
    data_block[j].setValues[v1[j].c_str(), v2[j].c_str());
    m.insert(key[i].c_str(), &data_block[j]);
}

同样,这不会节省大量内存,但每个16字节区域可以节省一些内存。 以上当然不是完整的代码,更多的是“如何完成它的说明”。

3)我仍然不确定“密钥”在多图中的来源,但如果密钥是value1和value2条目之一,那么你可以重用其中一个,而不是存储另一个副本[假设它是如何的目前完成]。

我很抱歉,如果这不是一个真正的答案,但我确实认为这是一个答案,在某种意义上说“在某个地方,在你解释你正在做的事情时,有些事情是不明智的”。

了解在您的程序中进行的分配肯定会有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM