2013-12-03 1 views
5

Я хочу автонастройки мой код, и для этого нужно измерить время, необходимое для некоторой части кода, напримерНужно ли мне волатильность при выборе фрагмента кода?

auto t0 = std::chrono::high_resolution_clock::now(); 
section_of_code_to_be_timed(arguments); 
auto dt = std::chrono::duration_cast<std::chrono::nanoseconds> 
     (std::chrono::high_resolution_clock::now()-t0).counts(); 
// ... using dt to tweak auto-tuning parameters 

Что я беспокоюсь о том, что компилятор может переставить вызовы к std::chrono::high_resolution_clock::now() и section_of_code_to_be_timed(), что позволяет аннулировать мое измерение времени. Является ли это веским беспокойством? Если да, могу ли я предотвратить его с объявлением t0volatile или иным образом (как)?

(я заметил, что я мог бы использовать RAII идиому, подобный std::lock_guard, который, кажется, не использовать volatile ...)

+3

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

+0

@ Михаил Я ** не профилирую ** мой код (как видно из Q). 'section_of_code_to_be_timed' не пуст и не может быть оптимизирован. Производительность 'section_of_code_to_be_timed()' зависит от параметров времени выполнения, и я не могу профилировать все возможные комбинации значений. Следовательно, я хочу использовать автонастройку. – Walter

+0

Из любопытства, на какой платформе вы делаете такое умение? –

ответ

4

Формально или практически? Формально звонки на std::chrono::high_resolution_clock::now() не наблюдаются в поведении, поэтому компилятор может перенастроить их так, как он хочет. Практически компиляторы будут относиться к ним как к наблюдаемому поведению, , так что у вас не будет никаких проблем на этот счет. С другой стороны, вы должны сделать что-то, чтобы section_of_code_to_be_timed действительно что-то делал. (Я часто сделать его виртуальным членом класса, который вводит достаточно косвенность дурачить большинство компиляторов. И в самой функции, я могу гарантировать, что это дает результат, который будет виден снаружи функции.)

Обратите внимание, что в любом случае, volatile не имеет значения. Обо всем этом гарантирует, что t0 и dt записаны в правильном порядке (и на практике часто это не гарантирует); он не делает гарантирует около section_of_code_to_be_timed по отношению к t0 или dt.

+0

'Формально, вызовы на std :: chrono :: high_resolution_clock :: now() не являются наблюдаемым поведением' Это правда? Если он вызывает системную процедуру или выполняет какую-либо встроенную ASM, не считается ли это наблюдаемым поведением так же, как это делает 'cout'? – Mikhail

+0

@Mikhail Кажется, я помню какой-то момент в прошлом, когда все системные вызовы (а также системный вызов, лежащий в основе 'std :: chrono :: high_resolution :: clock :: now()'), считались наблюдаемым поведением, но текущий Стандарт относится только к данным, записанным в файл. (С другой стороны: если он выводит 'dt', перемещение кода может, безусловно, повлиять на этот вывод. Но я не думаю, что это имеет значение, любая оптимизация также может повлиять на результат, и традиционно время выполнения не считается наблюдаемым, даже если пользователи могут их явно видеть.) –

1

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

Эти две концепции не связаны друг с другом. В частности, не путайте volatile с ключевым словом, используемым на других языках, чтобы сделать переменные атомными. В C++ volatile не имеет ничего общего с взаимодействиями потоков.

1

Вы можете вставить «барьер компилятора»: к сожалению, такие конструкции не являются стандартными.

Под gcc (и clang?) __asm__ __volatile__ ("" ::: "memory") будет действовать как полный барьер (примечание: барьер компилятора не барьер памяти).

У меня нет его под рукой, но http://msdn.microsoft.com/en-us/library/f20w0x5e.aspx указывает, что _ReadWriteBarrier(); должен оказывать аналогичный эффект при VC++ (хотя он устарел). Другие компиляторы могут или не могут поддерживать свои собственные эквиваленты.

И наконец, C++ 11 обеспечивает atomic_signal_fence, что выглядит многообещающим, но мне непонятно, правильно ли оно.