Intereting Posts
Malloc, strlen, strcat Программно проверить, выполняется ли процесс в фоновом режиме Декомпиляция ARM asm обратно на C Связь между символом и кодом ASCII? typedef struct в заголовке и указателе разыменования до неполного типа Итеративный алгоритм Фибоначчи дает мне неправильный результат после фила (47) Свободная выделенная память перед возвратом функции Необходимость memset с ‘\ 0’, в примере с игрушкой Существуют ли какие-либо проблемы при использовании чистых C (неclassифицированных) функций в приложении C ++? Что будет возвращать функция, если нет явного «return» Выделение указателя, не связанного с привязкой, который содержит адрес объекта (массив массива) Почему malloc (0) возвращает действительный адрес памяти? Какая польза? Компиляция с GCC на windows 7: \ mingw32 \ bin \ ld.exe: невозможно открыть выходной файл a.exe Что происходит быстрее: while (1) или while (2)? module_init не показывает printk, что я хочу, чтобы он

gcc для функций init-on-first-use

Я использовал gcc const и pure атрибуты для функций, которые возвращают указатель на «постоянные» данные, которые выделяются и инициализируются при первом использовании, т. Е. Когда функция будет возвращать одинаковое значение при каждом вызове. В качестве примера (не мой пример использования, но хорошо известный пример), мы рассмотрим функцию, которая выделяет и вычисляет таблицы поиска триггера при первом вызове и просто возвращает указатель на существующие таблицы после первого вызова.

Проблема: мне сказали, что это неправильное использование, поскольку эти атрибуты запрещают побочные эффекты и что компилятор может даже полностью оптимизировать вызов в некоторых случаях, если возвращаемое значение не используется. Является ли мое использование атрибутов const / pure безопасным или есть другой способ сообщить компилятору, что N>1 вызывает функцию, эквивалентно 1 вызову функции, но этот вызов функции не эквивалентен 0 вызовам к функции? Или, другими словами, функция только имеет побочные эффекты при первом вызове?

    Я говорю, что это правильно, основываясь на моем понимании чистого и const , но если у кого-то есть точное определение двух, пожалуйста, говорите. Это становится сложно, потому что в документации GCC не указано, что именно означает, что функция имеет «никаких эффектов, кроме возвращаемого значения» (для чистого ) или «не проверять значения, кроме их аргументов» (для константы ). Очевидно, что все функции имеют некоторые эффекты (они используют процессорные циклы, модифицируют память) и исследуют некоторые значения (код функции, константы).

    «Побочные эффекты» должны определяться с точки зрения семантики языка программирования C, но мы можем догадаться, что означают люди ССЗ, основанные на назначении этих атрибутов, что должно позволить дополнительные оптимизации (по крайней мере, это то, что я предположим, что они для).

    Простите меня, если некоторые из следующих слишком просты …

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

     z = f(x); y = f(x); 

    будет выглядеть так:

     z = y = f(x); 

    Или полностью исключается, если z и y не используются.

    Поэтому я думаю, что рабочее определение «чистого» – это «любая функция, которую можно назвать меньше раз, не изменяя семантику программы». Однако вызовы функций не могут быть перемещены, например,

     size_t l = strlen(str); // strlen is pure *some_ptr = '\0'; // Obviously, strlen can't be moved here... 

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

     // Assuming x and y not aliased, sin can be moved anywhere *some_ptr = '\0'; double y = sin(x); *other_ptr = '\0'; 

    Поэтому я думаю, что рабочее определение «const» – это «любая функция, которая может быть вызвана в любой точке без изменения семантики программы». Однако существует опасность:

     __attribute__((const)) double big_math_func(double x, double theta, double iota) { static double table[512]; static bool initted = false; if (!initted) { ... initted = true; } ... return result; } 

    Поскольку это const, компилятор может изменить его …

     pthread_mutex_lock(&mutex); ... z = big_math_func(x, theta, iota); ... pthread_mutex_unlock(&mutex); // big_math_func might go here, if the compiler wants to 

    В этом случае его можно было бы вызывать одновременно с двух процессоров, хотя он появляется только внутри критического раздела вашего кода. Тогда процессор может решить отложить изменения в table после того, как уже перешла на initted , что является плохими новостями. Вы можете решить это с помощью барьеров памяти или pthread_once .

    Я не думаю, что эта ошибка когда-либо появится на x86, и я не думаю, что она появляется во многих системах, у которых нет нескольких физических процессоров (не ядер). Таким образом, он будет работать отлично на века, а затем внезапно произойдет на компьютере с двойной розеткой POWER.

    Заключение . Преимущество этих определений состоит в том, что они дают понять, какие изменения компилятор может сделать при наличии этих атрибутов, что (я думаю,) несколько расплывчато в документах GCC. Недостатком является то, что неясно, что это определения, используемые командой GCC.

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

    Редактировать: я не смог принудить GCC или Clang к перемещению одиночного вызова __attribute__((const)) через другой вызов функции, но вполне возможно, что в будущем произойдет что-то подобное. Помните, когда -fstrict-aliasing стал дефолтом, и у всех вдруг появилось намного больше ошибок в их программах? Это то, что делает меня осторожным.

    Мне кажется, что когда вы отмечаете функцию __attribute__((const)) , вы обещаете компилятору, что результат вызова функции тот же, независимо от того, когда он вызывается во время выполнения вашей программы, если параметры являются так же.

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

     __attribute__((const)) extern int const_func(int x); int func(int x) { int y1, y2; y1 = const_func(x); pthread_mutex_lock(&mutex); y2 = const_func(x); pthread_mutex_unlock(&mutex); return y1 + y2; } 

    Компилятор переводит это в следующий код (из сборки):

     int func(int x) { int y; y = const_func(x); pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); return y * 2; } 

    Обратите внимание, что это не произойдет только с __attribute__((pure)) , атрибут const и только атрибут const вызывает это поведение.

    Как вы можете видеть, вызов внутри критической секции исчез. Кажется довольно произвольным, что предыдущий вызов был сохранен, и я не хотел бы утверждать, что компилятор в какой-то будущей версии не примет иного решения о том, какой вызов сохранить или может ли он переместить вызов функции где-то иначе полностью.

    Заключение 2. Протестируйте внимательно, потому что, если вы не знаете, какие обещания вы делаете компилятору, будущая версия компилятора может вас удивить.