char x[2048] 和缓存行问题
char x[2048] and cache line issue
下面是简单的c源码,其中char x[2048]
是一个全局变量,func1
被thread1
调用,func2
被[=15调用=]:
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中,一个cache line有64字节,所以x应该占32
缓存行,我的问题是:
当 thread1
调用 func1
时,是否所有 32 个缓存行都可用于该 CPU 缓存直到那时 strcpy
? (或)编译器知道只有一个高速缓存行就足以完成这项工作?
当 thread2
调用 func2
时,是否所有 32 个缓存行都可用于该 CPU 缓存直到那时 printf
? (或)编译器可以识别一个缓存行是否足够?
在 x86 和 x64(以及现代 ARM 和其他常见 CPU 上),缓存对用户模式程序是完全透明的。
因此,strcpy
执行第一次读取,CPU 自动拉入一个缓存行,strcpy
在 [=12=]
上退出,完成。与 printf("%s",x)
.
相同
我建议您阅读维基百科页面:https://en.wikipedia.org/wiki/CPU_cache
一些背景:
- 通常情况下,缓存行($L) 对程序是透明的。所以大多数程序员不直接处理缓存行(将其引入,将其踢出)。 CPU 一旦发现 code/data 不在 $L 中,就会停止这种内存访问并按需引入 $L。
- 虽然有编码技术可以在代码中将数据引入缓存行(例如通过预取指令),但通常编译器不会足够聪明地为您执行此操作,因为它可能预取得太早(所以到时候$L 已被使用,它已经被踢出),或者为时已晚(CPU 仍然需要暂停内存访问)。
回答你的问题:
- 没有。编译器不知道需要引入多少$L(它怎么知道一段数据是否已经在$L 中,所以安全起见,不要自取其辱)。编译器只是发出,例如MOV指令,CPU,在执行这条指令时,发现操作数不在$中,所以会按需带入。由于您的编程只复制到 '\0',所以 $L 引入停止在那里。
- 同#1。只有读取的 $L 才会被引入,编译器与此无关。
更多信息:
- CPU 预取器可能会带来当前需要的额外 $L。例如,它可能会引入下一个 $L 并希望获得数据局部性。
- 一些高级程序使用预取指令来提高程序性能。假设您知道您的代码将在不久的将来访问某个位置,您可以预取它,并且在您需要它时它已经存在,因此不会招致 $L 未命中惩罚。但是很难做到正确(您必须知道代码的内存访问模式并将预取指令插入正确的位置。一些高性能代码设计软件管道来执行此操作,但这又是一个高级主题)。
下面是简单的c源码,其中char x[2048]
是一个全局变量,func1
被thread1
调用,func2
被[=15调用=]:
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中,一个cache line有64字节,所以x应该占32 缓存行,我的问题是:
当
thread1
调用func1
时,是否所有 32 个缓存行都可用于该 CPU 缓存直到那时strcpy
? (或)编译器知道只有一个高速缓存行就足以完成这项工作?当
thread2
调用func2
时,是否所有 32 个缓存行都可用于该 CPU 缓存直到那时printf
? (或)编译器可以识别一个缓存行是否足够?
在 x86 和 x64(以及现代 ARM 和其他常见 CPU 上),缓存对用户模式程序是完全透明的。
因此,strcpy
执行第一次读取,CPU 自动拉入一个缓存行,strcpy
在 [=12=]
上退出,完成。与 printf("%s",x)
.
我建议您阅读维基百科页面:https://en.wikipedia.org/wiki/CPU_cache
一些背景:
- 通常情况下,缓存行($L) 对程序是透明的。所以大多数程序员不直接处理缓存行(将其引入,将其踢出)。 CPU 一旦发现 code/data 不在 $L 中,就会停止这种内存访问并按需引入 $L。
- 虽然有编码技术可以在代码中将数据引入缓存行(例如通过预取指令),但通常编译器不会足够聪明地为您执行此操作,因为它可能预取得太早(所以到时候$L 已被使用,它已经被踢出),或者为时已晚(CPU 仍然需要暂停内存访问)。
回答你的问题:
- 没有。编译器不知道需要引入多少$L(它怎么知道一段数据是否已经在$L 中,所以安全起见,不要自取其辱)。编译器只是发出,例如MOV指令,CPU,在执行这条指令时,发现操作数不在$中,所以会按需带入。由于您的编程只复制到 '\0',所以 $L 引入停止在那里。
- 同#1。只有读取的 $L 才会被引入,编译器与此无关。
更多信息:
- CPU 预取器可能会带来当前需要的额外 $L。例如,它可能会引入下一个 $L 并希望获得数据局部性。
- 一些高级程序使用预取指令来提高程序性能。假设您知道您的代码将在不久的将来访问某个位置,您可以预取它,并且在您需要它时它已经存在,因此不会招致 $L 未命中惩罚。但是很难做到正确(您必须知道代码的内存访问模式并将预取指令插入正确的位置。一些高性能代码设计软件管道来执行此操作,但这又是一个高级主题)。