Instrukcje SSE: które procesory mogą wykonywać operacje na pamięci atomic 16B?

Rozważ pojedynczy dostęp do pamięci (pojedynczy odczyt lub pojedynczy zapis, a nie Odczyt+Zapis) Instrukcja SSE na procesorze x86. Instrukcja uzyskuje dostęp do 16 bajtów (128 bitów) pamięci, a dostęp do lokalizacji pamięci jest wyrównany do 16 bajtów.

Dokument "Intel® 64 Architecture Memory Ordering White Paper" stwierdza, że dla "instrukcji odczytywających lub zapisujących quadword (8 bajtów), którego adres jest wyrównany na granicy 8 bajtów" operacja pamięci wydaje się być wykonywana jako pojedynczy dostęp do pamięci niezależnie od typu pamięci.

Pytanie: czy istnieją procesory Intel/AMD/etc x86, które gwarantują, że odczyt lub zapis 16 bajtów (128 bitów) wyrównanych do granicy 16 bajtów jest wykonywany jako pojedynczy dostęp do pamięci? jest tak, który konkretny typ PROCESORA to jest (Core2 / Atom/K8 / Phenom/...)? Jeśli podasz odpowiedź (Tak/nie) na to pytanie, proszę również podać metodę , która została użyta do określenia odpowiedzi - wyszukiwanie dokumentów PDF, testowanie brute force, dowód matematyczny lub cokolwiek innego inną metodą, której użyłeś do ustalenia odpowiedzi.

To pytanie dotyczy problemów takich jak http://research.swtch.com/2010/02/off-to-races.html


Aktualizacja:

Stworzyłem prosty program testowy w języku C, który można uruchomić na komputerach. Skompiluj i uruchom go na swoim Phenomie, Athlonie, Bobcat, Core2, atomie, Sandy Bridge lub jakimkolwiek innym procesorze obsługującym SSE2. Dzięki.

// 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;
}

PROCESOR, który mam w notebooku to Core Duo (nie Core2). To procesor nie przeszedł testu, implementuje 16-bajtową pamięć odczytu/zapisu z ziarnistością 8 bajtów. Wyjście to:

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
Author: Peter Cordes, 2011-10-04

6 answers

[[7]}W Intel® 64 i IA-32 architectures Developer ' s Manual: Vol. 3A , która obecnie zawiera specyfikacje białej księgi zamówień pamięci, o której wspomniałeś, mówi się w sekcji 8.2.3.1, jak sam zauważyłeś, że

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.

Teraz, ponieważ Powyższa lista nie zawiera tego samego języka dla podwójnego czworokąta (16 bajtów), wynika z tego, że architektura nie gwarantuje, że instrukcje, które mają dostęp do 16 bajtów pamięci, są atomowe.

To powiedziawszy, ostatni akapit podpowiada wyjście, a mianowicie instrukcję CMPXCHG16B z prefiksem blokady. Możesz użyć instrukcji CPUID, aby dowiedzieć się, czy twój procesor obsługuje CMPXCHG16B (bit funkcji "CX16").

W odpowiednim dokumencie AMD, amd64 Technology AMD64 Architecture Programmer ' s Manual Volume 2: System Programming , nie mogę znaleźć podobnego przejrzystego języka.

EDIT: test program results

(Program testowy zmodyfikowany w celu zwiększenia # iteracji przez współczynnik 10)

Na 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

Na Xeon 5150 (32-bit):

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

Na opteronie 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

Czy to oznacza, że Intel i / lub AMD gwarantują, że 16-bajtowy dostęp do pamięci jest atomowy na tych maszynach? IMHO, nie ma. Nie ma go w dokumentacji jako gwarantowanego zachowania architektonicznego, a zatem nie można wiedzieć, czy na tych konkretnych procesorach 16-bajtowy dostęp do pamięci jest naprawdę atomowy, czy program testowy tylko nie uruchamia ich z tego czy innego powodu. A zatem poleganie na nim jest niebezpieczne.

EDIT 2: Jak sprawić, by program testowy nie powiódł się

Ha! Udało mi się doprowadzić program do niepowodzenia. Na tym samym Opteron 2435 jak wyżej, z tym samym binarnym, ale teraz uruchamiając go za pomocą narzędzia "numactl" określającego, że każdy wątek działa na osobnym gnieździe, otrzymałem:
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
Więc co to oznacza? Cóż, Opteron 2435 może, lub nie, zagwarantować, że 16-bajtowy dostęp do pamięci będzie atomic dla dostępu wewnątrz gniazd, ale przynajmniej protokół Cache działający na interkonekcie HyperTransport między dwoma gniazdami nie daje takiej gwarancji.

Edycja 3: ASM dla funkcji wątku, na życzenie "GJ."

W tym artykule omówiono funkcje wątku dla GCC 4.4 x86-64 w systemie 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

I dla kompletności .LC3, czyli dane statyczne zawierające wektor (-1, -1, -1, -1) użyty przez 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

Zauważ również, że jest to składnia AT & T ASM, a nie Programiści Windows Intel syntax mogą być bardziej zaznajomieni z tą składnią. Na koniec, jest to z march = native, co sprawia, że GCC preferuje MOVAPS; ale to nie ma znaczenia, jeśli użyję March = core2 to użyje MOVDQA do przechowywania na X, I nadal mogę odtworzyć błędy.

 32
Author: janneb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-07 13:05:51

"AMD Architecture Programmer' s Manual Volume 1: Application Programming" mówi w sekcji 3.9.1: "CMPXCHG16B może być używany do wykonywania 16-bajtowego dostępu atomowego w trybie 64-bitowym (z pewnymi ograniczeniami wyrównania)."

Nie ma jednak takiego komentarza na temat instrukcji SSE. W rzeczywistości w 4.8.3 jest komentarz, że prefiks blokady "powoduje wyjątek nieprawidłowego kodu opcode, gdy jest używany z 128-bitowymi instrukcjami multimedialnymi". Dlatego wydaje mi się dość rozstrzygające, że AMD procesory nie gwarantują atomic 128-bitowego dostępu dla instrukcji SSE, a jedynym sposobem na uzyskanie atomic 128-bitowego dostępu jest użycie CMPXCHG16B.

" Intel 64 And IA-32 Architectures Software Developer ' s Manual Volume 3A: System Programming Guide, Part 1" mówi w 8.1.1 "Instrukcja x87 lub Instrukcja SSE, która uzyskuje dostęp do danych większych niż quadword, może być zaimplementowana przy użyciu wielu dostępów do pamięci."Jest to dość rozstrzygające, że 128-bitowe instrukcje SSE nie są gwarantowane atomic przez ISA. Tom 2a dokumentów Intela mówi o CMPXCHG16B: "instrukcja ta może być używana z prefiksem blokady, aby umożliwić wykonanie instrukcji atomicznie."

Ponadto producenci procesorów nie opublikowali pisemnych gwarancji operacji atomic 128B SSE dla określonych modeli procesorów, gdzie tak jest.

 4
Author: Anthony Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-10 04:14:24

W podręczniku architektury Intel Vol 3A. sekcja 8.1.1 (maj 2011), pod sekcją gwarantowane operacje atomowe, znajduje się Ostrzeżenie:

Instrukcja x87 lub Instrukcja SSE, która uzyskuje dostęp do danych większych quadword może być zaimplementowany przy użyciu wielu dostępów do pamięci. Jeśli taka instrukcja przechowuje w pamięci, niektóre z dostępów mogą zakończyć (zapis do pamięci), podczas gdy inna powoduje, że operacja usterka ze względów architektonicznych (np. z powodu page-table entry that is oznaczony jako "not present"). W tym przypadku skutki zrealizowanego dostęp może być widoczny dla oprogramowania, nawet jeśli ogólnie Instrukcja spowodowała błąd. Jeśli unieważnienie TLB zostało opóźnione (patrz 4.10.4.4), takie błędy strony mogą wystąpić, nawet jeśli wszystkie dostępy są do tej samej strony.

Tak więc instrukcje SSE nie są gwarantowane jako atomowe, nawet jeśli bazowa Architektura używa pojedynczego dostępu do pamięci (jest to jeden z powodów, dla których pamięć wprowadzono szermierkę).

Połącz to z tym stwierdzeniem z podręcznika optymalizacji Intel, sekcja 13.3 (kwiecień 2011)

Instrukcje AVX i FMA nie wprowadzają żadnych nowych gwarantowanych atomów operacje pamięci.

I fakt, że żadna z operacji load lub store dla SIMD nie gwarantuje atomiczności, możemy dojść do wniosku, że Intel nie obsługuje żadnej formy atomic SIMD (jeszcze).

Jako dodatkowy bit, jeśli pamięć jest dzielona wzdłuż bufora linie lub granice stron (przy użyciu rzeczy takich jak movdqu, które pozwalają na dostęp bez wyrównania), następujące procesory będą nie wykonywać dostęp atomowy, niezależnie od wyrównania, ale późniejsze procesory będą (ponownie z podręcznika architektury Intel):

Intel Core 2 Duo, Intel ® Atom™, Intel Core Duo, Pentium M, Pentium 4, Procesory Intel Xeon, rodziny P6, Pentium i Intel486. Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4, Intel Xeon i P6 Rodzina procesorów

 3
Author: Necrolis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-07 11:02:58

ISA x86 nie gwarantuje atomiczności dla niczego większego niż 8B, więc implementacje są wolne od implementacji SSE / AVX obsługują sposób, w jaki Pentium III / Pentium M / Core Duo: wewnętrzne dane są obsługiwane w połówkach 64-bitowych. 128-bitowy sklep odbywa się jako dwa 64-bitowe sklepy. Ścieżka danych do / z pamięci podręcznej ma tylko 64B szerokości w mikroarchitekturze Yonah (Core Duo). (Źródło: mikroarchitektura Agnera Foga ).

Nowsze implementacje czy mają szersze ścieżki danych Core 2 Duo (conroe/merom) był pierwszym mikroprocesorem Intela P6 ze ścieżkami danych 128b. (IDK o P4, ale na szczęście jest na tyle stary, że zupełnie nieistotny.)

Dlatego OP stwierdza, że 128b ops nie są atomowe na Intel Core Duo (Yonah), ale inne plakaty stwierdzają, że są atomowe na późniejszych projektach Intela, począwszy od Core 2 (Merom).

Diagramy na tym Realworldtech piszą o Merom vs. Yonah pokazuje 128-bitową ścieżkę pomiędzy ALU i L1 data-cache w Merom (i P4), podczas gdy Yonah o niskiej mocy ma 64-bitową ścieżkę danych. Ścieżka danych między L1 i L2 cache jest 256b we wszystkich 3 projektach.

[1]}kolejnym skokiem w szerokości ścieżki danych był Intel Haswell, wyposażony w 256b (32B) AVX/AVX2 loads/stores i 64byte ścieżkę między L1 i L2 cache. Spodziewam się, że ładunki/sklepy 256b są atomowe w Haswell, Broadwell i Skylake, ale nie mam żadnego do przetestowania. I forget if Skylake ponownie poszerzył ścieżki w przygotowaniu do AVX512 w Skylake-EP (wersja serwera), lub jeśli być może początkowa implementacja AVX512 będzie jak SNB/IvB AVX, i mają 512B obciążenia / sklepy zajmują port obciążenia / store dla 2 cykli.

Jak podkreśla janneb w swojej doskonałej, eksperymentalnej odpowiedzi, protokół spójności pamięci podręcznej między gniazdami w systemie wielordzeniowym może być węższy niż ten, który można uzyskać w CPU shared-last-level-cache. Nie ma wymagań architektonicznych na atomiczność dla szerokich obciążeń/sklepów, więc projektanci mogą je dowolnie tworzyć atomowe w gnieździe, ale niematomiczne w gniazdach, jeśli jest to wygodne. IDK jak szeroka jest logiczna ścieżka danych między gniazdami dla rodziny bulldozer AMD lub dla Intela. (Mówię "logiczne", ponieważ nawet jeśli dane są przesyłane w mniejszych kawałkach, może to nie zmodyfikować linii pamięci podręcznej, dopóki nie zostanie w pełni odebrana.)


Znalezienie podobnych artykułów na temat procesorów AMD powinno pozwolić na wyciągnięcie rozsądnych wniosków na temat tego, czy 128b operacje są atomowe lub nie. Po prostu sprawdzanie tabel instrukcji jest pewną pomocą:

K8 dekoduje movaps reg, [mem] do 2 m-op, podczas gdy K10 i bulldozer-family dekodują go do 1 m-op. mała moc AMD bobcat dekoduje go do 2 ops, podczas gdy jaguar dekoduje 128b movaps do 1 m-op. (obsługuje AVX1 podobne do procesorów z rodziny bulldozer: 256b insns (nawet ALU ops) są podzielone na dwa 128B ops. Intel SnB dzieli tylko ładunki/sklepy 256b, mając jednocześnie ALUS o pełnej szerokości.)

Opteron 2435 firmy Janneb to 6-rdzeniowy procesor, który jest częścią rodziny K10, więc ten pojedynczy-m-op -> wniosek atomowy wydaje się dokładny w pojedynczym gnieździe. Intel Silvermont obsługuje 128B ładowań/sklepów z jednym uop i przepustowością jednego zegara. Jest to to samo, co w przypadku ładunków/magazynów całkowitych, więc prawdopodobnie jest atomowe.
 2
Author: Peter Cordes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-13 02:53:32

EDIT: W ciągu ostatnich dwóch dni zrobiłem kilka testów na moich trzech komputerach i nie odtworzyłem żadnego błędu pamięci, więc nie mogę powiedzieć nic dokładniej. Może ten błąd pamięci jest również zależny od systemu operacyjnego.

EDIT: Programuję w Delphi, a nie w C, ale powinienem zrozumieć C. Więc przetłumaczyłem kod, tutaj masz procedury wątków, w których główna część jest wykonana w asemblerze:

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;

Wynik: żadnego błędu na moim quad core PC i nie błąd na moim dwurdzeniowym komputerze zgodnie z oczekiwaniami!

  1. komputer z procesorem Intel Pentium4
  2. komputer z procesorem Intel Core2 Quad Q6600
  3. komputer z procesorem Intel Core2 Duo P8400

Czy możesz pokazać, jak debuger widzi kod procedury wątku? Proszę...

 0
Author: GJ.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-19 04:09:16

Do tej pory zamieszczono wiele odpowiedzi i stąd wiele informacji jest już dostępnych(jako efekt uboczny dużo zamieszania). Chciałbym przedstawić fakty z podręcznika Intela dotyczące gwarantowanych operacji atomowych ...

W najnowszych procesorach Intela z rodziny nehalem i sandy bridge gwarantowany jest odczyt lub zapis do quadworda wyrównanego do granicy 64 bitów.

Nawet niepodpisane 2, 4 lub 8 bajtowe odczyty lub zapisy są gwarantowane jako atomowe, pod warunkiem, że są pamięci podręcznej i zmieścić się w linii pamięci podręcznej.

Powiedziawszy, że test zamieszczony w tym pytaniu przechodzi na procesor Intel i5 oparty na sandy bridge.

 -1
Author: Nitin Kunal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-10 10:46:43