淘宝面试题cacheline

本文深入探讨了缓存行对齐的概念及其在高性能服务器代码中的应用。详细解释了缓存行(cacheline)的基本原理,包括其大小、作用以及为何需要进行对齐。通过具体示例说明了在多线程环境中如何避免虚假共享(false sharing),并介绍了几种实现缓存行对齐的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在看淘宝之前的一道面试题目,内容是在高性能服务器的代码中经常会看到类似这样的代码:[cpp] view plaincopytypedef union { erts_smp_rwmtx_t rwmtx; byte cache_line_align_[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_smp_rwmtx_t))]; }erts_meta_main_tab_lock_t; erts_meta_main_tab_lock_t main_tab_lock[16]; 请问其中用来填充的cache_line_align的作用是?之前有学习到c语言中宏align是内存补齐的作用,那这个不就是cache line补齐?但是啥是cache line??为啥有这么一步?1.首先,什么是cache line?CPU处理指令时,由于“Locality of Reference”原因,需要决定哪些数据需要加载到CPU的缓存中,以及如何预加载。因为不同的处理器有不同的规范,导致这部分工作具有不确定性。在加载的过程中,涉及到一个非常关键的术语:cache line。cache line是能被cache处理的内存chunks,chunk的大小即为cache line size,典型的大小为32,64及128 bytes. cache能处理的内存大小除以cache line size即为cache line。了解了cache line,然后再熟悉一下cpu上cache的一些策略2.cpu上cache的策略cache entry (cache条目)包含如下部分1) cache line : 从主存一次copy的数据大小)2) tag : 标记cache line对应的主存的地址3) falg : 标记当前cache line是否invalid, 如果是数据cache, 还有是否dirtycpu访问主存的规律1) cpu从来都不直接访问主存, 都是通过cache间接访问主存2) 每次需要访问主存时, 遍历一遍全部cache line, 查找主存的地址是否在某个cache line中.3) 如果cache中没有找到, 则分配一个新的cache entry, 把主存的内存copy到cache line中, 再从cache line中读取.cache中包含的cache entry条目有限, 所以, 必须有合适的cache淘汰策略一般使用的是LRU策略.将一些主存区域标记为non-cacheble, 可以提高cache命中率, 降低没用的cache回写策略cache中的数据更新后,需要回写到主存, 回写的时机有多种1) 每次更新都回写. write-through cache2) 更新后不回写,标记为dirty, 仅当cache entry被evict时才回写3) 更新后, 把cache entry送如回写队列, 待队列收集到多个entry时批量回写.cache一致性问题有两种情况可能导致cache中的数据过期1) DMA, 有其他设备直接更新主存的数据2) SMP, 同一个cache line存在多个CPU各自的cache中. 其中一个CPU对其进行了更新.3.为啥需要cache line 补齐呢?让我们先看一个例子,举例:[cpp] view plaincopy// 如下代码在SMP环境下存在cache频繁刷新问题 double sum=0.0, sum_local[NUM_THREADS]; #pragma omp parallel num_threads(NUM_THREADS) { int me = omp_get_thread_num(); sum_local[me] = 0.0; #pragma omp for for (i = 0; i < N; i++) sum_local[me] += x[i] * y[i]; #pragma omp atomic sum += sum_local[me]; } 因为sum_local数组是个全局变量, 多个线程都会访问, 并且, 各个线程访问的地方很接近, 会导致一个线程更新, 其他CPU的cache line失效. 所以在尽量不要让更新频率非常高(例如,计数器)和经常访问的变量分布在同一个cache line中,以避免“cache ping-pong”,亦“false sharing”现象。 OK,为啥需要补齐呢,上面的例子里面多个线程的访问会出现false sharing现象,如果服务器采用这样的,则服务器性能会严重影响,为了解决这个问题,最简单的办法是采用cache line 补齐的方法。ps:在查找这个面试题的时候,有意思的是我在淘宝核心系统团队博客上发现了对这个题目的解答,我觉得简答的不是很认真,他们是参考一篇外文文献《Avoiding and Identifying False Sharing Among Threads》,这篇文章主要解决在SMP环境下cache line被频繁刷新的的问题。所以只是简单的将大意翻译过来。将复制过来:在做多线程程序的时候,为了避免使用锁,我们通常会采用这样的数据结构:根据线程的数目,安排一个数组, 每个线程一个项,互相不冲突. 从逻辑上看这样的设计无懈可击,但是实践的过程我们会发现这样并没有提高速度. 问题在于cpu的cache line. 我们在读主存的时候,数据同时被读到L1,L2中去,而且在L1中是以cache line(通常64)字节为单位的. 每个Core都有自己的L1,L2,所以每个线程在读取自己的项的时候, 也把别人的项读进去, 所以在更新的时候,为了保持数据的一致性, core之间cache要进行同步, 这个会导致严重的性能问题. 这就是所谓的False sharing问题, 有兴趣的同学可以wiki下.解决方法很简单:把每个项凑齐cache line的长度,实现隔离.12345678typedefunion { erts_smp_rwmtx_t rwmtx; byte cache_line_align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE( sizeof(erts_smp_rwmtx_t))];} erts_meta_main_tab_lock_t;或者_declspec (align(64)) intthread1_global_variable;__declspec(align(64)) intthread2_global_variable;这就是为什么在高性能服务器中到处看到cache_line_align, 号称是避免cache的trash.类似valgrind和intel vtune的工具可以做这个层次的性能微调.(淘宝核心系统博客:http://rdc.taobao.com/blog/cs/?p=523)(英文文献资料网址:http://software.intel.com/en-us/articles/avoiding-and-identifying-false-sharing-among-threads/ 或者http://software.intel.com/sites/default/files/m/d/4/1/d/8/3-4-MemMgt_-_Avoiding_and_Identifying_False_Sharing_Among_Threads.pdf)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值