В Intel® 64 и IA-32 Архитектура Руководство разработчика: Том. 3A , которая в настоящее время содержит спецификации упомянутой вами белой книги по упорядочению памяти, в разделе 8.2.3.1 сказано, как вы сами отметили, что
The Intel-64 memory ordering model guarantees that, for each of the following
memory-access instructions, the constituent memory operation appears to execute
as a single memory access:
• Instructions that read or write a single byte.
• Instructions that read or write a word (2 bytes) whose address is aligned on a 2
byte boundary.
• Instructions that read or write a doubleword (4 bytes) whose address is aligned
on a 4 byte boundary.
• Instructions that read or write a quadword (8 bytes) whose address is aligned on
an 8 byte boundary.
Any locked instruction (either the XCHG instruction or another read-modify-write
instruction with a LOCK prefix) appears to execute as an indivisible and
uninterruptible sequence of load(s) followed by store(s) regardless of alignment.
Теперь, поскольку вышеприведенный список НЕ содержит один и тот же язык для двойного четырехсловного слова (16 байт), из этого следует, что архитектура НЕ гарантирует, что инструкции, которые обращаются к 16 байтам памяти, являются атомарными.
Тем не менее, последний абзац намекает на выход, а именно на инструкцию CMPXCHG16B с префиксом LOCK. Вы можете использовать инструкцию CPUID, чтобы выяснить, поддерживает ли ваш процессор CMPXCHG16B (бит функции "CX16").
В соответствующем документе AMD, Технология AMD64, Руководство для программиста по архитектуре AMD64, том 2: Системное программирование Я не могу найти аналогичный понятный язык.
РЕДАКТИРОВАТЬ: Результаты программы испытаний
(Тестовая программа изменена для увеличения количества итераций в 10 раз)
на Xeon X3450 (x86-64):
0000 999998139 1572
0001 0 0
0010 0 0
0011 0 0
0100 0 0
0101 0 0
0110 0 0
0111 0 0
1000 0 0
1001 0 0
1010 0 0
1011 0 0
1100 0 0
1101 0 0
1110 0 0
1111 1861 999998428
на Xeon 5150 (32-разрядная версия):
0000 999243100 283087
0001 0 0
0010 0 0
0011 0 0
0100 0 0
0101 0 0
0110 0 0
0111 0 0
1000 0 0
1001 0 0
1010 0 0
1011 0 0
1100 0 0
1101 0 0
1110 0 0
1111 756900 999716913
На Opteron 2435 (x86-64):
0000 999995893 1901
0001 0 0
0010 0 0
0011 0 0
0100 0 0
0101 0 0
0110 0 0
0111 0 0
1000 0 0
1001 0 0
1010 0 0
1011 0 0
1100 0 0
1101 0 0
1110 0 0
1111 4107 999998099
Значит ли это, что Intel и / или AMD гарантируют, что 16-байтовые обращения к памяти являются атомарными на этих машинах? ИМХО, это не так. Это не указано в документации как гарантированное архитектурное поведение, и, таким образом, нельзя знать, действительно ли на этих конкретных процессорах 16-байтовые обращения к памяти являются атомарными или тестовая программа просто не в состоянии инициировать их по той или иной причине. И поэтому полагаться на это опасно.
РЕДАКТИРОВАТЬ 2: Как сделать тестовую программу неудачной
Ха! Мне удалось заставить тестовую программу провалиться. На том же Opteron 2435, что и выше, с тем же двоичным файлом, но теперь запускающим его через инструмент «numactl», указывающий, что каждый поток работает на отдельном сокете, я получил:
0000 999998634 5990
0001 0 0
0010 0 0
0011 0 0
0100 0 0
0101 0 0
0110 0 0
0111 0 0
1000 0 0
1001 0 0
1010 0 0
1011 0 0
1100 0 1 Not a single memory access!
1101 0 0
1110 0 0
1111 1366 999994009
Так что это значит? Что ж, Opteron 2435 может или не может гарантировать, что 16-байтовые обращения к памяти являются атомарными для доступа внутри сокетов, но, по крайней мере, протокол когерентности кэша, работающий на межсоединении HyperTransport между двумя сокетами, не дает такой гарантии. 1038 *
РЕДАКТИРОВАТЬ 3: ASM для функций потока, по запросу "GJ."
Вот сгенерированный asm для функций потоков для версии GCC 4.4 x86-64, используемой в системе Opteron 2435:
.globl thread2
.type thread2, @function
thread2:
.LFB537:
.cfi_startproc
movdqa .LC3(%rip), %xmm1
xorl %eax, %eax
.p2align 5,,24
.p2align 3
.L11:
movaps x(%rip), %xmm0
incl %eax
movaps %xmm1, x(%rip)
movmskps %xmm0, %edx
movslq %edx, %rdx
incl n2(,%rdx,4)
cmpl $1000000000, %eax
jne .L11
xorl %eax, %eax
ret
.cfi_endproc
.LFE537:
.size thread2, .-thread2
.p2align 5,,31
.globl thread1
.type thread1, @function
thread1:
.LFB536:
.cfi_startproc
pxor %xmm1, %xmm1
xorl %eax, %eax
.p2align 5,,24
.p2align 3
.L15:
movaps x(%rip), %xmm0
incl %eax
movaps %xmm1, x(%rip)
movmskps %xmm0, %edx
movslq %edx, %rdx
incl n1(,%rdx,4)
cmpl $1000000000, %eax
jne .L15
xorl %eax, %eax
ret
.cfi_endproc
и для полноты .LC3, который представляет собой статические данные, содержащие вектор (-1, -1, -1, -1), используемый thread2:
.LC3:
.long -1
.long -1
.long -1
.long -1
.ident "GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"
.section .note.GNU-stack,"",@progbits
Также обратите внимание, что это синтаксис AT & T ASM, а не синтаксис Intel. Программисты Windows могут быть более знакомы с ним. Наконец, это с march = native, что делает GCC предпочтительным MOVAPS; но это не имеет значения, если я использую march = core2, он будет использовать MOVDQA для хранения в x, и я все еще могу воспроизвести сбои.