Можно ли использовать встроенные функции _mm_store_si128/_mm_load_si128 для реализации 128-битного атомарного типа?

Если я хочу реализовать 128-битный атомарный тип на x64, могу ли я использовать _mm_store_si128 и _mm_load_si128, чтобы избежать cmpxchg16b для расслабленных load и store?

(Если нужно, могу предположить, что нужны только load и store, хотя было бы хорошо, если бы я мог смешать их с cmpxchg16b)


person Alex Guteniev    schedule 14.04.2020    source источник
comment
На некоторых реальных аппаратных средствах широко распространено мнение, что ответ положительный, но производители ЦП, к сожалению, не предоставляют возможности узнать, что это безопасно, или каких-либо опубликованных гарантий для любого ЦП, что это безопасно. Это может быть сложно, например. многосокетные системы AMD K10 имеют разрывы на 8-байтовых границах только между ядрами на разных сокетах (через Hypertransport).   -  person Peter Cordes    schedule 14.04.2020
comment
Но в остальном да, movdqa загрузка/сохранение в ассемблере — это получение/выпуск, и на практике это атомарно на последних процессорах. И на процессорах с lock cmpxchg16b, GCC/clang, по крайней мере, использует его вместо блокировки для 16-байтовых атомов либо путем встраивания, либо с кодом в libatomic (gcc7 изменился на вызов без блокировки, потому что чтение через lock cmpxchg16b означает, что читатели все еще спорят друг с другом, поэтому обычные ожидания производительности от блокировки не оправдываются). Так что в этом случае да, смешивание SIMD с atomic<a_16_byte_type> будет безопасным на большинстве аппаратных средств. Если вас интересует только один сервер, протестируйте его на этом сервере.   -  person Peter Cordes    schedule 14.04.2020
comment
Я мог бы снова открыть это и написать краткий ответ о смешивании встроенных функций с atomic<T> в текущем gcc/clang, если вы все еще хотите попробовать это, несмотря на отсутствие каких-либо будущих гарантий, что это безопасно. (или документацию для текущего поведения). Я закрыл это, потому что отсутствие гарантий безопасности должно быть препятствием для большей части кода, включая любой код, который вы хотите распространять среди других. Если бы он был известен как безопасный на некоторых/любых процессорах, GCC/clang уже использовал бы movdqa для 16-байтовой чистой загрузки/чистого сохранения, как GCC использует movq для 8-байтового в 32-битном режиме. (GCC даже использует fild, если SSE1 недоступен)   -  person Peter Cordes    schedule 14.04.2020
comment
Я удовлетворен ответом, что это не всегда безопасно.   -  person Alex Guteniev    schedule 14.04.2020
comment
Я был недоволен 128-битным атомарием как на основе cmpxchg16b (в Boost), так и на блокировках (поставляется с компилятором). Поэтому я работал, не используя 128-битный atomic. Но я заметил, что на x86 64-битный atomic реализуется с использованием SSE или fild, как вы упомянули (хотя я наблюдаю другое поведение компилятора, а не GCC), поэтому мне было интересно узнать о SSE для 128-битного.   -  person Alex Guteniev    schedule 14.04.2020
comment
GCC7 и более поздние версии не будут встраиваться lock cmpxchg16b, но по-прежнему используют его через libatomic. Он __atomic_load_16 или как там он называется в libatomic на самом деле не блокирует процессоры с этой функцией; один шаг в atomic<__int128>.load, если вам интересно. Я думаю, clang встроит его, если вы скомпилируете с -mcx16 или -march=anything, кроме k8.   -  person Peter Cordes    schedule 14.04.2020
comment
Я искал расслабленные загрузки памяти и хранилища, которые не применяют ограничение памяти. Инлайнинг мало что меняет в этом отношении. Хотя в MSVC 2019 все встроено, включая _InterlockedCompareExchange64 и __iso_volatile_load64 для 64-битного атомарного на x86, блокировки для 128-битного атомарного на x64 и _InterlockedCompareExchange128 для 128-битного boost::atomic на x64.   -  person Alex Guteniev    schedule 14.04.2020
comment
О да, IIRC MSVC просто блокирует 16-байтовые объекты, пропуская (вероятно, незначительную) оптимизацию использования lock cmpxchg непосредственно в качестве загрузки. Re: заборы: модель памяти x86/x86-64 программный порядок + store буфер с store forwarding. Компиляторы получают и выпускают бесплатно, просто блокируя переупорядочение во время компиляции. (Возможно, вы могли бы использовать atomic_signal_fence() как хак для портативного барьера только для компилятора или, конечно, просто использовать atomic_thread_fence(mo_release)). Вам нужна только инструкция mfence после сохранения seq_cst. (Или просто сделайте их с xchg, что является полным барьером).   -  person Peter Cordes    schedule 14.04.2020
comment
В компиляторе MSVC заборы - это _ReadWriteBarrier, std::atomic_thread_fence(seq_cst) - это lock cmpxchg с фиктивной переменной. И я хотел избежать каких-либо блокировок или инструкций *fence для загрузки/сохранения. Насколько я понимаю, пока они не выпустят набор инструментов для взлома ABI, cmpxchg16b не может быть введен в их std::atomic<128b>, даже защищенный обнаружением ЦП.   -  person Alex Guteniev    schedule 14.04.2020
comment
Вы говорите о способах написания полного барьера на C++ (который компилируется в инструкцию ассемблера барьера). Я говорю о способах просто заблокировать переупорядочивание во время компиляции, оставив упорядочивание во время выполнения аппаратному обеспечению. Большинство людей называют это барьером компилятора. preshing.com/20120625/упорядочение памяти во время компиляции Например, atomic_thread_fence(mo_acq_rel) на x86-64 - это нулевые ассемблерные инструкции или не встроенный вызов функции.   -  person Peter Cordes    schedule 14.04.2020
comment
Re: изменение std::atomic<128b> для использования lock cmpxchg16b - правильно, это было бы нарушением ABI для MSVC, поскольку существующий код использует отдельную блокировку. Но GCC/clang на x86-64 Linux, по крайней мере, работает так, как я описал. godbolt.org/z/yp8GYX clang 10.0 по-прежнему встраивает lock cmpxchg16b с -march, что позволяет это сделать.   -  person Peter Cordes    schedule 14.04.2020