Можно ли использовать расслабленный порядок памяти для наблюдения за состоянием? - PullRequest
1 голос
/ 01 июля 2019
std::atomic<bool> b;

void f()
{
    // block A
    if(b.load(std::memory_order_relaxed))
    {
        // block B
    }
    // block C
}

void g()
{
    // block B
    b.store(true, std::memory_order_release);
}

Теоретически блок B должен выполняться только в том случае, если атомная нагрузка возвращает true,
но возможно ли, что часть блока B может быть переупорядочена перед загрузкой? store с порядком освобождения памяти гарантирует, что все операции в блоке B являются видимым побочным эффектом, но применимо ли это, если load является расслабленной операцией?

Ответы [ 3 ]

3 голосов
/ 01 июля 2019

Корпорация Intel рекомендует делать расслабленную нагрузку перед попыткой блокировки . Повышение эффективности и быстродействия Sleep Loops :

ATTEMPT_AGAIN:
    if (!acquire_lock())
    {
        /* Spin on pause max_spin_count times before backing off to sleep */
        for(int j = 0; j < max_spin_count; ++j)
        {
            /* pause intrinsic */
            _mm_pause();
            if (read_volatile_lock()) // <--- relaxed load
            {
                if (acquire_lock())
                {
                    goto PROTECTED_CODE;
                }
            }
        }
        /* Pause loop didn't work, sleep now */
        Sleep(0);
        goto ATTEMPT_AGAIN;
    }
PROTECTED_CODE:
    get_work();
    release_lock();
    do_work();

acquire_lock использует семантику получения, так что расслабленная нагрузка нене переупорядочивается после acquire_lock.

Обратите внимание, однако, сначала он пытается безоговорочно заблокироваться перед выполнением цикла «занят-ожидание» с расслабленной загрузкой.

3 голосов
/ 02 июля 2019

У вас есть два block B в вашем примере.Я говорю о функции загрузки void f().

Возможно ли, что часть блока B может быть переупорядочена перед загрузкой?

Да.Компилятор может поднимать нагрузки из тела if() и выполнять их до b.load.Это может произойти, если оба блока B и C читают одну и ту же неатомарную переменную.

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

В частности, спекуляция ветвления (то есть прогноз ветвления + спекулятивное выполнение не по порядку) позволит ЦПУ начать выполнение блока B до того, как b.load() даже запустится.

Вы не можете зависетьв отношении "причинности" или любых других рассуждений, таких как "ему нужно знать результат b.load(), прежде чем он сможет знать, что выполнять дальше".

Или компилятор может потенциально выполнить if-преобразование if() в код без ответвлений, если в блоке В нет хранилищ. Тогда вполне очевидно, что он может переупорядочить с неатомарными нагрузками или другими ослабленными или получающими нагрузками, которые были в блоке В и С.

(Помните, что acq / rel являются односторонними барьерами.)


Подобные рассуждения (основанные на том, что могут делать настоящие компиляторы и процессоры) могут быть полезны, чтобы доказать, что что-то не безопасный. Но будьте осторожны, если вы пойдете другим путем: рассуждение, основанное на «безопасном для компилятора, о котором я знаю», не всегда означает «безопасное в переносимом ISO C ++» .

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

Поэтому всегда старайтесь рассуждать о порядке памяти с точки зрения модели памяти C ++, а также с точки зрения того, как она может эффективно компилироваться для интересующего вас ISA (например, строго упорядоченного x86).Как вы могли заметить, что relaxed позволит переупорядочить во время компиляции, что на самом деле полезно в вашем случае.

1 голос
/ 01 июля 2019

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...