Меня немного смущает реализация функции void DoNotOptimize
Google Benchmark Framework (определение отсюда):
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
asm volatile("" : : "r,m"(value) : "memory");
}
template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
#if defined(__clang__)
asm volatile("" : "+r,m"(value) : : "memory");
#else
asm volatile("" : "+m,r"(value) : : "memory");
#endif
}
Таким образом, он материализует переменную и, если она не является константой, также говорит компилятору забыть что-либо о ее предыдущем значении. ("+r"
- это операнд RMW).
И также всегда использует "memory"
clobber, который является барьером компилятора против переупорядочивания загрузок / хранилищ, то есть убедитесь, что все глобально доступные объекты имеют свою память, синхронизированную с абстрактной машиной C ++, и предполагают, что они также могут были изменены.
Я далек от того, чтобы быть экспертом в низкоуровневом коде, но насколько я понимаю реализацию, функция служит барьером для чтения / записи. Так что - в основном - он гарантирует, что переданное значение находится либо в регистре, либо в памяти.
Хотя это кажется вполне разумным, если я хочу сохранить результат функции (который должен быть протестирован) в целом, я немного удивлен степенью свободы, оставленной для компилятора.
Насколько я понимаю, данный код состоит в том, что компилятор может вставлять точку материализации всякий раз, когда вызывается DoNotOptimize
, что повлечет за собой значительные накладные расходы при повторном выполнении (например, в цикле). Когда значение не должно оптимизироваться, это просто одно скалярное значение, кажется, достаточно, если компилятор гарантирует, что значение находится в регистре.
Разве не было бы хорошей идеей различать указатели и не указатели, например:
template< class T >
inline __attribute__((always_inline))
void do_not_optimize( T&& value ) noexcept {
if constexpr( std::is_pointer_v< T > ) {
asm volatile("":"+m"(value)::"memory");
} else {
asm volatile("":"+r"(value)::);
}
}