Инструкции SSE: какие процессоры могут выполнять атомные операции памяти 16B?
Рассмотрим один доступ к памяти (одно чтение или однократное чтение, а не чтение + запись) SSE-инструкции на процессоре x86. Команда получает доступ к 16 байтам (128 бит) памяти, а доступная ячейка памяти соответствует 16 байтам.
В документе "Белая книга с памятью архитектуры архитектуры Intel® 64" указано, что для "Инструкции, которые читают или записывают квадрат (8 байт), адрес которого выровнен по границе 8 байтов", операция памяти, как представляется, выполняется как единая память доступ независимо от типа памяти.
Вопрос: Существуют ли процессоры Intel/AMD/etc x86, которые гарантируют, что чтение или запись 16 байтов (128 бит), выровненных по границе 16 байтов, выполняются как один доступ к памяти?, какой конкретный тип процессора он (Core2/Atom/K8/Phenom/...)? Если вы даете ответ (да/нет) на этот вопрос, , пожалуйста, также укажите метод, который использовался для определения ответа - поиск в PDF-документе, проверка грубой силы, математическое подтверждение или любой другой метод, который вы используется для определения ответа.
Этот вопрос относится к таким проблемам, как http://research.swtch.com/2010/02/off-to-races.html
Update:
Я создал простую тестовую программу на C, которую вы можете запускать на своих компьютерах. Пожалуйста, скомпилируйте и запустите его на своих процессорах Phenom, Athlon, Bobcat, Core2, Atom, Sandy Bridge или любых SSE2-совместимых процессорах, которые у вас есть. Спасибо.
// Compile with:
// gcc -o a a.c -pthread -msse2 -std=c99 -Wall -O2
//
// Make sure you have at least two physical CPU cores or hyper-threading.
#include <pthread.h>
#include <emmintrin.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
typedef int v4si __attribute__ ((vector_size (16)));
volatile v4si x;
unsigned n1[16] __attribute__((aligned(64)));
unsigned n2[16] __attribute__((aligned(64)));
void* thread1(void *arg) {
for (int i=0; i<100*1000*1000; i++) {
int mask = _mm_movemask_ps((__m128)x);
n1[mask]++;
x = (v4si){0,0,0,0};
}
return NULL;
}
void* thread2(void *arg) {
for (int i=0; i<100*1000*1000; i++) {
int mask = _mm_movemask_ps((__m128)x);
n2[mask]++;
x = (v4si){-1,-1,-1,-1};
}
return NULL;
}
int main() {
// Check memory alignment
if ( (((uintptr_t)&x) & 0x0f) != 0 )
abort();
memset(n1, 0, sizeof(n1));
memset(n2, 0, sizeof(n2));
pthread_t t1, t2;
pthread_create(&t1, NULL, thread1, NULL);
pthread_create(&t2, NULL, thread2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
for (unsigned i=0; i<16; i++) {
for (int j=3; j>=0; j--)
printf("%d", (i>>j)&1);
printf(" %10u %10u", n1[i], n2[i]);
if(i>0 && i<0x0f) {
if(n1[i] || n2[i])
printf(" Not a single memory access!");
}
printf("\n");
}
return 0;
}
В моем ноутбуке есть процессор Core Duo (не Core2). Этот конкретный процессор выходит из строя при тестировании, он реализует чтение/запись 16-байтовых данных с зернистостью 8 байтов. Выход:
0000 96905702 10512
0001 0 0
0010 0 0
0011 22 12924 Not a single memory access!
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 3092557 1175 Not a single memory access!
1101 0 0
1110 0 0
1111 1719 99975389
Ответы
Ответ 1
В Руководство разработчика для разработчиков Intel® 64 и IA-32: Vol. 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.
Теперь, поскольку приведенный выше список НЕ содержит один и тот же язык для double quadword (16 байт), следует, что архитектура НЕ гарантирует, что инструкции, которые обращаются к 16 байтам памяти, являются атомарными.
При этом последний абзац намекает на выход, а именно инструкцию CMPXCHG16B с префиксом LOCK. Вы можете использовать инструкцию CPUID, чтобы выяснить, поддерживает ли ваш процессор CMPXCHG16B (бит функции "CX16" ).
В соответствующем документе AMD AMD64 Technology AMD64 Architecture Programmers Manual Том 2: Системное программирование, я не могу найти такой же понятный язык.
EDIT: результаты тестовых программ
(тестовая программа изменена для увеличения #iterations в 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 между двумя сокетами, не дает такой гарантии.
EDIT 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 могут быть более знакомы. Наконец, это с маршем = native, что делает GCC предпочтительным MOVAPS; но это не имеет значения, если я использую march = core2, он будет использовать MOVDQA для хранения в x, и я все еще могу воспроизвести ошибки.
Ответ 2
В руководстве по архитектуре Intel Vol 3A есть предупреждение. Раздел 8.1.1 (май 2011 г.), в разделе гарантированных атомных операций:
Инструкция x87 или инструкции SSE, которые обращаются к данным больше чем квадловое слово может быть реализовано с использованием нескольких обращений к памяти. Если такая инструкция хранится в памяти, некоторые из них могут (запись в память), а другая - операция ошибка по архитектурным причинам (например, из-за того, что запись в таблице страниц отмечен "нет" ). В этом случае эффекты завершенных доступ может быть видимым для программного обеспечения, даже если общий инструкция вызвала ошибку. Если недействительность TLB была отложена (см. Раздел 4.10.4.4), такие ошибки страницы могут возникать, даже если все обращения на ту же страницу.
Таким образом, инструкции SSE не гарантируются как атомарные, даже если базовая архитектура использует один доступ к памяти (это одна из причин, по которой было введено ограждение памяти).
Объедините это с этим утверждением в Руководстве по оптимизации Intel, раздел 13.3 (апрель 2011 г.)
Команды AVX и FMA не вводят никаких новых гарантированных атомных операций с памятью.
и тот факт, что ни одна из операций загрузки или хранения SIMD не гарантирует атомарность, мы можем прийти к выводу, что Intel не поддерживает какой-либо формы атомного SIMD (пока).
В качестве дополнительного бита, если память разделяется вдоль линий кэша или границ страницы (при использовании таких вещей, как movdqu
, которые допускают неравномерный доступ), следующие процессоры не будут выполнять атомарные обращения, независимо от выравнивания, но в дальнейшем процессоры будут (опять же из руководства по архитектуре Intel):
Intel Core 2 Duo, Intel® Atom ™, Intel Core Duo, Pentium M, Pentium 4, Процессоры Intel Xeon, P6, Pentium и Intel486. Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4, Intel Процессоры семейства Xeon и P6
Ответ 3
"Руководство по программированию архитектуры AMD Volume 1: Application Programming" в разделе 3.9.1:" CMPXCHG16B
может использоваться для выполнения 16-байтовый атомный доступ в 64-битном режиме (с некоторыми ограничениями выравнивания).
Тем не менее, нет комментариев относительно инструкций SSE. На самом деле в комментарии 4.8.3 есть комментарий, что префикс LOCK "вызывает исключение недействительного кода операции при использовании с 128-битными инструкциями мультимедиа". Поэтому представляется мне совершенно убедительным, что процессоры AMD НЕ гарантируют атомные 128-битные обращения для инструкций SSE, и единственный способ сделать атомный 128-битный доступ - использовать CMPXCHG16B
.
" Руководство разработчика программного обеспечения Intel 64 и IA-32 Volume 3A: Руководство по системному программированию, часть 1" говорится в 8.1.1 "Инструкция x87 или инструкции SSE, которые обращаются к данным, большим, чем квадловое слово, могут быть реализованы с использованием нескольких обращений к памяти". Это довольно убедительно, что 128-битные SSE-инструкции не гарантируются атомарным ISA. Том 2A в документах Intel говорит о CMPXCHG16B
: "Эта инструкция может использоваться с префиксом LOCK, чтобы позволить инструкции выполняться атомарно."
Кроме того, производители ЦП не опубликовали письменных гарантий атомных 128-разрядных SSE-операций для конкретных моделей ЦП, если это так.
Ответ 4
x86 ISA не гарантирует атомарность для чего-либо большего, чем 8B, поэтому реализации могут свободно поддерживать SSE/AVX, как это делает Pentium III/Pentium M/Core Duo: внутренние данные обрабатываются в 64-разрядных половинах. 128-битный магазин выполнен в виде двух 64-битных магазинов. Путь данных в/из кеша имеет только 64-битную ширину в микроархитектуре Yonah (Core Duo). (источник: Agar Fog microarch doc).
В более поздних реализациях есть более широкие пути данных внутри и обрабатываются 128b-инструкциями как один op. Core 2 Duo (conroe/merom) стал первым микроархитом Intel P6 с потоком данных 128b. (IDK о P4, но, к счастью, он достаточно стар, чтобы быть абсолютно неактуальным.)
Вот почему OP обнаруживает, что 128b ops не являются атомарными в Intel Core Duo (Yonah), но другие плакаты считают, что они являются атомарными на более поздних версиях Intel, начиная с Core 2 (Merom).
Диаграммы этой записи Realworldtech о Merom vs. Yonah показывают 128-битный путь между кэшем данных ALU и L1 в Merom (и P4), в то время как маломощная Yonah имеет 64-битный путь передачи данных. Путь данных между кешами L1 и L2 составляет 256b во всех трех проектах.
Следующий переход по ширине пути данных пришел с Intel Haswell, с загрузкой/хранением AVB/AVX2 256b (32B) и 64-битным контуром между кешем L1 и L2. Я ожидаю, что 256 бит/магазины будут атомарными в Хасуэлле, Бродвелле и Скайлаке, но мне не нужно их проверять. Я забыл, что Skylake снова расширил пути подготовки AVX512 в Skylake-EP (серверная версия) или, возможно, первоначальная реализация AVX512 будет похожа на SnB/IvB AVX, а 512b загрузок/хранилищ занимают порт загрузки/хранения для 2 циклов.
Как замечает Яннеб в своем превосходном экспериментальном ответе, протокол кеширования между сокетами в многоядерной системе может быть уже, чем то, что вы получаете в CPU центрального процессора последнего уровня. Архитектурных требований к атомичности для широких нагрузок/хранилищ не существует, поэтому разработчики могут сделать их атомарными в сокете, но неатомными через сокеты, если это удобно. IDK, насколько широк путь межсотовых логических данных для семейства AMD Bulldozer или для Intel. (Я говорю "логично", потому что даже если данные передаются в меньших фрагментах, он не может изменять строку кэша до тех пор, пока он не будет полностью получен.)
Поиск похожих статей о процессорах AMD позволяет сделать разумные выводы о том, являются ли 128b ops атомарными или нет. Некоторая помощь: просто проверить таблицы команд.
K8 декодирует movaps reg, [mem]
до 2 m-ops, а K10 и семейство бульдозеров декодируют его до 1 м-op. AMD с низким энергопотреблением bobcat декодирует его до 2 операций, в то время как jaguar декодирует 128b movaps до 1 m-op. (Он поддерживает AVX1, аналогичный процессорам семейства Bulldozer: 256b insns (даже ALU ops) разделены на два 128b ops. Intel SnB разделяет только 256b загрузок/хранилищ, имея ALU с полной шириной.)
janneb Opteron 2435 является 6-ядерным центральным процессором в Стамбуле, который является частью семейства K10, поэтому этот single-m-op → атомное заключение кажется точным в пределах одного сокета.
Intel Silvermont делает 128b загрузок/хранилищ с одним uop и пропускной способностью одного на такт. Это то же самое, что и для целых нагрузок/хранилищ, поэтому он вполне вероятно атомный.
Ответ 5
EDIT:
За последние два дня я провел несколько тестов на трех моих компьютерах, и я не воспроизвел ошибку памяти, поэтому я не могу сказать ничего более точно. Возможно, эта ошибка памяти также зависит от ОС.
EDIT:
Я программирую в Delphi, а не в C, но я должен понимать C. Итак, я перевел код, вот у вас есть процедуры потоков, в которых основная часть выполняется в ассемблере:
procedure TThread1.Execute;
var
n :cardinal;
const
ConstAll0 :array[0..3] of integer =(0,0,0,0);
begin
for n := 0 to 100000000 do
asm
movdqa xmm0, dqword [x]
movmskps eax, xmm0
inc dword ptr[n1 + eax *4]
movdqu xmm0, dqword [ConstAll0]
movdqa dqword [x], xmm0
end;
end;
{ TThread2 }
procedure TThread2.Execute;
var
n :cardinal;
const
ConstAll1 :array[0..3] of integer =(-1,-1,-1,-1);
begin
for n := 0 to 100000000 do
asm
movdqa xmm0, dqword [x]
movmskps eax, xmm0
inc dword ptr[n2 + eax *4]
movdqu xmm0, dqword [ConstAll1]
movdqa dqword [x], xmm0
end;
end;
Результат: без ошибок на моем четырехъядерном ПК и без ошибок на моем двухъядерном ПК, как ожидалось!
- ПК с процессором Intel Pentium4
- ПК с процессором Intel Core2 Quad CPU Q6600
- ПК с процессором Intel Core2 Duo P8400
Можете ли вы показать, как отладчик видит ваш код процедуры потока? Пожалуйста,...
Ответ 6
До сих пор было опубликовано множество ответов, и, следовательно, много информации уже доступно (как побочный эффект много путаницы). Я хотел бы представить факты из руководства Intel относительно аппаратных гарантированных атомных операций...
В новейших процессорах Intel семейства nehalem и песчаных мостов гарантируется чтение или запись в квадловое слово, согласованное с 64-битной границей.
Даже неглавные 2, 4 или 8 байтовые чтения или записи гарантированно являются атомарными, если они кэшированы и помещаются в строку кэша.
Сказав, что тест, опубликованный в этом вопросе, проходит на процессоре Intel i5 на основе песочного моста.