为什么 std::bad_alloc 被抛出?
Why std::bad_alloc is thrown?
我正在实施一个 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[]=" \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
和 emit
中的 value
是基于模板的,但为了清楚起见,我在这个例子中省略了它们。
首先我认为 std::bad_alloc
是因为 char *value
(占用 1GB)而抛出的,但是在 cout
消息后放置的测试 cout
消息后抛出异常=21=]分配(所以这不是问题)。
从我读到的关于 strtok
实现的内容来看,原来的 char*
被修改了(在每个标记的末尾添加 [=29=]
)所以没有分配额外的内存。
唯一剩下的可能性是 vector<pair<char*,int>>
被占用 space,但我无法计算出它的 space(请帮助我)。假设平均字长为 5 个字符,我们应该有 ~ 2*10^8 个字。
在 之后更新:
不幸的是,预先计算单词的数量然后调用 resize()
以消除未使用的向量的内存是不可行的,原因有两个:
- 这会大大降低性能。在不调用
emit
的情况下,只计算一个 280MB 文件的字数,总执行时间为 1329 毫秒,耗时 1242 毫秒(第一次读取文件时约为 5000 秒)。
- 使用此解决方案,最终用户在编写 map 函数时应深入考虑内存使用情况,这在 Hadoop 等经典 map/reduce 框架中通常不会发生。
问题不是 vector
使用的 space,而是 vector 在其容量较小时使用的所有 space。除非你在 vector 上调用 reserve
,否则它开始是空的,并在你压入第一个元素时分配少量的 space (通常对于一个元素来说足够大)。在以后的推送过程中,如果没有足够的剩余 space 分配,它将分配更多(当前大小的 1.5 倍或 2 倍)。这意味着您需要足够的可用内存来容纳较小的尺寸和较大的尺寸。因为释放的内存块在组合时仍然不足以满足下一个更大的请求量,所以可能会有很多空闲但未使用的内存。
您应该调用 res.reserve(/*appropriate large size*/)
,或将容器切换到 deque
,虽然最终需要更多 space,但不需要进行重新分配,因为它成长。要获得保留的大小,您可以遍历一次文件以查看其中实际有多少单词,为它们保留 space,然后再次遍历并保存单词。
我正在实施一个 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[]=" \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
和 emit
中的 value
是基于模板的,但为了清楚起见,我在这个例子中省略了它们。
首先我认为 std::bad_alloc
是因为 char *value
(占用 1GB)而抛出的,但是在 cout
消息后放置的测试 cout
消息后抛出异常=21=]分配(所以这不是问题)。
从我读到的关于 strtok
实现的内容来看,原来的 char*
被修改了(在每个标记的末尾添加 [=29=]
)所以没有分配额外的内存。
唯一剩下的可能性是 vector<pair<char*,int>>
被占用 space,但我无法计算出它的 space(请帮助我)。假设平均字长为 5 个字符,我们应该有 ~ 2*10^8 个字。
在 resize()
以消除未使用的向量的内存是不可行的,原因有两个:
- 这会大大降低性能。在不调用
emit
的情况下,只计算一个 280MB 文件的字数,总执行时间为 1329 毫秒,耗时 1242 毫秒(第一次读取文件时约为 5000 秒)。 - 使用此解决方案,最终用户在编写 map 函数时应深入考虑内存使用情况,这在 Hadoop 等经典 map/reduce 框架中通常不会发生。
问题不是 vector
使用的 space,而是 vector 在其容量较小时使用的所有 space。除非你在 vector 上调用 reserve
,否则它开始是空的,并在你压入第一个元素时分配少量的 space (通常对于一个元素来说足够大)。在以后的推送过程中,如果没有足够的剩余 space 分配,它将分配更多(当前大小的 1.5 倍或 2 倍)。这意味着您需要足够的可用内存来容纳较小的尺寸和较大的尺寸。因为释放的内存块在组合时仍然不足以满足下一个更大的请求量,所以可能会有很多空闲但未使用的内存。
您应该调用 res.reserve(/*appropriate large size*/)
,或将容器切换到 deque
,虽然最终需要更多 space,但不需要进行重新分配,因为它成长。要获得保留的大小,您可以遍历一次文件以查看其中实际有多少单词,为它们保留 space,然后再次遍历并保存单词。