Обратный CAS, где сохранение происходит только в том случае, если новое значение не изменилось. Есть ли способ достичь этого атомарно? - PullRequest
0 голосов
/ 03 августа 2020

В основном то, что я ищу, - это что-то, что выполнит sh следующий лог c атомарно.

#define FAILED 1
#define SUCCESS 0

int foo (uint64_t * src, uint64_t * dst, uint64_t expected) {
    if (*src == expected) {
        *dst = *src;
        return SUCCESS;
    }
    return FAILURE;
}

Для этого конкретного варианта использования expected == 0 во всех случаях и *dst НЕ МОЖЕТ может быть изменен любым другим потоком. *src, однако может быть изменен другими потоками одновременно (эти другие потоки МОГУТ быть на других ядрах, иначе я мог бы использовать перезапускаемые последовательности)

Чтобы это было правильно *dst НЕЛЬЗЯ изменить, если *src != expected ( с expected == 0).

У меня есть инвариант *src != *dst, кроме случая *src == *dst == 0 (это может быть полезно для поиска решения).

Наконец, если он позволяет какие-либо потенциальные решения, я могу Гарантирую, что *src и *dst находятся либо в одной строке кэша, либо в разных строках кеша.

Я не верю ни одной из встроенных операций atomi c (https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html) может достичь этого, поэтому я думаю, что единственный способ сделать это будет с некоторой встроенной сборкой с префиксом lock или с использованием некоторого побочного эффекта какой-либо функции (то есть чего-то вроде того факта, что CAS установит ожидаемый на Ошибка).

В итоге я ищу либо способ реализовать настраиваемую операцию atomi c с встроенным asm, либо каким-то образом я могу использовать для этого bultin atomics.

Спасибо!

Ответы [ 2 ]

3 голосов
/ 03 августа 2020

Я не понимаю, для чего вам нужен этот примитив, и я думаю, что в принципе не имеет смысла называть его «atomi c». Если был какой-либо момент, когда *src наблюдалось как имеющее значение expected, это действительно для сохранения *dst. Никакой другой поток не может наблюдать атомарность или отсутствие таковой для этой операции. Так что просто напишите его так, как вы его написали (хотя и с соответствующим типом atomi c для *src), должно быть нормально.

Если вы не согласны с этим рассуждением, пожалуйста, объясните общую картину того, что вы хотите использовать примитив в, поскольку он почти наверняка неверен, и настоящая проблема состоит в том, чтобы понять, в чем его причина, и выяснить что-то, что работает вместо него. (И решение почти наверняка состоит в том, чтобы прекратить попытки использовать атомикс и использовать блокировку.)

1 голос
/ 03 августа 2020

Обычный CAS имеет только один операнд в памяти! Это похоже на частичный DCAS aka CAS2 .

Вы можете эмулировать его с широким CAS (например, lock cmpxchg16b), если вы поместите sr c и dst рядом друг с другом, но это могло бы ложно выйти из строя, если бы dst был изменен.

Или вы могли бы использовать транзакционную память Intel TSX, если у вас есть процессор, где он не отключен с помощью обновлений микрокода. (HLE отключен в микрокоде по каким-то причинам MDS уязвимости , я думаю, но AFAIK RTM все еще можно использовать на процессорах, где он не был отключен по причинам правильности, то есть работает на Skylake и, по крайней мере, позже, может быть, какой-нибудь Бродвелл.)

Обратите внимание, что загрузка AVX-512 всей строки кэша не гарантируется как atomi c, но на всех текущих процессорах AVX-512 считается, что это так. К сожалению, поставщики не удосуживаются предоставить какой-либо способ CPUID для обнаружения атомарности, гарантирующий более 8 байт, хотя 16-байтовая атомарность загрузки / сохранения SIMD широко распространена. Помните, что соединение между ядрами может вызвать разрыв, а не только узкий блок выполнения загрузки / сохранения: инструкции SSE: какие процессоры могут выполнять операции с памятью atomi c 16B? показывает разрыв на 8-байтовых границах на K10 Opteron только между ядрами в разных сокетах, благодаря HyperTransport.

...