代码之家  ›  专栏  ›  技术社区  ›  sharptooth

使用“指向易失性的指针”是否始终阻止编译器优化?

  •  6
  • sharptooth  · 技术社区  · 14 年前

    问题是: your program temporarily uses some sensitive data and wants to erase it when it's no longer needed . 使用 std::fill() 本身并不总是有帮助-编译器可能会决定 内存块稍后无法访问,因此删除它是浪费时间 消除擦除代码。

    User ybungalobill 建议 using volatile keyword :

    {
      char buffer[size];
      //obtain and use password
      std::fill_n( (volatile char*)buffer, size, 0);
    }
    

    其目的是在看到 不稳定的 关键字编译器不会尝试消除对 std::fill_n() .

    威尔 不稳定的 关键字总是阻止编译器修改代码消除这样的内存?

    4 回复  |  直到 6 年前
        1
  •  2
  •   Johannes Schaub - litb    14 年前

    编译器可以自由地优化代码 因为 buffer 不是易失性对象 .

    该标准只要求编译器严格遵守易失性对象的语义。下面是C++ 03所说的

    对合格实施的最低要求是:

    • 在序列点,易失性对象是稳定的,因为以前的评估是完整的,并且 尚未进行后续评估。 […]

    抽象机的可观察行为是它对易失性数据的读写顺序,以及 调用库I/O函数

    在您的示例中,使用易失性lvalue对非易失性对象进行读写。C++ 0x删除了上面引用的第二个文本,因为它是多余的。c++只是说

    对合格实施的最低要求是:

    • 对易失性对象的访问严格按照抽象机的规则进行评估。…]

    这些统称为 可观察行为 程序的。

    虽然有人会说“易失性数据”可能意味着“由易失性LValk访问的数据”,这仍然是一个相当大的扩展,C++0X措辞消除了您对代码的所有疑虑,并清楚地允许实现优化它。

    但正如人们向我指出的,这在实践中可能并不重要。优化这样一个东西的编译器很可能违背程序员的意图(为什么有人会有一个指向volatile的指针,否则),因此可能包含一个bug。不过,我还是有经验的编译器供应商在遇到关于他们过度激进优化的错误报告时引用了这些段落。最后, volatile 是固有的平台特定的,您无论如何都应该对结果进行双重检查。

        2
  •  3
  •   Yakov Galka    14 年前

    从最后的C++0X草案[介绍执行]:

    8对A的最低要求 符合性实施包括:

    对易失性对象的访问 严格按照 抽象机器的规则。

    […]

    12访问由 volatile glvalue(3.10),修改 对象,调用库I/O 函数,或调用 所有这些操作都是 副作用,[…]

    因此,即使您提供的代码也不能优化。

        3
  •  1
  •   Tony Delroy    14 年前

    您希望删除的内存内容可能已经从CPU/内核的内部高速缓存中刷新到RAM,其他CPU可以继续看到它。覆盖之后,您需要使用互斥/内存屏障指令/原子操作或其他东西来触发与其他核心的同步。实际上,编译器在调用任何外部函数(Google Dave Butenhof关于volatile在多线程中的可疑实用程序的文章)之前可能会这样做,因此,如果稍后线程就这么做了,那么这不是主要问题。总之:不需要挥发性。

        4
  •  1
  •   supercat    6 年前

    一致性实现可以在空闲时推迟任何可变读写的实际性能,直到可变读的结果会影响可变写操作或I/O操作的执行。

    例如,假设如下:

    volatile unsigned char vol1,vol2;
    extern unsigned char res[1000];
    void test(int scale)
    {
      unsigned char ch;
    
      for (int 0=0; i<10000; i++)
      {
        res[i] = i*vol1*scale;
        vol2 = res[i];
      }
    }
    

    一致性编译器可以根据自己的选择检查 scale 是128的倍数,如果是,则清除 res 在阅读之前 vol1 或者写信给 vol2 . 即使编译器需要从 VLU1 在它可以执行以下写入操作之前 VO2 编译器可以将这两个操作都延迟到运行了基本上无限量的代码之后。