char x [2048]和缓存行问题

时间:2016-04-15 05:23:12

标签: c cpu-cache

以下是简单的c源代码,其中char x[2048]是全局var,func1thread1调用,func2thread2调用:

char x[2048]={0} , y[16]={0};

void func1(){
    strcpy(x,y);
}

void func2(){
    printf("(%s)\n",x);
} 

int main(int argc, char **argv){
    strncpy(y,argv[1],sizeof(y)-1);
} 

在Intel的cpu中,一个缓存行有64个字节,所以x应该占用32个 缓存行,我的问题是:

  1. thread1调用func1时,是否所有32个缓存行都可用于该缓存行,直到那时为strcpy? (或)编译器知道只有一个缓存行足以完成这项工作吗?

  2. thread2调用func2时,是否所有32个缓存行都可用于该缓存,然后执行printf? (或)编译器可以识别一个缓存行就足够了吗?

2 个答案:

答案 0 :(得分:4)

我建议你阅读维基百科页面:https://en.wikipedia.org/wiki/CPU_cache

一些背景知识:

  1. 通常,缓存行($ L)对程序是透明的。所以大多数程序员都不直接处理缓存行(将其置入,踢出)。一旦发现代码/数据不在$ L中,CPU将停止这种内存访问并按需提供$ L.
  2. 虽然有编码技术可以将数据引入代码中的缓存行(例如通过预取指令),但通常编译器不够智能,因为它可能会过早预取(所以当时$ L使用,它已被踢出),或太晚(CPU仍然需要停止访问内存)。
  3. 回答你的问题:

    1. 否。编译器不知道需要引入多少$ Ls(它怎么能知道一块数据是否已经在$ L中,所以只是安全的一面而不是智能本身)。编译器只发出MOV指令和CPU,在执行该指令时发现操作数不在$中,因此会按需提供它们。由于你只编写副本直到'\ 0',所以$ L带来了止损。
    2. 与#1相同。只会读取$ Ls,而编译器与此无关。
    3. 更多信息:

      1. 除了目前需要的内容之外,CPU预取器可能会带来额外的$ Ls。例如,它可能带来下一个$ L,希望数据位置。
      2. 某些高级程序使用预取指令来提高程序性能。假设您知道您的代码将在不久的将来访问某个位置,您可以预取它,并且当您需要它时,它已经存在,因此不会产生$ L未命中罚款。但是很难做到正确(您必须知道代码的内存访问模式并在正确的位置插入预取指令。一些高性能代码设计软件管道来执行此操作,但同样这是一个高级主题。)
      3. https://en.wikipedia.org/wiki/Instruction_prefetch

答案 1 :(得分:0)

在x86和x64(以及现代ARM和其他常见的CPU)上,缓存对用户模式程序完全透明。

结果,strcpy执行第一次读取,CPU自动拉入一个缓存行,strcpy退出\0并完成。与printf("%s",x)相同。