为什么抛出std :: bad_alloc?

时间:2015-12-30 05:10:47

标签: c++ memory-management vector mapreduce bad-alloc

我正在实施map / reduce并行项目。但是,使用(或多或少)1GB的输入文件,对于字数统计玩具示例,只有一个映射器(映射整个文件),我会收到std::bad_alloc异常。不幸的是,这只发生在远程Xeon Phi(RAM较小)上,所以没有深度调试。

但是,内存占用了2个位置:当映射器在char *中读取(存储)整个文件时:

void getNextKeyValue() {
    key = pos;//int
    value = new char[file_size];//file_size only with 1 mapper
    ssize_t result = pread(fd, value, file_size, pos);
    assert(result == ( file_size ) );
    morePairs = false;
}

调用map函数时的另一个函数和一系列pair<char*,int>作为地图的结果存储在vector内:

地图功能:

std::function<void(int key, char *value,MapResult<int,char*,char*,int> *result)> map_func = [](int key,char *value,MapResult<int,char*,char*,int> *result) {
    const char delimit[]=" \t\r\n\v\f";
    char *token , *save;
    token = strtok_r(value, delimit, &save);
    while (token != NULL){
        result->emit(token,1);
        token = strtok_r (NULL,delimit, &save);
    }
};

emit实施(以及地图'结果生成):

    void emit(char* key, int value) {
        res.push_back(pair<char*,int>(key,value));
    }
    ...
    private:
    vector<pair<char*,int>> res;

注意: key中的valueemit通常都是基于模板的,但在此示例中我省略了它们。

首先,我认为由于std::bad_alloc(需要1GB)而抛出char *value,但在{{1}之后放置测试cout消息后抛出异常分配(这不是问题)。

从我读到的关于value实现的内容中,原始的strtok被修改(在每个令牌的末尾添加char*),因此没有分配额外的内存。

唯一剩下的可能性是\0占用的空间,但我无法计算它的空间(请帮我解决)。假设平均字长为5个字符,我们应该有~2 * 10 ^ 8个字。

1201ProgramAlarm's answer之后更新 不幸的是,预先计算单词的数量,然后调用vector<pair<char*,int>>以消除未使用的向量的内存是不可行的,原因有两个:

  1. 会大大降低性能。如果不调用resize()并仅计算280MB文件的字数,则总执行时间超过1329ms需要1242ms(第一次读取文件时需要大约5000s)。
  2. 使用此解决方案,最终用户在编写map函数时应该深入考虑内存使用情况,这通常不会发生在像Hadoop这样的经典map / reduce框架中。

1 个答案:

答案 0 :(得分:0)

问题不在于vector所使用的空间,它是载体在容量较小时以前使用的所有空间。除非你在向量上调用reserve,否则当你按下第一个元素时,它会开始为空并分配少量空间(通常足够一个元素)。在以后的推送期间,如果没有足够的剩余空间分配,它将分配更多(当前大小的1.5倍或2倍)。这意味着你需要足够的可用内存用于较小的和较大的。因为释放的内存块在合并时仍然不足以满足下一个更大的请求量,可能会有很多空闲但未使用的内存。

你应该调用res.reserve(/*appropriate large size*/),或者将容器切换到deque,虽然它最终需要更多空间,但随着它的增长,不需要重新分配。要获得保留的大小,您可以遍历文件一次以查看其中有多少单词,为它们预留空间,然后再次移动并保存单词。