由一道淘宝面试题到False sharing问题

发布时间:2017-1-17 14:54:59 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"由一道淘宝面试题到False sharing问题",主要涉及到由一道淘宝面试题到False sharing问题方面的内容,对于由一道淘宝面试题到False sharing问题感兴趣的同学可以参考一下。

今天在看淘宝之前的一道面试题目,内容是 在高性能服务器的代码中经常会看到类似这样的代码: typedef 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, 还有是否dirty cpu访问主存的规律 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 cache 2) 更新后不回写,标记为dirty, 仅当cache entry被evict时才回写 3) 更新后, 把cache entry送如回写队列, 待队列收集到多个entry时批量回写. cache一致性问题 有两种情况可能导致cache中的数据过期 1) DMA, 有其他设备直接更新主存的数据 2) SMP, 同一个cache line存在多个CPU各自的cache中. 其中一个CPU对其进行了更新. 3.为啥需要cache line 补齐呢? 让我们先看一个例子, 举例: // 如下代码在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的长度,实现隔离. 1 2 3 4 5 6 7 8 typedef 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; 或者 _declspec (align(64)) int thread1_global_variable; __declspec (align(64)) int thread2_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)    

上一篇:黑马程序员--异常在子父类覆盖中的体现
下一篇:敏捷测试需注意的五种危险行为

相关文章

相关评论