添加约10000个密钥后,无序映射中的分割错误

segmentation fault in unordered_map after ~ 10000 keys added

本文关键字:映射 分割 错误 无序 10000个 密钥 添加      更新时间:2023-10-16

当datact=10736尝试插入到一个无序映射中时,会发生分段故障(请参阅注释行,调用会为其抛出故障)。请参阅下面的尝试修复程序。

当抛出SIGSEGV时,它将我指向hashtable_policy.h 的第764行

INPUT:列1=计数,列2=16个字符串的数据文件

目的:通过将1个取代的不同序列的所有计数相加,对16个字符序列进行聚类。看到的第一个序列是"原点",通过它可以识别它的所有1-替换友元。

PSEUDOCODE:对于文件中的每一行:

  1. 读取计数,读取顺序。

  2. 如果序列key_value存在于哈希"location"(类型unordered_map),添加当前计数;

  3. 否则,创建一个新的key_value,使其指向此处的计数,然后将所有1-取代序列也分配给该计数。

代码:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cmath>
#include <vector>
#include <unordered_map>
#include <map>
#include "matrix.h"
using namespace std;
int nuc2num(char currchar)
{   // returns 0,1,2,3 for A,C,T,G respectively
    int outnum;
    if (currchar=='A')
    {
        outnum=0;
    }
    else if (currchar=='C')
    {
        outnum=1;
    }
    else if (currchar=='T')
    {
        outnum=2;
    }
    else
    {
        outnum=3;
    }
    return outnum;
}
int main(int argc, char* argv[])
{
    //command line arguments
    //  arg1=filename, arg2=barcode sequence length, arg3=#mismatches permitted
    //input handling
    //  file format: column 1 | column 2
    //               counts   | sequence    [int | string]
    string filename;
    string funnelstring;
    // define lookup matrix; rows=ACTG, cols = ACTG without row element
    Matrix <char> sub_lookup(4,3);
    sub_lookup[0][0] = 'C';
    sub_lookup[0][1] = 'T';
    sub_lookup[0][2] = 'G';
    sub_lookup[1][0] = 'A';
    sub_lookup[1][1] = 'T';
    sub_lookup[1][2] = 'G';
    sub_lookup[2][0] = 'A';
    sub_lookup[2][1] = 'C';
    sub_lookup[2][2] = 'G';
    sub_lookup[3][0] = 'A';
    sub_lookup[3][1] = 'C';
    sub_lookup[3][2] = 'T';
    int L,k;
    int j=0;
    const int buffersize=10000;
    int currentsize=buffersize;
    int datact=0;
    int currchar;
    vector <unsigned int> ctarr(buffersize);
    vector <string> seqarr(buffersize);
    filename=argv[1];
    L=atoi(argv[2]);
    k=atoi(argv[3]);
    unsigned int sct;
    int substrlen;
    string sequence,textct;
    ifstream seqfile (filename.c_str());
    //map <string,unsigned int*> location;
    unordered_map <string,unsigned int*> location;
    if (seqfile.is_open())
    {
        getline(seqfile,textct,'n');
        while (textct != "")
        {
            sct=atoi(textct.c_str());
            substrlen=textct.length();
            //cout << textct << endl;
            sequence=textct.substr(substrlen-L,L);
            //cout << sequence << endl;
            //is there an associated guy?
            if (location.find(sequence) != location.end()) //just asks whether this key has been assigned
            {   //there's a value in the region
                *location[sequence]+=sct;
            }
            else
            {   //no value in region, make a footprint
                ctarr[datact]=sct;
                seqarr[datact]=sequence;
                location[sequence]=&ctarr[datact]; //assign current key to point to data count

                //assign k substitution "funnel" region to point to this count as well
                for (j=0; j<L; j++)
                {
                    funnelstring=sequence;
                    currchar = nuc2num(sequence[j]);
                    if (datact==10736 && j==13)
                    {
                        cout << "here" << endl;
                        cout << sequence << endl;
                    }
                    for (k=0; k<3; k++)
                    {
                        funnelstring[j]=sub_lookup[currchar][k];
//                    if (datact==10736 && j==13)
//                    {
//                        cout << funnelstring << endl;
//                        cout << location.max_size() << " | " << location.size() << endl;
//                        string asdf;
//                        asdf="AAAAAAAAAAAAAAAA";
//                        location[asdf]=&ctarr[datact]; //still segfaults
//                    }
                        if (location.find(funnelstring) == location.end()) // asks whether this key has been assigned
                        {   //this region is not assigned to another funnel
                            location[funnelstring]=&ctarr[datact]; //LINE THAT CAUSES SIGSEGV
                        }
                    }
                }
                datact++;
                cout << datact << endl;
                if (datact>=currentsize)
                {
                    ctarr.resize(currentsize+buffersize);
                    seqarr.resize(currentsize+buffersize);
                    currentsize+=buffersize;
                }
            }
            getline(seqfile,textct,'n');
         }
        seqfile.close();
    }

探索。

  1. 添加了什么密钥并不重要,当datact==10736j=13添加到(unordered_map)位置的键会导致SIGSEGV
  2. 有问题的代码行(由上面的注释标记)在之前被调用过多次,并且操作正确
  3. 将无序映射交换为映射会导致相同的事件,但数据计数不同(更高)。仍低(datact之间==16-35千)
  4. 重写这段代码几乎完全正确,但据我所知,使用随机生成的16个字符序列可以完美地工作(没有数据量高达200000的segfault,没有测试更高)
  5. 在(4)中的代码中,确实出现了10000左右的rehash,这可能是相关的,也可能是巧合

如果需要,我可以发布读取的数据文件。

编辑:已解决unordered_map <string,unsigned int*> location替换为unordered_map <string,unsigned int> location(value_type为int而非int*)。现在value_type将索引保存在ctarr[]中。运行良好。谢谢

调用vector::resize()时,指向vector元素的指针可能无效。这是因为为了找到适合新大小的连续内存块,可能必须四处移动整个数据。换句话说:一旦调用resize,所有的location数据就会突然变成无用的垃圾。

可能的解决方案:

  • locationctarr中所需元素的索引存储为其值,而不是指针。(这肯定不会改变程序的语义。)
  • location存储实际的unsigned int值,而不是指针。根据您的程序逻辑以及更改和访问这些数据的方式,这可能不是您想要的

还要注意的是,尽管segfault发生在hashtable_policy.h中,但此错误与unordered_map(或vector)的实现无关-完全是您没有读取vector::resize()的引用的错误;-):http://www.cplusplus.com/reference/vector/vector/resize/("迭代器有效性"部分)


关于您的代码,我注意到的另一件事是,您使用operator[]来访问vector元素。这将禁用越界检查。如果我在代码中遇到像您这样的错误(很难追溯,因为它发生在离错误代码很远的地方),我的第一个操作方案将是将operator[]替换为vector::at()(实际上,我总是从at()开始,只有当我能够毫无疑问地证明边界检查是实现这一特定目的的性能瓶颈时,我才会切换)。这对你的问题没有帮助,但通常对发现错误有着宝贵的帮助。