улучшить атомарное чтение из InterlockedCompareExchange ()

Предполагается, что архитектура ARM64 или x86-64.

Я хочу убедиться, что эти два эквивалентны:

  1. a = _InterlockedCompareExchange64((__int64*)p, 0, 0);
  2. MyBarrier(); a = *(volatile __int64*)p; MyBarrier();

куда MyBarrier() это барьер памяти (подсказка) уровня компилятора, например __asm__ __volatile__ ("" ::: "memory"),
Таким образом, метод 2 должен быть быстрее, чем метод 1.

я слышал, что _Interlocked() Функции также подразумевают барьер памяти как на уровне компилятора, так и на уровне оборудования.

Я слышал, что считанные (правильно выровненные) внутренние данные являются атомарными на этих архитектурах, но я не уверен, что метод 2 может быть широко использован?

(ps. потому что я думаю, что CPU будет обрабатывать зависимость от данных автоматически, поэтому аппаратный барьер здесь не особо рассматривается.)

Спасибо за любой консультировать/коррекция на этом.


Вот несколько тестов на Ivy Bridge (ноутбук i5).

(Петли 1E + 006: 27ms):

; __int64 a = _InterlockedCompareExchange64((__int64*)p, 0, 0);
xor eax, eax
lock cmpxchg QWORD PTR val$[rsp], rbx

(Петли 1E + 006: 27ms):

; __faststorefence(); __int64 a = *(volatile __int64*)p;
lock or DWORD PTR [rsp], 0
mov rcx, QWORD PTR val$[rsp]

(Петли 1E + 006: 7мс):

; _mm_sfence(); __int64 a = *(volatile __int64*)p;
sfence
mov rcx, QWORD PTR val$[rsp]

(Петли 1E + 006: 1.26ms, не синхронизируется?)

; __int64 a = *(volatile __int64*)p;
mov rcx, QWORD PTR val$[rsp]

2

Решение

Чтобы вторая версия была функционально эквивалентной, вам, очевидно, нужны атомарные 64-битные операции чтения, что верно для вашей платформы.

Тем не мение, _MemoryBarrier() не является «подсказкой компилятору». _MemoryBarrier() на x86 предотвращает переупорядочение компилятора и процессора, а также обеспечивает глобальную видимость после записи. Вам также, вероятно, нужно только первое _MemoryBarrier()второй можно заменить на _ReadWriteBarrier() если a также является разделяемой переменной, но вам это даже не нужно, так как вы читаете через изменчивый указатель, который предотвратит переупорядочение компилятора в MSVC.

Когда вы создаете эту замену, вы в основном получаете тот же результат:

// a = _InterlockedCompareExchange64((__int64*)&val, 0, 0);
xor eax, eax
lock cmpxchg QWORD PTR __int64 val, r8 ; val

// _MemoryBarrier(); a = *(volatile __int64*)&val;
lock or DWORD PTR [rsp], r8d
mov rax, QWORD PTR __int64 val ; val

Выполнение этих двух циклов на моем ноутбуке i7 Ivy Bridge дает одинаковые результаты в пределах 2-3%.

Однако с двумя барьерами памяти «оптимизированная версия» на самом деле примерно в 2 раза медленнее.

Итак, лучший вопрос: Почему вы используете _InterlockedCompareExchange64 совсем? Если вам нужен атомарный доступ к переменной, используйте std::atomicи оптимизирующий компилятор должен скомпилировать его в наиболее оптимизированную версию для вашей архитектуры и добавить все необходимые барьеры для предотвращения переупорядочения и обеспечения согласованности кэша.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]