Замок-бесплатная проверка на изменение глобального состояния в C использование кэш-линии выравнивания


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

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

  1. я не смог интерпретировать документацию GNU по атрибутам типа
  2. мне кажется, что я не могу найти никакой литературы и примеров, которые я мог бы легко перевести на C, таких как выравнивание по кэш-строке и знание размера кэш-строки на ST или 1 (хотя ... 1 кажется, чтобы ответить на мой вопрос несколько я не уверен с моей реализацией)
  3. мой опыт работы с C ограничен

Мои вопросы:

  1. Документация по атрибутам типов гласит:

    Этот атрибут задает минимальное выравнивание (в байтах) для переменных указанного типа. тип. Например, объявления:

    (пожалуйста, смотрите документацию по атрибутам типа для объявления)

    Заставьте компилятор гарантировать (насколько это возможно), что каждая переменная, тип которой struct S или more_aligned_int будет выделено и выровнено по крайней мере на границе 8-byte. На SPARC, имея все переменные типа struct S, выровненные по границам 8-byte, позволяет компилятор для использования инструкций ldd и std (doubleword load and store) при копировании одного переменная типа struct s к другой, таким образом улучшая эффективность времени выполнения.

    Означает ли это, что начало struct S или more_aligned_int будет всегда быть выровненным по границе 8-byte? Это не означает, что данные будут дополнены, чтобы использовать ровно 64 байта, не так ли?

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

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

  4. Я ... не нужно использовать aligned_malloc при создании экземпляра struct, если struct объявляется с помощью __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
  }
} 

Извините за длинный пост.

Спасибо!

1 10

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

Вывод (идеон):

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 здесь. Для динамически выделяемых объектов с произвольным выравнивание вы должны использовать соответствующую функцию выделения памяти или выполнить выравнивание вручную (выделив больше памяти и затем "выровняв" значение указателя).