2012-09-25 15 views
10

Edycja: ST nie zezwala na publikowanie więcej niż dwóch linków dla początkujących. Przepraszamy za brakujące referencje.Sprawdzanie bez blokady dla modyfikacji globalnego stanu współdzielonego w C przy użyciu wyrównania z pamięcią podręczną

Próbuję zmniejszyć obciążenie blokujące w aplikacji C, w której wykrywanie zmian w stanie globalnym ma wpływ na wydajność. Chociaż ostatnio dużo czytałem na ten temat (np. Wiele z H. Suttera i wielu innych), nie jestem pewien co do mojej implementacji. Chciałbym użyć kombinacji operacji podobnej do CAS i DCL dla sprawdzenia zmiennej globalnej z pamięcią podręczną wyrównaną, unikając w ten sposób dzielenia się błędami, aby zaktualizować lokalne dane wątków z danych współdzielonych przez wiele wątków. Mój brak zaufania jest przede wszystkim ze względu na

  1. mi braku interpretacji dokumentacji GNU na Type-Attributes
  2. Nie wydaje mogąc znaleźć żadnej literatury i przykładów, które mógłbym łatwo przełożyć do C, takie jak aligning- do-cache-line-a-wiedzy-o-cache-line-format na ST lub 1 (chociaż wydaje się odpowiedzieć na moje pytanie trochę nie jestem przekonana o mojej realizacji)
  3. moje doświadczenie z C jest ograniczona

Moje pytania:

  1. rodzaju atrybuty państwa dokumentacja:

    Ten atrybut określa minimalne wyrównanie (w bajtach) dla zmiennych o określonym typu. Na przykład, deklaracje:

    (proszę zapoznać się z dokumentacją typu atrybuty deklaracji)

    siła kompilator do ubezpieczenia (o ile to możliwe), że każda zmienna, której typ jest struct S lub more_aligned_int zostaną przydzielone i wyrównane przynajmniej na granicy 8-byte. Na architekturze SPARC , posiadanie wszystkich zmiennych typu struct S wyrównanych do granic 8-byte umożliwia kompilatorowi używanie instrukcji ldd i std (ładowanie i przechowywanie podwójnego tekstu) podczas kopiowania jednej zmiennej typu struct S na inną, poprawiając w ten sposób wydajność pracy .

    Czy to znaczy, że początek struct S lub more_aligned_int będzie zawsze dostosowane do 8-byte granicy? Nie oznacza to, że dane zostaną dopełnione do użycia dokładnie 64 bajtów, prawda?

  2. Przyjmując 1.Jest prawdą, że każdy przypadek struct cache_line_aligned (patrz kod Przykład 1 poniżej) wyrównuje na 64-byte granic i wykorzystują dokładnie jeden cache wiersza (zakładając, Cache-linie są 64 bytes długości)

  3. Korzystanie typedef dla deklaracja typu nie zmieniają semantyki __attribute__ ((aligned (64))) (patrz kod Przykład 2 poniżej)

  4. nie potrzeba używać aligned_malloc przy uruchamianiu strukturę, czy struktura jest zadeklarowana __attribute__ ...

// Example 1 
struct cache_line_aligned { 
 int version; 
 char padding[60]; 
} __attribute__ ((aligned (64))); 

// Example 2 
typedef struct { 
 int version; 
    // place '__attribute__ ((aligned (64)))' after 'int version' 
    // or at the end of the declaration 
 char padding[60]; 
} cache_line_aligned2 __attribute__ ((aligned (64))); 

I wreszcie szkic funkcji, która korzysta z cache-line wyrównane podejście skutecznie sprawdzić, czy stan globalny został zmodyfikowany przez innego wątku:

void lazy_update_if_changed(int &t_version, char *t_data) { 
    // Assuming 'g_cache_line_aligned' is an instance of 
    // 'struct cache_line_aligned' or 'struct cache_line_aligned2' 
    // and variables prefixed with 't_' being thread local 
    if(g_cache_line_aligned.version == t_version) { 
    // do nothing and return 
    } else { 
    // enter critical section (acquire lock e.g. with pthread_mutex_lock) 
    t_version = g_cache_line_aligned.version 
    // read other data that requires locking where changes are notified 
    // by modifying 'g_cache_line_aligned.version', e.g. t_data 
    // leave critical section 
    } 
} 

Przepraszam za długi post.

Dziękujemy!

Odpowiedz

7

Po zdefiniowaniu typu wyrównanego, powiedzmy, wyrównanego do granic 8 bajtów, kompilator powinien uczynić typ wielokrotnością linii trasowania (tutaj wielokrotność 8 bajtów) przez dopełnienie.

Uzasadnienie tego jest proste. Załóżmy, że chcesz zdefiniować tablicę tego wyrównanego typu. Oczywiście każdy jej element powinien być również wyrównany. Dlatego może być dopełnienie.

Oto mały pokaz:

#include <stdio.h> 

struct cache_line_aligned { 
    int version; 
// char padding[60]; 
} __attribute__ ((aligned (64))); 

int main(void) 
{ 
    struct cache_line_aligned s; 
    struct cache_line_aligned a[2]; 
    printf("sizeof(struct cache_line_aligned) = %d\n", (int)sizeof(struct cache_line_aligned)); 
    printf("sizeof(s) = %d\n", (int)sizeof(s)); 
    printf("sizeof(a[0]) = %d\n", (int)sizeof(a[0])); 
    printf("sizeof(a) = %d\n", (int)sizeof(a)); 
    return 0; 
} 

wyjściowa (ideone):

sizeof(struct cache_line_aligned) = 64 
sizeof(s) = 64 
sizeof(a[0]) = 64 
sizeof(a) = 128 

Jeśli utworzyć instancję struct cache_line_aligned zakaz dynamicznie (IOW, a nie przez malloc() i takie), podobnie jak w powyższym kodzie zostanie wyrównany.

Standard C (z 1999 roku) stanowi dla malloc(), calloc() i realloc():

The pointer returned if the allocation succeeds is suitably aligned so that 
it may be assigned to a pointer to any type of object and then used to 
access such an object or an array of such objects in the space allocated 
(until the space is explicitly deallocated). 

przypadku any type of object nie zawiera sztucznego wyrównane/wypełnienia typu jak wyżej struktury, ponieważ nie ma nic jak __attribute__ ((aligned (64))) w The C standard. To jest rozszerzenie GNU tutaj. W przypadku dynamicznie przydzielanych obiektów z dowolnym wyrównaniem należy użyć odpowiedniej funkcji alokacji pamięci lub ręcznie wyrównać (przydzielając więcej pamięci, a następnie "wyrównywanie" wartości wskaźnika).

+0

Pozdrawiam! to bardzo dobrze wyjaśniło sprawy. Nie myślałem o używaniu sizeof do sprawdzenia wyrównania! Zapamiętam to. A co z dynamicznie przydzielanymi wyrównanymi strukturami? Czy 'aligned_malloc' wykona zadanie? – instilled

+0

Prawdopodobnie to zrobi. Przeczytaj jego dokumentację, aby się upewnić. –

+0

Pewnie! Tak zrobię. Jeszcze raz dziękuję za genialną odpowiedź. – instilled

Powiązane problemy