Информация об изменениях

Сообщение Re[12]: оффтоп+volatile от 14.09.2009 15:26

Изменено 17.05.2015 13:43 netch80

+ gcc asm memory clobber

Здравствуйте, ДимДимыч, Вы писали:

N>>какой "дефолт" вызвала всего лишь попытка перейти на GCC 4.4

ДД>Так GCC 4.4 уже под GPLv3 идет, разве не этот факт вызвал "дефолт"?

Нет, речь о другом. Linux ломается с каждой новой версией, потому что что-то в мелочах меняется (чаще всего стили оптимизации), но в случае 4.4 количество поломок было особенно крупным.

Я разверну мысль из предыдущих сообщений. Отсутствие volatile склоняет компилятор к возможности оптимизировать доступ к переменной его кэшированием (прочитать или записать меньшее количество раз, чем надо), но есть чёткие границы, через которые он не переходит. Это границы тел функций (не в случае инлайнинга) и границы — вызовы функций, но второе — только если он гарантированно не может сам понять, что происходит в этой функции.

Представим себе систему с кооперативной многозадачностью, когда запрет переключения реализуется глобальной переменной. Если установка и снятие такого "лока" сделано напрямую в функции, то компилятор вполне вероятно захочет это оптимизировать. Это код следующего вида:

extern volatile nosched;

...
  foo = 1;
  nosched = 0; // разрешили переключение
  *bar = 1; // что-то снаружи стронули и возможно нам дали другие данные
  nosched = 1; // запретили переключение - работаем с данными
  if (foo != 3) {
    ...
  }


тут компилятор захочет оптимизировать и наверняка это сделает — именно foo ничем не защищено против этого.

А вот тут мы ему явно ставим барьер:

  foo = 1;
  unlock_scheduler();
  *bar = 1;
  lock_scheduler();
  if (foo != 3) {
    ...
  }


Главное здесь — чтобы компилятор при сборке данного модуля _не знал_, что такое эти lock_scheduler() и unlock_scheduler(), или для них была явная метка "что-то меняют, а что — ХЗ" (как в asm volatile). Тогда компилятор остережётся кэшировать foo — он не знает, менялось оно в этих вызовах или нет. Незнание реализуется ссылкой на другой модуль и его отделённой компиляцией.

Именно это и является той защитой, которая не позволяет коду в Linux сходить с ума и которая работает и против gcc, и вообще против любого нормального компилятора. И, как видим, в том тексте про это — ни слова. Есть, однако, одна фраза про "it acts as a memory barrier", но она неоднозначна: понятие memory barrier давно закреплено за другим явлением — средствами предотвращения нежелательного переупорядочения операций процессора. И при этом не сказано, что защитой против оптимизации компилятором является любая функция неизвестного компилятору смысла, а не только функции работы с семафорами и спинлоками (хотя именно эти содержат полный комплект необходимых защит, не только против компиляторной оптимизации).

Ну а почему я это связал с версией gcc — потому что качество кода и качество документации тут примерно одинаково по сути, а именно — резко неровно. Рядом с идеальным результатом может находиться кусок гнилого бреда. На что собственно и нарвались в обоих случаях.

Фух... надеюсь, теперь понятно.;)
Re[12]: оффтоп+volatile
Здравствуйте, ДимДимыч, Вы писали:

N>>какой "дефолт" вызвала всего лишь попытка перейти на GCC 4.4

ДД>Так GCC 4.4 уже под GPLv3 идет, разве не этот факт вызвал "дефолт"?

Нет, речь о другом. Linux ломается с каждой новой версией, потому что что-то в мелочах меняется (чаще всего стили оптимизации), но в случае 4.4 количество поломок было особенно крупным.

Я разверну мысль из предыдущих сообщений. Отсутствие volatile склоняет компилятор к возможности оптимизировать доступ к переменной его кэшированием (прочитать или записать меньшее количество раз, чем надо), но есть чёткие границы, через которые он не переходит. Это границы тел функций (не в случае инлайнинга) и границы — вызовы функций, но второе — только если он гарантированно не может сам понять, что происходит в этой функции.

Представим себе систему с кооперативной многозадачностью, когда запрет переключения реализуется глобальной переменной. Если установка и снятие такого "лока" сделано напрямую в функции, то компилятор вполне вероятно захочет это оптимизировать. Это код следующего вида:

extern volatile nosched;

...
  foo = 1;
  nosched = 0; // разрешили переключение
  *bar = 1; // что-то снаружи стронули и возможно нам дали другие данные
  nosched = 1; // запретили переключение - работаем с данными
  if (foo != 3) {
    ...
  }


тут компилятор захочет оптимизировать и наверняка это сделает — именно foo ничем не защищено против этого.

А вот тут мы ему явно ставим барьер:

  foo = 1;
  unlock_scheduler();
  *bar = 1;
  lock_scheduler();
  if (foo != 3) {
    ...
  }


Главное здесь — чтобы компилятор при сборке данного модуля _не знал_, что такое эти lock_scheduler() и unlock_scheduler(), или для них была явная метка "что-то меняют, а что — ХЗ" (как в asm volatile). Тогда компилятор остережётся кэшировать foo — он не знает, менялось оно в этих вызовах или нет. Незнание реализуется ссылкой на другой модуль и его отделённой компиляцией.

(UPDATE: ещё штатный метод для gcc:
asm volatile("":::"memory")

— явное указание, что встраиваемый код изменил содержимое памяти и нельзя через его границу оптимизировать доступ. Должны быть аналоги для других компиляторов.)

Именно это и является той защитой, которая не позволяет коду в Linux сходить с ума и которая работает и против gcc, и вообще против любого нормального компилятора. И, как видим, в том тексте про это — ни слова. Есть, однако, одна фраза про "it acts as a memory barrier", но она неоднозначна: понятие memory barrier давно закреплено за другим явлением — средствами предотвращения нежелательного переупорядочения операций процессора. И при этом не сказано, что защитой против оптимизации компилятором является любая функция неизвестного компилятору смысла, а не только функции работы с семафорами и спинлоками (хотя именно эти содержат полный комплект необходимых защит, не только против компиляторной оптимизации).

Ну а почему я это связал с версией gcc — потому что качество кода и качество документации тут примерно одинаково по сути, а именно — резко неровно. Рядом с идеальным результатом может находиться кусок гнилого бреда. На что собственно и нарвались в обоих случаях.

Фух... надеюсь, теперь понятно.;)