【问题标题】:How to generate a hashmap for huge chunk of data?如何为大量数据生成哈希图?
【发布时间】:2014-06-16 06:23:19
【问题描述】:

我想制作一个映射,使一组指针指向动态大小的数组。 我确实使用了带有链接的散列。但由于我使用它的数据量很大,因此程序在几次迭代后会给出std::bad_alloc。原因可能是new用于生成链表。

请有人建议我应该使用哪种数据结构? 或者还有什么可以提高我的哈希表的内存使用率?

程序是 C++。

这是我的代码的样子: 哈希表的初始化:

class Link
{ 
  public:
         double iData; 
         Link* pNext; 
         Link(double it) : iData(it) 
         { }
         void displayLink()
         { cout << iData << " "; }
}; 

class List
 {
  private:
          Link* pFirst; 
  public:
         List() 
         { pFirst = NULL; }
         void insert(double key) 
         {

           if(pFirst==NULL)
           pFirst = new Link(key);
       else
          {
        Link* pLink = new Link(key);
        pLink->pNext = pFirst;
        pFirst = pLink;
       }

         }     

 }; 
class HashTable
{      
  public:
         int arraySize;
         vector<List*> hashArray; 

         HashTable(int size) 
         {

            hashArray.resize(size); 
            for(int j=0; j<size; j++) 
            hashArray[j] = new List; 
         }
};

主要的sn-p:

int t_sample = 1000;
 for(int i=0; i < k; i++)                                // initialize random position
{
        x[i] = (cal_rand() * dom_sizex);   //dom_sizex = 20e-10  cal_rand() generates rand no between 0 and 1
        y[i] = (cal_rand() * dom_sizey);    //dom_sizey = 10e-10
}

for(int t=0; t < t_sample; t++)
{
 int size;
 size = cell_nox * cell_noy; //size of hash table cell_nox = 212, cell_noy = 424

 HashTable theHashTable(size); //make table
 int hashValue = 0;

 for(int n=0; n<k; n++)   // k = 10*212*424
 {
  int m = x[n] /cell_width;     //cell_width = 4.7e-8
  int l = y[n] / cell_width;

   hashValue = (kx*l)+m;
   theHashTable.hashArray[hashValue]->insert(n); 

  }

   -------
   -------
 }

【问题讨论】:

  • 所以您需要在内存中总共存储大约 9 亿个值?即使每个值只有 4 个字节,您也会用完 32 位程序的可用虚拟地址空间。没有任何数据结构改变可以解决这个问题。
  • @T.C.但是人们确实在 Fortran 中运行了这样的模拟。没有办法解决这个问题吗?
  • @aks:好吧 - 您可能会评论您是否需要同时保留每次迭代的所有值,或者是否有某种方法可以更早地处理和丢弃其中一些结果?根据您的数据访问需求,您可能会考虑配置大量交换空间,或将值显式写入磁盘,直到再次需要它们为止。另外,你能编译一个 64 位的应用程序吗?你有多少可用内存?
  • @aks 所以你真的只有大约 900k 的值,并且只是在每次迭代中更新它们而不是创建新的?那么你可能需要在你的代码中寻找内存泄漏。
  • 作为 T.C.说,如果它通过几次迭代然后崩溃,这表明它有足够的内存用于初始迭代,但后来泄漏了。更一般地说,std::unordered_map&lt;key, std::vector&lt;value&gt;&gt; 听起来是正确的,除非你有例如连续递增键 - 然后你可以只拥有vector&lt;value&gt;。如果最小和最大长度之间的差异很小,您可能需要考虑使用带有初始长度元素或尾随标记的std::array&lt;&gt;

标签: c++ data-structures hashtable


【解决方案1】:

首先,使用标准容器。在您的具体情况下,您可能需要:

  • std::unordered_multimap&lt;int, double&gt;
  • std::unordered_map&lt;int, std::vector&lt;double&gt;&gt;

(注意:如果您没有 C++11,则在 Boost 中可用)

你的主循环变成(使用第二个选项):

typedef std::unordered_map<int, std::vector<double>> HashTable;

for(int t = 0; t < t_sample; ++t)
{
    size_t const size = cell_nox * cell_noy;
       // size of hash table cell_nox = 212, cell_noy = 424

    HashTable theHashTable;
    theHashTable.reserve(size);

    for (int n = 0; n < k; ++n)   // k = 10*212*424
    {
        int m = x[n] / cell_width;     //cell_width = 4.7e-8
        int l = y[n] / cell_width;

        int const cellId = (kx*l)+m;

        theHashTable[cellId].push_back(n);
    }
}

这不会(可靠地)泄漏内存,尽管您当然可能有其他泄漏,因此会给您一个可靠的基线。它也可能比您的方法更快,界面更方便,等等......

一般而言,您不应该重新发明轮子,除非您有可用轮子未解决的特定需求,或者您实际上正在尝试学习如何创建轮子或创建更好的轮子。

【讨论】:

    【解决方案2】:

    操作系统必须解决与内存页面相同的问题,也许值得看看它是如何完成的?首先,假设所有页面都在磁盘上。一个页面是一个固定大小的内存块。对于您的用例,假设它是您的记录数组。由于 RAM 是有限的,操作系统会维护页码和它在 RAM 中的位置之间的映射。

    因此,假设您的页面有 1000 条记录,并且您想访问记录 2024,您会向操作系统询问第 2 页,并从该页面读取记录 24。这样,您的地图大小只有 1/1000。

    现在,如果您的页面没有映射到内存位置,那么它要么在磁盘上,要么之前从未被访问过(为空)。然后您需要换出另一个页面,并从磁盘加载该页面(并更新位置映射)。

    这是对所发生情况的非常简化的描述,如果有人因为这样描述它而让我大吃一惊,我不会感到惊讶。

    重点是:

    这对你意味着什么?

    首先,您的数据超出了 RAM - 如果您不想先尝试压缩,您将无法绕过写入磁盘。 其次,如果你愿意,你的链可以作为页面工作,但我想知道分页你的哈希码是否会更好。我的意思是,使用高位作为页码,使用低位作为页面中的偏移量。避免冲突仍然是关键,因为您希望加载尽可能少的页面。您仍然可以链接您的页面,并最终得到一个小得多的地图。 其次 - 一个关键部分是决定哪些页面要换掉,以便为新页面腾出空间。 LRU 应该没问题。如果您可以更好地预测您将(不需要)需要哪些页面,那么对您来说会更好。 第三 - 您需要页面的占位符来告诉您它们是在内存中还是在磁盘上。

    希望这会有所帮助。

    【讨论】:

    • 我很抱歉,但大部分内容都在我脑海中浮现。让我重新表述我的问题。只知道我实际上想用我的代码做什么。我有 898880 个粒子,89888 个细胞。所以每个单元格中有 10 个粒子。现在,由于我随机分配位置给这些粒子,我需要将每个粒子索引到它的单元格。所以我确实使用链表将 10 个值映射到一个单元格。并且单元格由哈希表的哈希值标记。当我用超过 200 次迭代模拟这个系统时,程序终止于:在抛出 'std::bad_alloc' 的实例后调用终止
    • 那么,这些单元格、空间位置/体积是什么?为什么要使用哈希表,一个普通的数组还不够吗?根据我从您的代码中了解到的情况,您的迭代是相互独立的吗?您不能在开始下一次迭代之前将每次迭代的结果保存到磁盘,然后将其从内存中丢弃吗?
    • 你的意思是我要初始化 89888 个数组吗?我用它来删除: for(int m=0; ml)+m; delete[] theHashTable.hashArray[hashValue]; } } void del_list() { 链接 pCurrent = pFirst;而(pCurrent!= NULL){链接* del = pCurrent; pCurrent = pCurrent->pNext;免费(删除); } pFirst = NULL; }
    • 另外,一个单元格中是否总是有 10 个粒子,或者这只是预期值?删除对我来说看起来不错,但老实说,我对 Java 有更多的经验。
    • 没有 10 是平均水平。它会从一个单元格变为另一个单元格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-21
    • 1970-01-01
    相关资源
    最近更新 更多