Замок-бесплатная проверка на изменение глобального состояния в C использование кэш-линии выравнивания
Edit: ST не позволяет размещать более двух ссылок для новичков. Извините за недостающие ссылки.
Я пытаюсь уменьшить накладные расходы на блокировку в приложении C, где обнаружение изменений в глобальном состоянии имеет отношение к производительности. Хотя в последнее время я довольно много читал на эту тему (например, много от Х. Саттера и многое другое), я не уверен в своей реализации. Я хотел бы использовать комбинациюCAS like operation и DCL для a проверьте глобальную переменнуюcache-Line Aligned , чтобы избежать ложного совместного использования, чтобы обновить локальные данные потока из данных, совместно используемых несколькими потоками. Моя неуверенность в себе вызвана главным образом
- я не смог интерпретировать документацию GNU по атрибутам типа
- мне кажется, что я не могу найти никакой литературы и примеров, которые я мог бы легко перевести на C, таких как выравнивание по кэш-строке и знание размера кэш-строки на ST или 1 (хотя ... 1 кажется, чтобы ответить на мой вопрос несколько я не уверен с моей реализацией)
- мой опыт работы с C ограничен
Мои вопросы:
-
Документация по атрибутам типов гласит:
Этот атрибут задает минимальное выравнивание (в байтах) для переменных указанного типа. тип. Например, объявления:
(пожалуйста, смотрите документацию по атрибутам типа для объявления)
Заставьте компилятор гарантировать (насколько это возможно), что каждая переменная, тип которой
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 байта, не так ли? -
Предположение 1. верно, что каждый экземпляр
struct cache_line_aligned
(см. код Пример 1 ниже) выравнивается по границам64-byte
и использует ровно одну кэш-строку (предполагая, что кэш-строки имеют длину64 bytes
) -
Использование
typedef
для объявления типа не изменяет семантику__attribute__ ((aligned (64)))
(см. код Пример 2 ниже) Я ... не нужно использовать
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 ответ:
Когда вы определяете выровненный тип, скажем, выровненный по 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 здесь. Для динамически выделяемых объектов с произвольным выравнивание вы должны использовать соответствующую функцию выделения памяти или выполнить выравнивание вручную (выделив больше памяти и затем "выровняв" значение указателя).