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

时间:2010-09-24 09:34:38

标签: c++ volatile

问题在于: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);
}

目的是在看到volatile关键字时,编译器不会尝试消除对std::fill_n()的调用。

volatile关键字总是会阻止编译器修改代码吗?

4 个答案:

答案 0 :(得分:3)

从最后的C ++ 0x草案[intro.execution]:

  

8对a的要求最低   符合要求的实施是:

     

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

     

[...]

     

12访问由a指定的对象   volatile glvalue(3.10),修改一个   对象,调用库I / O.   函数,或调用一个函数   这些操作中的任何一个都是   副作用,[...]

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

答案 1 :(得分:2)

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

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

  

符合实施的最低要求是:

     
      
  • 在序列点处,易失物体在先前评估完成的意义上是稳定的   后来的评估尚未发生。   [...]
  •   

  

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

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

  

符合实施的最低要求是:

     
      
  • 严格按照抽象机的规则评估对volatile对象的访问。[...]
  •   
     

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

虽然有人可能认为“易变数据”可能意味着“由易失性左值访问的数据”,这仍然相当长,但C ++ 0x措辞消除了对代码的所有疑虑,并明确允许实现对其进行优化程。

但正如人们向我指出的那样,在实践中这可能无关紧要。优化这种事情的编译器很可能会违背程序员的意图(为什么有人会有一个指向volatile的指针),所以可能会包含一个bug。尽管如此,我还是经验丰富的编译器供应商在他们面临关于他们过度激进的优化的错误报告时引用了这些段落。最后,volatile是固有的特定平台,无论如何你应该仔细检查结果。

答案 2 :(得分:1)

您要删除的内存内容可能已经从CPU /内核的内部缓存刷新到RAM,其他CPU可以继续查看它。覆盖后,您需要使用互斥/内存屏障指令/原子操作或其他东西来触发与其他内核的同步。在实践中,你的编译器可能会在调用任何外部函数之前执行此操作(谷歌Dave Butenhof在多线程中使用volatile的可疑实用程序的帖子),所以如果你之后很快就做了线程,那么这不是一个主要问题。总之:不需要挥发性。

答案 3 :(得分:1)

符合标准的实现可能会随意延迟任何易失性读写的实际性能,直到易失性读取的结果会影响易失性写入或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的所有偶数索引值{1}}或写入vol1。即使编译器可能需要先从vol2读取数据,然后才能对vol1进行以下写入操作,但编译器可能能够将这两个操作推迟到运行了无限制的代码量之后