Проверка без блокировки для модификации глобального общего состояния в C с использованием выравнивания Cache-Line

Изменить: ST не позволяет размещать более двух ссылок для новичков. Приносим извинения за недостающие ссылки.

Я пытаюсь уменьшить накладные расходы на блокировку в приложении C, где обнаружение изменений глобального состояния имеет значение для производительности. Несмотря на то, что в последнее время я довольно много читал по этой теме (например, много от Х. Саттера и многих других), я не уверен в своей реализации. Я хотел бы использовать комбинацию операции, подобной CAS, и DCL для проверки глобальной переменной Cache-Line Aligned, чтобы избежать ложных -sharing, чтобы обновить локальные данные потока из данных, совместно используемых несколькими потоками. Моя неуверенность в себе в основном связана с

  1. я не могу интерпретировать документацию GNU по Атрибуты типа
  2. Кажется, я не могу найти никакой литературы и примеров, которые я мог бы легко перевести на C, таких как aligning-to-cache-line-and-known-the-cache-line-size на ST или 1 (хотя 1 < / em>, кажется, немного отвечает на мой вопрос, я не уверен в своей реализации)
  3. мой опыт работы с C ограничен

Мои вопросы:

  1. # P4 #
    # P5 # # P6 # # P7 #
    # P8 #
  2. Предполагается, что 1. верно, что каждый экземпляр struct cache_line_aligned (см. Код Пример 1 ниже) выравнивается по 64-byte границам и использует ровно одну строку кэша (при условии, что строки кэша имеют длину 64 bytes)

  3. Использование typedef для объявления типа не изменяет семантику __attribute__ ((aligned (64))) (см. Код Пример 2 ниже)

  4. Мне не нужно использовать aligned_malloc при создании экземпляра структуры, если структура объявлена ​​с __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)));

И, наконец, набросок функции, которая использует подход с выравниванием строки кэша для эффективной проверки того, было ли изменено глобальное состояние каким-либо другим потоком:

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

Извините, за длинную статью.

Спасибо!


person instilled    schedule 25.09.2012    source источник


Ответы (1)


Когда вы определяете выровненный тип, скажем, выровненный по 8-байтовым границам, компилятор должен сделать тип размером, кратным выравниванию (здесь, кратным 8 байтам), путем заполнения.

Причина этого проста. Предположим, вы хотите определить массив этого выровненного типа. Естественно, что каждый его элемент тоже должен быть выровнен. Вот почему могут быть отступы.

Вот небольшая демонстрация:

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

Вывод (ideone):

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

Если вы создаете экземпляр struct cache_line_aligned нединамически (IOW, а не через malloc() и т. Д.), Как в приведенном выше коде, он будет выровнен.

Стандарт C (с 1999 г.) устанавливает для malloc(), calloc() и 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).

Где any type of object не включает искусственно выровненные / дополненные типы, подобные приведенной выше структуре, потому что в стандарте C нет ничего похожего на __attribute__ ((aligned (64))). Здесь это расширение GNU. Для динамически выделяемых объектов с произвольным выравниванием вы должны использовать соответствующую функцию выделения памяти или выполнить выравнивание вручную (выделив больше памяти и затем «выровняв» значение указателя).

person Alexey Frunze    schedule 26.09.2012
comment
Ваше здоровье! это очень хорошо прояснило ситуацию. Я не думал об использовании sizeof для проверки выравнивания! Я запомню это. А как насчет динамически выделяемых выровненных структур? aligned_malloc выполнит ли работу? - person instilled; 26.09.2012
comment
Вероятно, так и будет. Прочтите его документацию, чтобы убедиться. - person Alexey Frunze; 26.09.2012
comment
Конечно! Будет делать так. Еще раз спасибо за блестящий ответ. - person instilled; 27.09.2012