为什么抛出std::bad_alloc

Why std::bad_alloc is thrown?

本文关键字:bad alloc std 为什么      更新时间:2023-10-16

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

然而,内存被占用在两个位置:当映射器读取(存储)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[]=" trnvf";
    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;

注意:通常emit中的keyvalue是基于模板的,但在本例中,为了简洁起见,我省略了它们。

首先,我认为std::bad_alloc是因为char *value(占用1GB)而抛出的,但异常是在value分配之后放置的测试cout消息之后抛出的(所以没有问题)。

根据我所读到的关于strtok实现的内容,原始char*被修改(在每个令牌的末尾添加),因此不分配添加内存。

唯一剩下的可能性是vector<pair<char*,int>>占用了空间,但我无法计算它的空间(请帮助我)。假设平均单词长度为5个字符,我们应该有~2*10^8个单词。

更新1201ProgramAlarm的答案:不幸的是,预先计算单词的数量,然后调用resize()来消除未使用的向量的内存是不可行的,原因有两个:

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

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

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