为什么GCC的ifstream >> double分配这么多内存?

2024-03-03

我需要读取一系列数字以空格分隔的人类可读文件 https://earth-info.nga.mil/GandG/wgs84/gravitymod/egm96/ww15mgh.grd.z并做了一些数学计算,但我在读取文件时遇到了一些真正奇怪的内存行为。

如果我读到这些数字并立即丢弃它们......

#include <fstream>

int main(int, char**) {
    std::ifstream ww15mgh("ww15mgh.grd");
    double value;
    while (ww15mgh >> value);
    return 0;
}

我的程序根据 valgrind 分配 59MB 内存,相对于文件大小线性缩放:

$ g++ stackoverflow.cpp
$ valgrind --tool=memcheck --leak-check=yes ./a.out 2>&1 | grep total
==523661==   total heap usage: 1,038,970 allocs, 1,038,970 frees, 59,302,487 

但是,如果我使用ifstream >> string相反,然后使用sscanf为了解析字符串,我的内存使用看起来更加理智:

#include <fstream>
#include <string>
#include <cstdio>

int main(int, char**) {
    std::ifstream ww15mgh("ww15mgh.grd");
    double value;
    std::string text;
    while (ww15mgh >> text)
        std::sscanf(text.c_str(), "%lf", &value);
    return 0;
}
$ g++ stackoverflow2.cpp
$ valgrind --tool=memcheck --leak-check=yes ./a.out 2>&1 | grep total
==534531==   total heap usage: 3 allocs, 3 frees, 81,368 bytes allocated

为了排除 IO 缓冲区的问题,我尝试了两种方法ww15mgh.rdbuf()->pubsetbuf(0, 0);(这使得该程序需要很长时间,并且仍然执行价值 59MB 的分配)并且pubsetbuf具有巨大的堆栈分配缓冲区(仍然是 59MB)。在任一平台上编译时都会重现该行为gcc 10.2.0 https://archlinux.org/packages/core/x86_64/gcc/ and clang 11.0.1 https://archlinux.org/packages/extra/x86_64/clang/使用时/usr/lib/libstdc++.so.6 from gcc-libs 10.2.0 https://archlinux.org/packages/core/x86_64/gcc-libs/ and /usr/lib/libc.so.6 from glibc 2.32 https://archlinux.org/packages/core/x86_64/glibc/。系统区域设置设置为en_US.UTF-8但如果我设置环境变量,这也会重现LC_ALL=C.

我第一次注意到这个问题的ARM CI环境是在Ubuntu Focal上使用交叉编译的海湾合作委员会9.3.0 https://packages.ubuntu.com/focal/g++-arm-linux-gnueabihf, libstdc++6 10.2.0 https://packages.ubuntu.com/focal/libstdc++6-armhf-cross and libc 2.31 https://packages.ubuntu.com/focal/libc6-dev-armhf-cross.

下列的评论中的建议 https://stackoverflow.com/questions/65703206/why-does-ifstream-double-allocate-so-much-memory?noredirect=1#comment116168866_65703206,我尝试了 LLVM 的 libc++,并在原始程序中获得了完全正常的行为:

$ clang++ -std=c++14 -stdlib=libc++ -I/usr/include/c++/v1 stackoverflow.cpp
$ valgrind --tool=memcheck --leak-check=yes ./a.out 2>&1 | grep total
==700627==   total heap usage: 3 allocs, 3 frees, 8,664 bytes allocated

因此,这种行为似乎是 GCC 实现所独有的fstream。在构建或使用时我可以做些什么不同的事情ifstream在 GNU 环境中编译时可以避免分配大量堆内存吗?这是他们的错误吗<fstream>?

正如在评论讨论中发现的,程序的实际内存占用是完全正常的(84kb),它只是分配和释放相同的一小块内存数十万次,这在使用像 ASAN 这样避免重新分配的自定义分配器时会产生问题。 - 使用堆空间。我已经发布后续问题 https://stackoverflow.com/questions/65721208/how-do-i-exclude-allocations-in-a-tight-loop-from-asan询问“ASAN”层面如何应对此类问题。

A gitlab 项目在其 CI 管道中重现了该问题 https://gitlab.com/drmoose/why-does-gccs-ifstream-double-allocate-so-much-memory由 Stack Overflow 用户慷慨捐赠@卡米尔库克 https://stackoverflow.com/users/9072753/kamilcuk.


确实没有。显示的数字 59,302,487valgrind is the sum所有分配的内存,并不代表程序的实际内存消耗。

事实证明,相关的libstdc++实现operator>>创建一个临时的std::string为暂存空间,并为其保留 32 个字节。使用后立即释放。看num_get::do_get https://github.com/gcc-mirror/gcc/blob/16e2427f50c208dfe07d07f18009969502c25dc8/libstdc%2B%2B-v3/include/bits/locale_facets.tcc#L703。考虑到开销,这实际上可能分配了 56 个字节左右,乘以大约 100 万次重复,从某种意义上来说,确实意味着总共分配了 59 MB,当然,这就是为什么该数字与输入数量成线性比例的原因。但同样的 56 个字节被一遍又一遍地分配和释放。这是 libstdc++ 完全无辜的行为,并不是泄漏或过多的内存消耗。

我没有检查 libc++ 源代码,但一个不错的选择是它使用堆栈上的暂存空间而不是堆。

正如评论中所确定的,您真正的问题是您在 AddressSanitizer 下运行它,这会延迟已释放内存的重用,以帮助捕获释放后使用错误。我对如何解决这个问题有一些想法(没有双关语)并将其发布在如何从 ASAN 中排除紧密循环中的分配? https://stackoverflow.com/questions/65721208/how-do-i-exclude-allocations-in-a-tight-loop-from-asan

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么GCC的ifstream >> double分配这么多内存? 的相关文章

随机推荐

  • 使用 php 将事件插入谷歌日历

    如何将活动插入 Google 日历 我正在使用本指南 https developers google com google apps calendar v3 reference events insert https developers
  • 在android上使用ormlite删除?

    我有一个客户端 bean DatabaseField columnName client id generatedId true useGetSet true private Integer clientId DatabaseField c
  • 如何在网页视图中播放本地 swf 文件

    我正在尝试在 webview 中播放本地 swf 文件 保存在 asset 或 sdcard 中 但我没有运气 任何人都可以指导我正确的方法吗 我可以通过 url 播放 swf 文件 但是在 webview 中播放本地文件时遇到困难 swf
  • 如何使用 gson 库将字符串转换为 JsonObject

    请指教如何转换String to JsonObject using gson图书馆 我不成功的做法 String string abcde Gson gson new Gson JsonObject json new JsonObject
  • 如何忽略部分文本并在剩余部分进行搜索和替换? [复制]

    这个问题在这里已经有答案了 在文本文件中进行正则表达式查找和替换时 我想跳过并忽略文本的某些片段 也就是说 应该将文本的某些部分排除在搜索之外 而只对其余部分进行搜索和替换 标准是 1 任何介于START and END应从搜索和替换中排除
  • 通过 Enum 快速递增

    我喜欢 Swift 允许使用枚举方法 我正在尝试使用一种方法 但正在寻找一种更具可扩展性的方法来执行此操作 enum CopyState case binary hex both init self both mutating func n
  • 转换运算符:gcc 与 clang

    考虑以下代码 https godbolt org z s17aoczj6 https godbolt org z s17aoczj6 template
  • 使用 D3 创建 DIV 动态列表

    我一直在使用D3 http d3js org创建精美的动画图表 并且示例很棒 然而 我正在尝试做一些看似更基本的事情 但遇到了麻烦 将数据绑定到简单的 DIV 列表 我设置了enter 以不透明度 0 初始化元素 transition 让它
  • 使用尺寸类别时 UITableViewCell 子视图报告不正确的宽度

    由于某种原因 当在 xcode 6 中使用尺寸类时 我的单元格中的子视图宽度不正确 我有一个UIImageView使用自动布局调整大小 常量 顶部 左 右 底部为 10 当从以下位置调用以下内容时tableView tableView UI
  • jQuery 'mmenu' 打开时的回调事件

    使用 jQuery菜单插件 http mmenu frebsite nl 我需要在菜单打开后调用 JavaScript 函数 我看不到从API文档 http mmenu frebsite nl documentation api html如
  • 如何将字符转换为等效的 System.Windows.Input.Key Enum 值?

    我想写一个这样的函数 public System Windows Input Key ResolveKey char charToResolve Code goes here that resolves the charToResolve
  • Flask SQLAlchemy 不会关闭 MySQL 数据库连接

    我有一个 Flask 应用程序 使用 Flask SQLAlchemy 和 MySQL 数据库 其中数据库定义如下 db py from flask sqlalchemy import SQLAlchemy db SQLAlchemy ma
  • VBA宏快速删除行

    我有几个非常大的 Excel 数据文件 我需要遍历它们并删除 T 列中单元格值为 1 的所有行 现在我的代码如下所示 Sub test Dim cell As Range For Each cell In Worksheets Sheet1
  • 下载blob存储并返回Json对象

    我正在尝试下载一个 json我存储在容器中的 blobAzure Storage using Newtonsoft Json将其写入对象 我通过调用来做到这一点 CloudBlockBlob blob DownloadToStream st
  • PostgreSQL - 错误:查询没有结果数据的目标

    为什么我会收到以下错误 错误 查询没有结果数据的目标 这是我的功能 CREATE OR REPLACE FUNCTION public SumASCII value character varying RETURNS int LANGUAG
  • 由点聚类着色的等高线图 matlab

    我有两个向量 它们是成对的值 size X 1e4 x 1 size Y 1e4 x 1 是否可以绘制一个contour plot某种方式通过最高密度的点来制作轮廓 即最高聚类 红色 然后在其他地方渐变颜色 如果您需要更多说明 请询问 问候
  • 引用另一个 Snakemake 规则的输入或输出文件

    如何以编程方式引用另一个 Snakemake 规则的属性 我需要更换什么
  • 为什么惰性评估有用?

    我长期以来一直想知道为什么惰性评估有用 我还没有人以一种有意义的方式向我解释 大多数情况下 它最终都会归结为 相信我 注意 我的意思不是记忆 主要是因为它可以更有效 如果不使用值 则不需要计算它们 例如 我可以将三个值传递到一个函数中 但根
  • 如何消除数字范围中的重叠 (AWK)

    我正在尝试删除文件内的重叠部分 有一堆以 A 开头的记录 并且具有 起始值 和 结束值 还有一堆以 B 开头的记录 也有范围 并且显示与以 A 开头的记录可能重叠 这个想法是从 A 中删除重叠范围 以便只存在非重叠范围 B 中的某些记录具有
  • 为什么GCC的ifstream >> double分配这么多内存?

    我需要读取一系列数字以空格分隔的人类可读文件 https earth info nga mil GandG wgs84 gravitymod egm96 ww15mgh grd z并做了一些数学计算 但我在读取文件时遇到了一些真正奇怪的内存