线程缓存使用、多核代码性能及缓存术语相关技术咨询
代码多核扩展性差的原因及相关疑问解答
相关代码
#include <stdlib.h> #include <stdio.h> #include <omp.h> int main(int argc, char *argv[]) { long long int tosses = atoll(argv[1]); long long int hits = 0; int threads = atoi(argv[2]); double start, end; int i; unsigned int seeds[threads]; for (i = 0; i < threads; i++) seeds[i] = i + 1; start = omp_get_wtime(); #pragma omp parallel reduction(+:hits) num_threads(threads) { int myrank = omp_get_thread_num(); long long int local_hits = 0, toss; double x, y; #pragma omp for for (toss = 0; toss < tosses; toss++) { x = rand_r(&seeds[myrank])/(double)RAND_MAX * 2 - 1; y = rand_r(&seeds[myrank])/(double)RAND_MAX * 2 - 1; if (x*x + y*y < 1) local_hits++; } hits += local_hits; } end = omp_get_wtime(); printf("Pi: %f\n", 4.0 * hits / tosses); printf("Duration: %f\n", end-start); return 0; }
原问题:为何这段代码在多核环境下扩展性极差?
这段代码扩展性差的核心原因是伪共享(False Sharing):
代码中所有线程的种子存储在数组seeds中,每个种子是4字节的整型,而系统cache line为64字节,意味着单个cache line会容纳16个连续的种子。当多个线程同时修改各自的种子时,哪怕只修改其中一个,整个cache line会被标记为失效,其他共享同一cache line的线程需要重新从主存加载该cache line,频繁的缓存失效和主存访问会大幅拖慢性能,导致多核扩展时性能不升反降甚至变差。
具体疑问解答
1. 上述表述想传达什么?第17/33个核心的cache line同样可能失效,这与1-16核心有何不同?
先看你提到的这段表述:
当核心数超过16或32时,性能会有小幅提升。种子为整型值,占4字节内存。本系统的cache line为64字节,因此单个cache line可容纳16个种子。当线程数增至17/33时,新增种子会单独占用一个cache line,避免线程进一步受阻。
这段表述讲的是伪共享的部分缓解逻辑:
- 当线程数在1-16时,所有线程的种子都挤在1个或多个满的cache line里(比如16个种子占1行,32个占2行),每个线程修改自己的种子都会导致所在cache line失效,同一行里的其他线程都要重新加载,伪共享冲突非常频繁。
- 当线程数增至17或33时,新增的第17/33个种子会单独占用一个新的cache line(因为前16/32个已经占满了整数行),这个线程修改自己的种子时,只会让自己的cache line失效,不会影响其他线程的cache line,避免了更严重的伪共享冲突,因此性能会有小幅提升。
简单说,1-16线程是“多个线程抢用同一cache line”,17+线程里新增的线程是“独占cache line”,冲突范围缩小了。
2. 线程的独立内存(栈内存/私有内存)属于cache memory还是主内存?
线程的栈内存、私有内存本质上是主内存的一部分,只是CPU会把频繁访问的私有内存数据加载到cache memory中做高速缓存。cache是主存的一层高速缓冲,不是独立的内存空间,私有内存的物理存储在主存,但会被缓存到cache以提升访问速度。
3. 如何在cache memory层面关联cache line与block?
- cache line是CPU cache的最小数据操作单位(这里是64字节),**block(内存块)**是主存中与cache line对应的等量数据块。
- 当CPU需要访问主存中的某块数据时,会把整个block(大小等于cache line)加载到cache的一个空闲cache line中。后续对该block内数据的访问,都会直接操作cache line中的缓存数据,直到该cache line失效被替换。简单来说,cache line和主存block是一一对应的映射关系,大小完全相同,cache line是block在cache中的缓存副本。
内容的提问来源于stack exchange,提问作者Jeet
相关产品推荐
相关产品推荐

