Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 访问单个结构成员是否会将整个结构拉入缓存?_C_Linux_Memory_Caching - Fatal编程技术网

C 访问单个结构成员是否会将整个结构拉入缓存?

C 访问单个结构成员是否会将整个结构拉入缓存?,c,linux,memory,caching,C,Linux,Memory,Caching,我一直在读Ulrich Drepper的“”,在第节(页面的一半)中,它给我的印象是,访问结构的任何成员都会导致整个结构被拉入CPU缓存 这是正确的吗?如果是,硬件如何知道这些结构的布局?或者编译器生成的代码以某种方式强制加载整个结构 或者,使用较大结构的速度变慢主要是由于结构分布在更多内存页上导致TLB未命中所致 Drepper使用的示例结构为: struct l { struct l *n; long int pad[NPAD]; }; 其中,sizeof(l)由N

我一直在读Ulrich Drepper的“”,在第节(页面的一半)中,它给我的印象是,访问结构的任何成员都会导致整个结构被拉入CPU缓存

这是正确的吗?如果是,硬件如何知道这些结构的布局?或者编译器生成的代码以某种方式强制加载整个结构

或者,使用较大结构的速度变慢主要是由于结构分布在更多内存页上导致TLB未命中所致

Drepper使用的示例结构为:

  struct l {
    struct l *n;
    long int pad[NPAD];
  };
其中,
sizeof(l)
NPAD
等于0、7、15或31确定,从而产生相隔0、56、120和248字节的结构,并假设缓存线为64字节和4k页


随着结构的增长,仅仅在链表中进行迭代会大大降低速度,即使实际上只访问指针

硬件不知道结构的布局,只是将访问成员周围的大量字节加载到缓存中。是的,较大结构的减速是因为它们将分布在更多的缓存线上。

通常,一级缓存使用特定数量的字节(一个,大小通常在8到512字节之间)来访问
结构的成员。由于所有的
struct
成员在内存中并排对齐,因此整个结构进入缓存的可能性有点大(取决于
sizeof(struct your_struct)
)…

访问结构成员不会比访问内存中的任何其他区域造成更多的性能损失。事实上,如果在同一区域中访问多个结构成员,可能会有性能改进,因为第一次访问可能会缓存其他成员。

硬件根本不知道该结构。但确实,缓存中的硬件负载在实际访问的字节周围有一些字节。这是因为缓存线的大小不同。它不适用于逐字节访问,但适用于每次16个字节的大小

在对结构的成员进行排序时必须小心,以便经常使用的成员彼此接近。例如,如果您具有以下结构:

struct S {
  int foo;
  char name[64];
  int bar;
};
如果经常使用成员变量foo和bar,硬件将在缓存中加载foo周围的字节,当您访问bar时,它将不得不加载bar周围的字节。即使foo和bar周围的字节从未使用过。现在按如下方式重写结构:

struct S {
  int foo;
  int bar;
  char name[64];
};
当您使用foo时,硬件将在缓存中加载foo周围的字节。使用bar时,bar已经在缓存中,因为bar包含在foo周围的字节中。CPU不必等待bar进入缓存


答案是:访问单个结构成员并不会将整个结构拉入缓存,而是将结构的其他成员拉入缓存。

虽然CPU可以轻松处理负载并存储小到一个字节的数据,但缓存只处理“缓存线”大小的数据。在计算机体系结构教科书中,这也被称为“块大小”

在大多数系统上,这是32或64字节。从一个CPU到下一个CPU,有时甚至从一个缓存级别到下一个缓存级别,它都可能不同

此外,一些CPU执行推测性预取;这意味着,如果您按顺序访问缓存线5和6,它将尝试加载缓存线7,而无需您的请求。

“随着结构的增长,仅在链接列表中进行迭代会大大降低速度,即使实际访问的只是指针。”

当NPAD=0时,每个缓存线包含8个列表节点,因此您可以了解为什么这是最快的

当NPAD=7、15、31时,每个列表节点只需加载一条缓存线,您可能希望它们的速度都相同—每个节点一次缓存未命中。但现代内存管理器将进行推测性缓存。如果它有备用容量(可能是这样,因为现代内存可以与主内存并行执行多个读取),那么它将开始加载靠近正在使用的内存的内存。虽然它是一个链表,但如果您以任何明显的方式构造它,那么很有可能您是按顺序访问内存的。因此,列表节点在内存中的距离越近,缓存就越可能成功地满足您的需要


在最坏的情况下,当您使用内存时,您的程序会受到磁盘I/O的限制。您在列表中的进度可能完全取决于每页有多少个节点,并且您可能会看到所花费的时间与节点的大小成正比,高达4k。不过,我还没有尝试过,操作系统在交换方面也会很聪明,就像MMU在主内存方面很聪明一样,所以它不一定那么简单。

这是完全正确的。这里的奇特概念是引用的局部性。因此,当你说“跨越更多缓存线”时,你的意思是,除了TLB未命中之外,预取周围结构的未使用部分也会导致部分速度减慢。@Robert:我认为它可以用两种不同的方式应用:1。单个结构太大,无法轻松放入单个缓存页。如果你“到处摸”(听起来很脏),可能会导致多页抓取。2.对于更大的结构,只要读取任何单个缓存页,就可以将更少的结构放入缓存,从而增加所需的下一个结构不在缓存中的可能性。这里的一个基本问题是引用的局部性。如果你在内存中跳跃,你将会有更多的缓存未命中。了解您的访问模式并进行相应的设计。