[[GNU :: pure]]功能属性和线程的优化问题



我有一个程序,该程序几乎立即用GCC上的-O0完成,但使用GCC和-O3永远悬挂。如果我删除[[gnu::pure]]函数属性,它也会立即退出,即使该函数不会修改全局状态。该程序在三个文件中:

thread.hpp

#include <atomic>
extern ::std::atomic<bool> stopthread;
extern void threadloop();
[[gnu::pure]] extern int get_value_plus(int x);

thread.cpp

#include <thread>
#include <atomic>
#include "thread.hpp"
namespace {
::std::atomic<int> val;
}
::std::atomic<bool> stopthread;
void threadloop()
{
   while (!stopthread.load())
   {
      ++val;
   }
}
[[gnu::pure]] int get_value_plus(int x)
{
   return val.load() + x;
}

main.cpp

#include <thread>
#include "thread.hpp"
int main()
{
   stopthread.store(false);
   ::std::thread loop(threadloop);
   while ((get_value_plus(5) + get_value_plus(5)) % 2 == 0)
      ;
   stopthread.store(true);
   loop.join();
   return 0;
}

这是编译器错误吗?缺乏适当的警告使用[[gnu::pure]]的文档?误读了[[gnu::pure]]的文档,以使我编码一个错误?

我有一个程序,该程序几乎可以立即使用GCC上的-O0完成,但是与GCC和-O3

一起悬挂

是的,因为启用了优化时,该程序会汇编为无限循环。

这是编译器错误吗?缺乏适当的警告文档来使用[[gnu::pure]]?误读了[[gnu::pure]]的文档,以使我编码一个错误?

它不是编译器错误。get_value_plus不是pure功能:

[[gnu::pure]] int get_value_plus(int x)
{
    return val.load() + x;
}

由于返回值可以随时更改(对于同一x),因为val有望通过其他线程修改。

编译器认为get_value_plus总是会返回相同的值,将执行CSE,因此会假设以下内容:

while ((get_value_plus(5) + get_value_plus(5)) % 2 == 0);

可以写为:

int x = get_value_plus(5);
while ((x + x) % 2 == 0);

实际上,这是一个无限的循环,无论 x的值如何:

while (true);

有关更多详细信息,请参阅pure上的GCC文档。

通常,避免使用优化提示,除非它们得到充分了解!

在这种情况下,误解是允许 pure函数读取全局内存,但如果该内存从呼叫者以外的其他人的呼叫发生变化:

但是,用纯属性声明的函数可以安全地读取任何非易失性对象,并以不影响其返回值或程序的可观察状态的方式修改对象的值。

事实证明,我误读了文档。从GCC中的pure属性的在线文档:

纯属属性禁止函数修改程序的状态,除了检查函数的返回值以外,该函数可观察到。但是,用纯属性声明的函数可以安全地读取任何 non-ol-lodlatile 对象,并以不影响其返回值或程序的可观察状态的方式修改对象的值。/blockquote>

和不同的段落:

纯函数的一些常见示例是strlen或memcmp。有趣的非岩石功能是具有无限循环或取决于波动性内存或其他系统资源的功能,可能会在连续调用之间发生变化(例如,在多线程环境中标准C FEOF函数)。/blockquote>

这两个段落清楚地表明我一直在骗编译器,而我写的功能并不符合"纯"的资格,因为它取决于可能随时改变的变量。

我问这个问题的原因是因为这个问题的答案是:__ Atribute __((const))vs ___________________________________am((pure))根本没有解决这个问题(无论如何我还是问我的问题)。最近的C 每周一集对线程和纯粹的功能进行了评论。所以很明显那里有一些混乱。

因此,符合此标记的函数的标准是它必须修改全局状态,尽管它可以读取它。但是,如果它确实阅读了全球状态,则不允许阅读任何可以认为"挥发性"的全球状态,这最好将其理解为可能在两个立即连续呼叫的函数之间改变的状态,即,如果状态是否是在这样的情况下,阅读可能会改变:

f();
f();

相关内容

  • 没有找到相关文章

最新更新