V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dangyuluo
V2EX  ›  C

问一个 G++对于未赋初值的变量纯声明优化的问题

  •  
  •   dangyuluo · 2019-02-06 03:14:56 +08:00 · 3190 次点击
    这是一个创建于 2117 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们公司对于系统的实时性要求很高,不允许 runtime 动态内存分配。最近在工作中遇到了一个很神器的 BUG,代码类似如下:

    void func(){
      int count = 0;
      count = calculate_people();
      ...
    }
    
    memory_test_start(); // catch all unwanted dynamic memory malloc or free
    func();
    

    如上所示,变量count在声明的时候顺便赋了个初始值,然后立即被覆盖。代码运行和测试没有任何问题,Heap 内存也没有动过。

    但是当我删除 = 0; 这个初始值之后,memory_test_tool发现了一大堆mallocfree。我怀疑是编译器看到count没有赋初值,就做了一些优化?导致后期的动态内存分配?

    我不是编译器大牛,也没有搜索到相关的信息。不知道哪位来释疑一下?谢谢。

    第 1 条附言  ·  2019-02-06 08:50:54 +08:00
    查到原因了,并不是编译器优化导致的。如果不赋初值的话,在某个 test 中会发生逻辑问题导致错失败。测试用的是 gtest 框架,`operator<<`操作符被 overloaded 用来输出错误信息。

    问题就在于,这时候会生成一个`std::string`,而是否在堆上分配内存完全取决于字符串的长度,超过一定阈值之后才会在 heap 上分配,所以造成了 unexpected malloc and free。还是自己学艺不精,这么简单的问题应该早意识到的。
    9 条回复    2019-02-06 19:26:44 +08:00
    hackpro
        1
    hackpro  
       2019-02-06 03:30:41 +08:00 via iPad   ❤️ 1
    会不会因为变量后续没有被引用到 count = calculate_people()直接被优化掉了
    在左值 count 后加个 log 验证下
    henglinli
        2
    henglinli  
       2019-02-06 10:33:25 +08:00
    楼主遇到的问题的 msan ( MemorySanitizer )可以定位。

    看到“不允许 runtime 动态内存分配”,我很好奇并相信一定有和我一样的人同样感兴趣:能分享下你们是怎么做到这一点的?
    这是我能想到的一种思路,作为抛砖引玉:
    假定 std::string 仅测试部分使用,故不受到“不允许 runtime 动态内存分配”限制。标题限定到使用 g++,如果指的是 gnu c++的话,采用 splitstack ( http://gcc.gnu.org/wiki/SplitStacks )也许算是能做到(如果 splitstack 被认为不是 runtime 的话)。假设 google sanitizers ( https://github.com/google/sanitizers )和 splitstack 相互兼容的话(目前已知 splitstack 被 gcc go ( https://github.com/golang/gofrontend )用到,同公司的不同团队合作的可能性很高),那么测试部分也没多大问题。即使考虑到 llvm 也是支持的( http://llvm.org/docs/SegmentedStacks.html)
    ,但是真的有人这么写吗?
    dangyuluo
        3
    dangyuluo  
    OP
       2019-02-06 10:44:37 +08:00
    @henglinli 我们公司用的是 ROS2,使用的 OSRF 的内存测试框架。具体原理我没有钻研。
    henglinli
        4
    henglinli  
       2019-02-06 11:25:40 +08:00
    @dangyuluo 是这 2 个吧 https://github.com/ros2 https://github.com/osrf/osrf_testing_tools_cpp
    专有硬件加专有 os ( ros2 是 os 的话),msan 可能不支持你的平台或者 OS。
    即使是专有硬件加专有 OS,“不允许 runtime 动态内存分配”这一点,以我的水平怎么想也觉得做不到。
    我理解“不允许 runtime 动态内存分配”,意味着没有“堆”。楼主写的是 OS 或者 kernel ?没有其他进程?
    不知道有没有专业人士出来科普一下。
    dangyuluo
        5
    dangyuluo  
    OP
       2019-02-06 11:40:43 +08:00
    @henglinli 算是写的 OS,别人可以在我们的系统上开发。“没有 runtime 动态内存分配”在我们公司指的是,没有运行过程中的堆内存分配或者释放,没有文件 /网络接口等 blocking 过程的读取,尽量提高实时性,可以运行在硬实时系统上。

    比如程序启动过程锁住 900MB 内存,维护一个内存池。所有需要内存的程序都只在启动时从这个内存池分配。
    henglinli
        6
    henglinli  
       2019-02-06 12:40:40 +08:00
    @dangyuluo 看来我们理解的 runtime 不是同一个概念。我理解的 runtime:比如 c++语言的 runtime 是 c++标准库和 c 库。 [lvm.org/docs/SegmentedStacks.html] 有一句说 The runtime functionality is already there in libgcc. 她说 splitstack 作为 runtime 一部分包含在 libgcc 中。libgcc 对于 gnu c 而言也是其 runtime。所以,我认为只有汇编才没有 runtime。感觉你指 runtime 仅仅是字面的“运行过程中”。

    在 cpu 执行你所指的” runtime “之前实际上是有动态内存分配的吧。比如你所说的”启动过程锁定 900MB 内存”。
    std::string 的 allocator 用到你预分配的 900M,自然也没有显式的动态内存分配了。

    要真正做到应用程序直接控制内存,也许这个可以 [en.wikipedia.org/wiki/Exokernel] 做到。
    codehz
        7
    codehz  
       2019-02-06 12:56:14 +08:00
    @dangyuluo #5 所以你可以直接重载 operator new/operator delete/calloc/realloc/malloc/free/aligned_alloc 然后弄个专用分配器(
    FrankHB
        8
    FrankHB  
       2019-02-06 15:10:51 +08:00
    @codehz 不是重载是直接重定义。
    operator new/delete 以外要链接器魔法,或者自己提供标准库实现。
    CRVV
        9
    CRVV  
       2019-02-06 19:26:44 +08:00 via Android
    尽量提高实时性,可以运行在硬实时系统上

    这句话前后矛盾
    硬实时系统上没有尽量一说

    感觉你们低估了 real time 这件事情,随意设定了一些自以为重要的标准
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2918 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 03:16 · PVG 11:16 · LAX 19:16 · JFK 22:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.