当前位置: 首页 > news >正文

如何做x响应式网站免费制作招聘的app

如何做x响应式网站,免费制作招聘的app,医疗云网站建设,35互联做的网站如何文章目录 1. 前言2. 编译器内存屏障2.1 编译器内存访问重排序规则2.2 编译器屏障的几种形式2.2.1 显式编译器屏障2.2.2 隐式编译器屏障2.2.3 硬件内存屏障充当编译屏障2.2.4 编程语言内存模型提供的编译屏障 2.3 编译器内存屏障实例2.3.1 Linux spinlock 3. 结语4. 参考资料 1.… 文章目录 1. 前言2. 编译器内存屏障2.1 编译器内存访问重排序规则2.2 编译器屏障的几种形式2.2.1 显式编译器屏障2.2.2 隐式编译器屏障2.2.3 硬件内存屏障充当编译屏障2.2.4 编程语言内存模型提供的编译屏障 2.3 编译器内存屏障实例2.3.1 Linux spinlock 3. 结语4. 参考资料 1. 前言 2. 编译器内存屏障 编译屏障相对于运行时的硬件屏障它是一种编译时行为其目的是阻止编译器对内存访问实行程序员不期望的优化行为如存储访问合并、将循环内的读取行为移动到循环外等等。 2.1 编译器内存访问重排序规则 虽然编译器可以优化代码的存储访问以得到更好的性能但编译器开发人员和CPU 供应商普遍应遵循的内存访问重排序的一个基本规则该规则可以表述为不得修改单线程程序的行为。这条基本规则的意思是不管是编译时的编译器内存访问重排序还是运行时的 CPU 内存访问重排序都要在不改变单线程行为的前提下进行。如有以下代码 int x, y;void foo(void) {x y 1;y 0; }假定代码通过如下版本 ARM GCC 编译器(这也是本文所有示范代码采用的编译器)进行编译 $ arm-linux-gnueabi-gcc --version arm-linux-gnueabi-gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.在不启用优化的情形下编译会生成如下汇编代码(只截取函数 foo() 对应部分) $ arm-linux-gnueabi-gcc -S foo.c/* foo.s */....type foo, %function foo: args 0, pretend 0, frame 0 frame_needed 1, uses_anonymous_args 0 link register save eliminated.str fp, [sp, #-4]!add fp, sp, #0ldr r3, .L2 // r3 yldr r3, [r3] // r3 yadd r3, r3, #1 // r3 y 1ldr r2, .L24 // r2 xstr r3, [r2] // (1) x y 1ldr r3, .L2 // r3 ymov r2, #0 // r2 0str r2, [r3] // (2) y 0nopsub sp, fp, #0 sp neededldr fp, [sp], #4bx lr .L3:.align 2 .L2:.word y.word x ...从上面编译器生成的汇编代码代码我们看到在上面 (1) 和 (2) 处先 x y 1; 后 y 0; 是符合编程顺序的。再看一下在(加上 -O2 编译选项)启用编译器优化的情形下会生成怎样的汇编代码(只截取函数 foo() 对应部分) $ arm-linux-gnueabi-gcc -O2 -S foo.c/* foo.s */....type foo, %function foo: args 0, pretend 0, frame 0 frame_needed 0, uses_anonymous_args 0 link register save eliminated.mov r0, #0ldr r2, .L2 // r2 yldr r1, .L24 // r1 xldr r3, [r2] // r3 ystr r0, [r2] // (1) x 0 add r3, r3, #1 // r3 y 1str r3, [r1] // (2) x y 1bx lr .L3:.align 2 .L2:.word y.word x ...我们看到优化后的汇编代码在 (1) 和 (2) 处将 x 0; 的赋值操作放在了 x y 1 的赋值操作之前这和前面的编程顺序不一致也即编译器出现了对存储访问的重排序。由于 y 的初始值为0所以 x 0; 和 x y 1 无论哪个在前执行在单线程执行环境下都不影响结果这遵循前面提到的规则。 由于这条规则编写单线程代码的程序员基本上不会注意到内存重排序。在多线程编程中它也经常被忽视因为互斥锁、信号量自带编译屏障功能可以防止在它们周围的内存操作重排序。只有当使用无锁技术时当内存在线程之间共享而没有任何互斥操作时可以清楚地观察到内存重排序的效果。编译器的内存访问重排序通常只在启用优化的时候发生。在单处理器系统上编译屏障已经足够保障对数据的并发访问安全因为不会出现真正的并行访问毕竟系统中只有一个处理器。 2.2 编译器屏障的几种形式 2.2.1 显式编译器屏障 以 GCC 编译器为例来说明显式编译器屏障其定义如下 __asm__ volatile( ::: memory)memory 阻止将变量缓存到寄存器强制从内存读取同时如果访问变量既没有出现在 __asm__ 语句的 input 部分也没有出现在 __asm__ 语句的 output 部分则还要加上 __volatile__ 关键字强制从内存读取。__volatile__ 关键字同时也阻止了编译移动语句(譬如移出循环)指令重排。 编译器(显式)屏障的作用就是阻止对其前后的内存访问进行重排序。还是以章节 2.1 的代码为例在其中插入插入一个显式编译器屏障仍然用 -O2 优化选项进行编译看是否能阻止对 x y 1; 和 y 0; 的重排序。修改后的代码如下 int x, y;void foo(void) {x y 1;asm volatile( ::: memory); // 插入一个编译屏障y 0; }再看一下在(加上 -O2 编译选项)启用编译器优化的情形下会生成怎样的汇编代码(只截取函数 foo() 对应部分) $ arm-linux-gnueabi-gcc -O2 -S foo.c....type foo, %function foo: args 0, pretend 0, frame 0 frame_needed 0, uses_anonymous_args 0 link register save eliminated. A B 1;ldr r2, .L2 // r2 yldr r1, .L24 // r1 xldr r3, [r2] // r3 yadd r3, r3, #1 // r3 y 1str r3, [r1] // (1) x y 1mov r3, #0 // r3 0str r3, [r2] // (2) y 0bx lr .L3:.align 2 .L2:.word y.word x ...我们看到在加入(显式)编译器屏障后优化后的汇编代码在 (1) 和 (2) 处已经按编程顺序执行了对 x 和 y 的存储访问这是编译屏障起了作用。 2.2.2 隐式编译器屏障 在代码里面的一些 sequence points 如函数调用、对 volatile 变量的访问等可以作为隐式编译屏障。事实上大多数函数调用都可充当编译器屏障无论它们自身是否包含编译器屏障但这不包括内联函数、使用 pure 属性声明的函数以及使用链接时生成的代码。另外带编译屏障的函数哪怕内联了也作为编译屏障使用。下面来看函数做为隐式编译器屏障的一个例子 int x, y;void foo(void) {x y 1;y 0; }struct foo_data {int bar, bar2; };void do_something(struct foo_data *foo_data) {foo_data-bar 5;foo();foo_data-bar2 foo_data-bar; }开启 -O2 选项进行编译 $ arm-linux-gnueabi-gcc -O2 -S foo.c生成如下汇编代码(只截取相关部分) ....type do_something, %function do_something: args 0, pretend 0, frame 0 frame_needed 0, uses_anonymous_args 0str lr, [sp, #-4]! // 保存 lr 到堆栈mov r1, #5 // r1 5mov lr, #0 // lr 0ldr r2, .L6 // r2 yldr ip, .L64 // ip xldr r3, [r2] // r3 ystr r1, [r0, #4] // (1) foo_data-bar2 5add r3, r3, #1 // r3 y 1str r1, [r0] // foo_data-bar 5 (2)str lr, [r2] // y 0str r3, [ip] // x y 1ldr pc, [sp], #4 // 从 do_something() 函数返回 .L7:.align 2 .L6:.word y.word x ...从汇编代码看到函数 foo() 已经被内联到函数 do_something() 内且 在 (1) 和 (2) 处foo_data-bar 5; 和 foo_data-bar2 foo_data-bar; 的顺序和编程顺序正好相反即产生了编译乱序。为避免这种情况第 1 种方法是阻止函数 foo() 被内联第 2 种方法是在函数 foo() 内插入编译屏障。这里只演示第 1 种方法修改后代码如下 int x, y;void foo(void) __attribute__((noinline)); // 阻止函数 foo() 被内联void foo(void) {x y 1;y 0; }struct foo_data {int bar, bar2; };void do_something(struct foo_data *foo_data) {foo_data-bar 5;foo();foo_data-bar2 foo_data-bar; }开启 -O2 选项进行编译 $ arm-linux-gnueabi-gcc -O2 -S foo.c生成如下汇编代码(只截取相关部分) .type do_something, %function do_something: args 0, pretend 0, frame 0 frame_needed 0, uses_anonymous_args 0mov r3, #5 // r3 5push {r4, lr} // 保存 r4, lr 到堆栈mov r4, r0 // r4 foo_datastr r3, [r0] // (1) foo_data-bar 5;bl foo // 调用函数 foo()函数没有被内联了ldr r3, [r4] // (2) r3 foo_data-barstr r3, [r4, #4] // (3) foo_data-bar2 foo_data-bar;pop {r4, pc} // 从堆栈恢复 r4并从函数返回从 (1) 和 (3) 处看到编译器优化后的代码仍然保持对 foo_data-bar 和 foo_data-bar2 写操作顺序和代码一致另外从 (2) 处看到对 foo_data-bar2 的赋值前重新读取了 foo_data-bar 的值这是合理的因为我们无法知道函数 foo() 是否对 foo_data-bar 进行了修改(譬如函数参数 foo_data 指向一个全局变量而刚好 foo() 修改了它) 除了这些情况之外对外部函数的调用甚至比编译器障碍更强因为编译器不知道函数的副作用是什么。编译器必须忘记它对内存所做的任何假设这些假设可能对该函数可见。仔细想想这很有道理。在上面的代码片段中假设 foo() 实现存在于外部库中编译器如何知道 foo() 不依赖于 foo_data-bar 的值它如何知道 foo() 不会修改内存中的 foo_data-bar显然编译器不得而知。因此为了遵守内存访问重排序的基本规则它不得围绕对 foo() 这个外部调用对任何内存访问进行重排序。同样在调用完成后它必须从内存中加载 foo_data-bar 的新值而不是假设它仍然等于 5即使启用了优化。 在许多情况下编译器指令重新排序是被禁止的甚至当编译器必须从内存中重新加载某些值时也是如此。这些隐藏的规则构成了人们长期以来一直说 C 中的 valotile 数据类型在正确编写的多线程代码中通常不是必需的重要原因。 2.2.3 硬件内存屏障充当编译屏障 硬件内存屏障也可以充当编译屏障。而在单处理器系统上所有的硬件内存屏障定义都退化为编译屏障。如 Linux 的内存屏障接口在单处理器系统上定义如下 /* include/asm-generic/barrier.h *//** Force strict CPU ordering. And yes, this is required on UP too when were* talking to devices.** Fall back to compiler barriers if nothing better is provided.*/#ifndef mb #define mb() barrier() #endif#ifndef rmb #define rmb() mb() #endif#ifndef wmb #define wmb() mb() #endif...#ifdef CONFIG_SMP/* 多处理器系统定义 */#else/* 单处理器系统定义 */#ifndef smp_mb #define smp_mb() barrier() #endif#ifndef smp_rmb #define smp_rmb() barrier() #endif#ifndef smp_wmb #define smp_wmb() barrier() #endif#endif /* CONFIG_SMP */2.2.4 编程语言内存模型提供的编译屏障 譬如 C引入了自己的内存模式并提供内存屏障接口对存储访问保序。如下面的例子 int value; std::atomicint is_updated(0);void update_value(int x) {value x;// 在这里阻止 value 和 is_updated 存储操作的重排序is_updated.store(1, std::memory_order_release); }2.3 编译器内存屏障实例 2.3.1 Linux spinlock /* kernel/locking/qspinlock.c *//** Per-CPU queue node structures; we can never have more than 4 nested* contexts: task, softirq, hardirq, nmi.** Exactly fits one 64-byte cacheline on a 64-bit architecture.** PV doubles the storage and uses the second cacheline for PV state.*/ /* per-cpu 的数组 mcs_nodes[MAX_NODES] */ static DEFINE_PER_CPU_ALIGNED(struct mcs_spinlock, mcs_nodes[MAX_NODES]);void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) {... queue:node this_cpu_ptr(mcs_nodes[0]); // node 指向当前 CPU 的 mcs_nodes[MAX_NODES] 数组idx node-count; // count 最大值为 MAX_NODEStail encode_tail(smp_processor_id(), idx);node idx; // 使用当前 CPU mcs_nodes[MAX_NODES] 的 mcs_nodes[idx]/** Ensure that we increment the head node-count before initialising* the actual node. If the compiler is kind enough to reorder these* stores, then an IRQ could overwrite our assignments.*/barrier();node-locked 0;node-next NULL;pv_init_node(node); // (1) 修改 node 指向的结构体成员变量值... }在这里由于变量 mcs_nodes[MAX_NODES] 是 per-cpu 的每个 CPU 只会访问自己的变量空间所以不用考虑多个 CPU 并行、并发访问 mcs_nodes[MAX_NODES] 的情形。在当前上下文由于 spin_lock() 已经禁用了当前 CPU 上抢占唯一的并发场景是在中断中也使用同一 spinlock 的情形所以这里只要保障 node-count 操作在对 node 的初始化操作 node-locked 0; node-next NULL; pv_init_node(node); 之前完成那么就不会出现中断中对当前 CPU 数据 mcs_nodes[idx] 的覆写因为 mcs_nodes[MAX_NODES] 是每 CPU 的数据所以不会有多个 CPU 对它的并行访问对它的访问相当于单核场景。所以这里要做的就是简单的插入一个编译屏障 barrier() 就可以了。如果读者难以能理解这里的场景那么可以反过来思考如果对 node 的初始化操作 node-locked 0; 和 node-next NULL; pv_init_node(node); 先于 node-count 发生会变成怎样譬如某个线程刚好执行完了 pv_init_node(node); 修改了 node 的值在 node-count 执行前中断进来了也使用和线程相同的 spinlock然后因为线程中 node-count 还没执行所以中断和线程使用同一个 node然后修改 node 的值这时候前面线程对 node 的修改值就会被中断中的写操作覆写。如果 保证 node-count 执行在前那么线程和中断修改的将会是不同的 node 也就不会出现覆写的情况。 3. 结语 虽然编译乱序为我们带来了很多烦恼但它只影响无锁编程的场合因为带锁的场合锁自身就含有内存屏障语义。 在多 CPU 系统下编译器屏障无法阻止 CPU 带来的存储乱序这需要硬件内存屏障来保护。 如果我们不是编译器的开发者或者对使用的编译的各个细节非常熟悉这时候编译器生成的汇编代码可以指导我们应该怎样、或这哪里插入编译屏障。 4. 参考资料 [1] How can I judge where should I put memory barrier in the code? [2] GCC-Inline-Assembly-HOWTO [3] Memory Ordering at Compile Time [4] Optimization of conditional access to globals: thread-unsafe? [5] https://www.oracle.com/technetwork/server-storage/solarisstudio/documentation/oss-compiler-barriers-176055.pdf [6] https://topic.alibabacloud.com/a/understanding-memories-barrier-memory-barrier_8_8_10252214.html [7] https://yarchive.net/comp/linux/ACCESS_ONCE.html [8] https://yarchive.net/comp/linux/memory_barriers.html [9] https://www.quora.com/Can-you-explain-what-a-compiler-barrier-is [10] https://alibaba-cloud.medium.com/memory-model-and-synchronization-primitive-part-1-memory-barrier-9585e50b4735 [11] https://android.googlesource.com/kernel/msm//android-msm-marlin-3.18-nougat-dr1/Documentation/memory-barriers.txt [12] https://hackmd.io/VIRqdo35SvekIiH4p76B7g/Hy9JyrBw?typeview [13] https://developer.arm.com/documentation/den0024/a/Memory-Ordering/Barriers/Use-of-barriers-in-C-code
http://www.tj-hxxt.cn/news/223491.html

相关文章:

  • 微网站 服务器网站美食建设图片素材
  • 东莞网站seo公司哪家大wordpress 幻灯片标签
  • 如何做垂直网站做网站的图片用什么格式
  • 做网站网站应该注意什么怎样用前端知识制作企业网站
  • 企业网站wordpress茶叶公司网站源码
  • 天津创思佳网络网站制作公司万网查询本地公网ip地址
  • 商务网站建设教学视频建立平台型组织第一步需要做什么
  • 天水市住房和城乡建设局网站四合一做网站
  • 网站后台编辑器不能用个人主页html源码
  • 可以做兼职笔译的网站中信建设网站
  • 自助建站吧网站图怎么做会高清图片
  • 大酒店网站源代码网站设计网页配色
  • 手机平板购物网站的设计背景郑州市域名服务公司
  • 怎么做网站二级页面做义工的网站
  • 珠海市斗门建设局网站网络推广招聘
  • 中国数学外国人做视频网站房价必涨的十大城市
  • 上线了怎么建网站泰安新闻头条最新消息
  • 怎么创建卡密网站乐山市城乡规划建设局网站
  • 外卖网站怎么做北京企业网站案例
  • 高中信息技术课程做网站网站宣传图
  • 学网站维护怎么改一个网站的关键词密度
  • 个人做众筹网站合法吗wordpress更换域名后台登不进去
  • 建设银行儿童网站2345网址导航站
  • 网站设计制作厂家有哪些网站为什么要seo
  • 洛阳微信平台网站建设推荐一个免费网站
  • 小馋网站建设书专业的营销型网站建设价格
  • 苗木网站素材佛山网站建设业务员
  • 企业网站一般要素即墨区城乡建设局网站官网
  • 好的网站有哪些灰色关键词排名代发
  • wordpress付费建站北京城建建设工程有限公司网站