10

编辑:ST 不允许为新手发布两个以上的链接。很抱歉缺少参考资料。

我正在尝试减少 C 应用程序中的锁定开销,其中检测全局状态的更改与性能相关。尽管我最近读了很多关于这个主题的书(例如,很多来自 H. Sutter 的书,等等),但我对自己的实施没有信心。我想使用类似CAS的操作和DCL的组合来检查Cache-Line Aligned全局变量,从而避免错误共享,以从多个线程之间共享的数据更新线程本地数据。我缺乏信心主要是因为

  1. 我未能解释关于类型属性的 GNU 文档
  2. 我似乎无法找到任何可以轻松翻译为 C 的文献和示例,例如ST 或1上的 aligning-to-cache-line-and-knowing-the-cache-line-size(尽管1似乎可以回答我的问题有点我对我的实施没有信心)
  3. 我对 C 的经验有限

我的问题:

  1. 类型属性文档指出:

    此属性为指定类型的变量指定最小对齐方式(以字节为单位)。例如,声明:

    (请参阅类型属性文档进行声明)

    强制编译器确保(尽可能地)每个变量的类型已经struct Smore_aligned_int将被分配并至少在8-byte边界上对齐。在 SPARC 上,将所有类型的变量与边界struct S对齐8-byte允许编译器在将一个 struct S 类型的变量复制到另一个变量时使用 ldd 和 std(双字加载和存储)指令,从而提高运行时效率。

    这是否意味着开始struct Smore_aligned_int将始终与8-byte边界对齐?这并不意味着数据将被填充为正好使用 64 个字节,对吧?

  2. 假设 1. 的每个实例struct cache_line_aligned(参见下面的代码示例 1)在边界上对齐64-byte并恰好使用一个高速缓存行(假设高速缓存行的64 bytes长度)为真

  3. 使用typedef类型声明不会改变的语义__attribute__ ((aligned (64)))(参见下面的代码示例 2

  4. 如果aligned_mallocstruct 声明为__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
  }
} 

对不起,很长的帖子。

谢谢!

4

1 回答 1

7

当您定义对齐类型时,例如,对齐到 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,不是viamalloc()等),就像上面的代码一样,它会被对齐。

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).

Whereany type of object不包括像上述结构这样的人为对齐/填充类型,因为__attribute__ ((aligned (64)))在 C 标准中没有任何东西。这是这里的 GNU 扩展。对于具有任意对齐的动态分配的对象,您必须使用适当的内存分配函数或手动进行对齐(通过分配更多内存然后“对齐”指针值)。

于 2012-09-26T06:03:50.187 回答