Проблема оптимизации с атрибутом функции [[gnu :: pure]] и потоками - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть программа, которая почти сразу заканчивается на -O0 на gcc, но навсегда зависает с 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;
}

Isэто ошибка компилятора?Отсутствие документации для правильных предостережений для использования [[gnu::pure]]?Неправильное прочтение документации для [[gnu::pure]] такое, что я кодировал ошибку?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Как оказалось, я неправильно прочитал документацию.Из онлайн-документации об атрибуте pure в gcc:

Чистый атрибут запрещает функции изменять состояние программы, которое можно наблюдать с помощью других средств, кроме проверки возвращаемого значения функции.Однако функции, объявленные с атрибутом pure, могут безопасно считывать любые энергонезависимые объекты и изменять значение объектов таким образом, чтобы это не влияло на их возвращаемое значение или наблюдаемое состояние программы.

и другой абзац:

Некоторыми распространенными примерами чистых функций являются strlen или memcmp. Интересными не чистыми функциями являются функции с бесконечными циклами или функции, зависящие от энергозависимой памяти или другого системного ресурса , которые могут изменяться между последовательными вызовами (например, стандартная функция C feof в многопоточной среде).

Эти два абзаца проясняют, что я лгал компилятору, и написанная мною функция не квалифицируется как «чистая», поскольку она зависит от переменной, которая может измениться в любое время.

Причина, по которой я задал этот вопрос, заключается в том, что ответы на этот вопрос: __ attribute __ ((const)) vs __attribute __ ((pure)) в GNU C вообще не решали эту проблему (ввремя я все равно задал свой вопрос).А в недавнем еженедельном выпуске C ++ был комментарий с вопросом о потоках и чистых функциях.Таким образом, ясно, что существует некоторая путаница.

Таким образом, критерий для функции, которая соответствует этому маркеру, заключается в том, что она не должна изменять глобальное состояние, хотя ей разрешено читать его.Но, если он читает глобальное состояние, ему не разрешается читать любое глобальное состояние, которое можно считать «изменчивым», и это лучше всего понимать как состояние, которое может измениться между двумя сразу последовательными вызовами функции, т. Е. Если это состояниечтение может измениться в такой ситуации:

f();
f();
0 голосов
/ 22 февраля 2019

У меня есть программа, которая почти сразу же заканчивается на -O0 на gcc, но навсегда зависает с gcc и -O3

Да, потому что программа компилируется до бесконечностицикл при включенной оптимизации.

Это ошибка компилятора?Отсутствие документации для правильных предостережений для использования [[gnu::pure]]?Неправильное прочтение документации для [[gnu::pure]] такое, что я кодировал ошибку?

Это не ошибка компилятора.get_value_plus не является pure функцией:

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

, поскольку возвращаемое значение может измениться в любое время (для того же x), поскольку ожидается, что val будет изменен другимthread.

Однако компилятор, полагая, что get_value_plus всегда будет возвращать одно и то же значение, выполнит CSE и, следовательно, примет следующее:

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

canзаписать как:

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

Что, действительно, представляет собой бесконечный цикл независимо от значения x:

while (true);

Пожалуйста, см. GCCдокументация по pure для получения более подробной информации.

В общем, избегайте использования подсказок по оптимизации, если они не совсем понятны!

В этом случае недоразумение заключается в том, что функции pureразрешено чтение глобальной памяти, но только если эта память изменяется от вызова к вызову кем-то, кроме вызывающей стороны:

Однако функции, объявленные с атрибутом pure, могут безопасно читать любые энергонезависимые объекты,и изменить значение объектов таким образом,это не влияет на их возвращаемое значение или наблюдаемое состояние программы.

...