ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux concurrency - 2.barrier

2022-01-23 17:33:44  阅读:206  来源: 互联网

标签:barrier Linux smp volatile concurrency memory CPU compiler


现在的compiler与CPU为了最佳化执行效能,必要时可能重新安排执行程式的流程顺序。

1.compiler最佳化可依据CPU的instruction issue数目,执行的latency cycles以及程式流程,在不影响程式上下文结果下重排或简化程式。

2.硬件设计最佳化:

  • multiple issue of instructions:一个cycle可以执行多条指令.
  • out-of-order execution:如果某一条指令stall等待之前的结果时,CPU可以先执行下一条没有相依性的指令。
  • speculation:当CPU遇到一些条件十判断的指令时,在判断出结果前可以先预测性的执行可能path以求得performance gain。
  • speculative loads: 在cacheable region, load instrcution 真正被执行前就猜测性的先把资料读进cache。
  • load and store optimizations:读写外部存储会花很长时间且可能stall pipline等待结果,CPU可尽量减少传输次数提高performance,例如合并数据相邻地址的store成一笔。
  • external memory systems:在许多SOC系统中,有许多不同的master、slave,以及之间不同的routing。有些device同时接受来自不同的master的资料传输请求。这些传输的transaction有可能在interconnection之间被buffer或时recorder。

基于以上原因,你的程式的执行顺序与流程可能在编译时被重排或者修改,CPU执行结果的出现顺序又可能与assembly看到的不同,compiler跟CPU只会保证执行结果上下文时正确的。这在SMP环境下,其他CPU或是IO因为上述因素可能得到非预期的执行结果。我们需要在某些CPU/CPU或CPU/IO之间需要通过的地址确保compiler与CPU的执行顺序,memory barrier提供这样的功能。

compiler support

1.volatile keyword:volatile是一个type qualifier。它声明锁修饰的变量的值有可能被memory-mapped IO或者是asynchronously interrupting function修改,这个关键字告诉compiler不要针对此变量的存取做最佳化。你可以对变量设定volatile,但是他对所有存取这个变量的地方都会造成效果。这造成能效的减损。Linux kernel里面提供ACCESS_ONCE()macro在使用上进一步优化,只在需要的地方才套用volatile这个关键字,保留给programmer更多弹性:

#define ACCESS_ONCE(x) (*(volatile typeof(x)) &(x))

基本上就是在有需要阻止最佳化的地方通过类型转换来增加volatile修饰:

static int rcu_gp_in_progress(struct rcu_state *rsp)
{
  return (ACCESS_ONCE(rsp->completed) != ACCESS_ONCE(rsp->gpnum));
}

表示我们希望读取rsp->completedrsp->gpnum动作不要被最优化。

compiler barrier:compiler barrier 本身是一個 sequence point

int A, B;
void foo()
{
    A = B + 1;
    B = 0;
}

有可能在加了-O2被 reorder 成

B = 0;
A = B + 1;

GCC 使用下列的 inline assembly 來表示 compiler barrier

asm volatile("" ::: "memory");

如同上面的程式碼改寫成以下的方式,我們可以確保編譯時期保持預期的順序。

A = B + 1;
asm volatile("" ::: "memory");
B = 0;

Linux kernel 在include/linux/compiler-gcc.h 中定義 compiler barrier macro barrier() 。

#define barrier() __asm__ __volatile__("": : :"memory")

CPU barrier

每个CPU architecture根据各自的memory model,通常会提供自己的barrier instruction,以达到不同程度的读写顺序的保证。如ARM dmb等,有的有不同程度与作用范围的barrier,以达到细读控制。

在 Linux 中,這些指令在 arch 下被包成通用的介面,分類介紹如下

  • mb()/rmb()/wmb():rmb() 確保 barrier 之前的 read operation 都能在 barrier 之後的 read operation 之前發生,簡單來說就是確保 barrier 前後的 read operation 的順序;wmb() 如同 rmb() 但是只針對 write operation。mb() 則是針對所有的 memory access。
  • smp_mb()/smp_rmb()/smp_wmb():在 SMP 的系統被定義成 mb()/rmb()/wmb() ,UP 時就只是 compiler barrier。可特別用在只於 SMP 時才需要 barrier 的地方[8]。
  • dma_rmb/dma_wmb():如果 architecture 對於 barrier 作用的範圍有提供更 fine-grained 的控制,在 device driver 中需要同步 CPU 與 IO 中的 memory data 時 我們就不需要使用作用達到整個系統的 barrier。
  • smp_load_acquire()/smp_store_release(): 这部分单向的barrier。ACQUIRE确保之后所有memory operation都只在ACQUIRE之后出现;RLEASE则是确保之前的所有memory operation都在RELEASE之前出现。通常这两个承兑出现。通过两个marco,确保之前的critical section之内的变量存取都在这次critical section前完成。
  • read_barrier_depends()/smp_read_barrier_depends():只有在 barrier 上下的資料 存取有相依性時才有作用,這樣我們就可以避免使用 rmb() 達到更輕量的控制。但是這 只有 ALPHA CPU 才有支援,其他的 architecture 都是定義成空巨集。
  • smp_mb__before_atomic()/smp_mb__after_atomic():在某些沒有 return value 的 atomic operation 中有些沒有使用 memory barrier。這兩個 macro 讓我們在這些操作前 後確保資料一致性。

Barrier 在需要時幫助我們達到記憶體存取的順序的準確與可預測性。但是相對的它也減 低了原本效能最佳化的好處。它在其它 Concurrency 機制的實現上是個不可或缺的角色!

标签:barrier,Linux,smp,volatile,concurrency,memory,CPU,compiler
来源: https://www.cnblogs.com/ellabrain/p/15836843.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有