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

网站建设案例 算命网站电子商务网站建设规划开题报告

网站建设案例 算命网站,电子商务网站建设规划开题报告,辽阳网站推广,一元购网站怎么做前言 你清楚下面这几个问题吗#xff1f; 有了内存#xff0c;为什么还需要 CPU Cache#xff1f; CPU 是怎么读写数据的#xff1f; 如何让 CPU 能读取数据更快一些#xff1f; CPU 伪共享是如何发生的#xff1f;又该如何避免#xff1f; CPU 是如何调度任务的 有了内存为什么还需要 CPU Cache CPU 是怎么读写数据的 如何让 CPU 能读取数据更快一些 CPU 伪共享是如何发生的又该如何避免 CPU 是如何调度任务的如果你的任务对响应要求很高你希望它总是能被先调度这该怎么办 … 这篇我们就来回答这些问题。 正文 CPU 如何读写数据的 先来认识 CPU 的架构只有理解了 CPU 的 架构才能更好地理解 CPU 是如何读写数据的对于现代 CPU 的架构图如下 可以看到一个 CPU 里通常会有多个 CPU 核心比如上图中的 1 号和 2 号 CPU 核心并且每个 CPU 核心都有自己的 L1 Cache 和 L2 Cache而 L1 Cache 通常分为 dCache数据缓存 和 iCache指令缓存L3 Cache 则是多个核心共享的这就是 CPU 典型的缓存层次。 上面提到的都是 CPU 内部的 Cache放眼外部的话还会有内存和硬盘这些存储设备共同构成了金字塔存储层次。如下图所示 从上图也可以看到从上往下存储设备的容量会越大而访问速度会越慢。至于每个存储设备的访问延时你可以看下图的表格 你可以看到 CPU 访问 L1 Cache 速度比访问内存快 100 倍这就是为什么 CPU 里会有 L1~L3 Cache 的原因目的就是把 Cache 作为 CPU 与内存之间的缓存层以减少对内存的访问频率。 CPU 从内存中读取数据到 Cache 的时候并不是一个字节一个字节读取而是一块一块的方式来读取数据的这一块一块的数据被称为 CPU Line缓存行所以CPU Line 是 CPU 从内存读取数据到 Cache 的单位。 至于 CPU Line 大小在 Linux 系统可以用下面的方式查看到你可以看我服务器的 L1 Cache Line 大小是 64 字节也就意味着 L1 Cache 一次载入数据的大小是 64 字节。 那么对数组的加载 CPU 就会加载数组里面连续的多个数据到 Cache 里因此我们应该按照物理内存地址分布的顺序去访问元素这样访问数组元素的时候Cache 命中率就会很高于是就能减少从内存读取数据的频率 从而可提高程序的性能。 但是在我们不使用数组而是使用单独的变量的时候则会有 Cache 伪共享的问题Cache 伪共享问题上是一个性能杀手我们应该要规避它。 接下来就来看看 Cache 伪共享是什么又如何避免这个问题 现在假设有一个双核心的 CPU这两个 CPU 核心并行运行着两个不同的线程它们同时从内存中读取两个不同的数据分别是类型为 long 的变量 A 和 B这个两个数据的地址在物理内存上是连续的如果 Cahce Line 的大小是 64 字节并且变量 A 在 Cahce Line 的开头位置那么这两个数据是位于同一个 Cache Line 中又因为 CPU Line 是 CPU 从内存读取数据到 Cache 的单位所以这两个数据会被同时读入到了两个 CPU 核心中各自 Cache 中。 我们来思考一个问题如果这两个不同核心的线程分别修改不同的数据比如 1 号 CPU 核心的线程只修改了 变量 A或 2 号 CPU 核心的线程的线程只修改了变量 B会发生什么呢 分析伪共享的问题 现在我们结合保证多核缓存一致的 MESI 协议来说明这一整个的过程如果你还不知道 MESI 协议你可以看我这篇文章「10 张图打开 CPU 缓存一致性的大门」。 ①. 最开始变量 A 和 B 都还不在 Cache 里面假设 1 号核心绑定了线程 A2 号核心绑定了线程 B线程 A 只会读写变量 A线程 B 只会读写变量 B。 ②. 1 号核心读取变量 A由于 CPU 从内存读取数据到 Cache 的单位是 Cache Line也正好变量 A 和 变量 B 的数据归属于同一个 Cache Line所以 A 和 B 的数据都会被加载到 Cache并将此 Cache Line 标记为「独占」状态。 ③.  接着2 号核心开始从内存里读取变量 B同样的也是读取 Cache Line 大小的数据到 Cache 中此 Cache Line 中的数据也包含了变量 A 和 变量 B此时 1 号和 2 号核心的 Cache Line 状态变为「共享」状态。 ④. 1 号核心需要修改变量 A发现此 Cache Line 的状态是「共享」状态所以先需要通过总线发送消息给 2 号核心通知 2 号核心把 Cache 中对应的 Cache Line 标记为「已失效」状态然后 1 号核心对应的 Cache Line 状态变成「已修改」状态并且修改变量 A。 ⑤. 之后2 号核心需要修改变量 B此时 2 号核心的 Cache 中对应的 Cache Line 是已失效状态另外由于 1 号核心的 Cache 也有此相同的数据且状态为「已修改」状态所以要先把 1 号核心的 Cache 对应的 Cache Line 写回到内存然后 2 号核心再从内存读取 Cache Line 大小的数据到 Cache 中最后把变量 B 修改到 2 号核心的 Cache 中并将状态标记为「已修改」状态。 所以可以发现如果 1 号和 2 号 CPU 核心这样持续交替的分别修改变量 A 和 B就会重复 ④ 和 ⑤ 这两个步骤Cache 并没有起到缓存的效果虽然变量 A 和 B 之间其实并没有任何的关系但是因为同时归属于一个 Cache Line 这个 Cache Line 中的任意数据被修改后都会相互影响从而出现 ④ 和 ⑤ 这两个步骤。 因此这种因为多个线程同时读写同一个 Cache Line 的不同变量时而导致 CPU Cache 失效的现象称为伪共享False Sharing。 避免伪共享的方法 因此对于多个线程共享的热点数据即经常会修改的数据应该避免这些数据刚好在同一个 Cache Line 中否则就会出现为伪共享的问题。 接下来看看在实际项目中是用什么方式来避免伪共享的问题的。 在 Linux 内核中存在 __cacheline_aligned_in_smp 宏定义是用于解决伪共享的问题。 从上面的宏定义我们可以看到 如果在多核MP系统里该宏定义是 __cacheline_aligned也就是 Cache Line 的大小 而如果在单核系统里该宏定义是空的 因此针对在同一个 Cache Line 中的共享的数据如果在多核之间竞争比较严重为了防止伪共享现象的发生可以采用上面的宏定义使得变量在 Cache Line 里是对齐的。 举个例子有下面这个结构体 结构体里的两个成员变量 a 和 b 在物理内存地址上是连续的于是它们可能会位于同一个 Cache Line 中如下图 所以为了防止前面提到的 Cache 伪共享问题我们可以使用上面介绍的宏定义将 b 的地址设置为 Cache Line 对齐地址如下 这样 a 和 b 变量就不会在同一个 Cache Line 中了如下图 所以避免  Cache 伪共享实际上是用空间换时间的思想浪费一部分 Cache 空间从而换来性能的提升。 我们再来看一个应用层面的规避方案有一个 Java 并发框架 Disruptor 使用「字节填充 继承」的方式来避免伪共享的问题。 Disruptor 中有一个 RingBuffer 类会经常被多个线程使用代码如下 你可能会觉得 RingBufferPad 类里 7 个 long 类型的名字很奇怪但事实上它们虽然看起来毫无作用但却对性能的提升起到了至关重要的作用。 我们都知道CPU Cache 从内存读取数据的单位是 CPU Line一般 64 位 CPU 的 CPU Line 的大小是 64 个字节一个 long 类型的数据是 8 个字节所以 CPU 一下会加载 8 个 long 类型的数据。 根据 JVM 对象继承关系中父类成员和子类成员内存地址是连续排列布局的因此 RingBufferPad 中的 7 个 long 类型数据作为 Cache Line 前置填充而 RingBuffer 中的 7 个 long 类型数据则作为 Cache Line 后置填充这 14 个 long 变量没有任何实际用途更不会对它们进行读写操作。 另外RingBufferFelds 里面定义的这些变量都是 final 修饰的意味着第一次加载之后不会再修改 又由于「前后」各填充了 7 个不会被读写的 long 类型变量所以无论怎么加载 Cache Line这整个 Cache Line 里都没有会发生更新操作的数据于是只要数据被频繁地读取访问就自然没有数据被换出 Cache 的可能也因此不会产生伪共享的问题。 CPU 如何选择线程的 了解完 CPU 读取数据的过程后我们再来看看 CPU 是根据什么来选择当前要执行的线程。 在 Linux 内核中进程和线程都是用 tark_struct 结构体表示的区别在于线程的 tark_struct 结构体里部分资源是共享了进程已创建的资源比如内存地址空间、代码段、文件描述符等所以 Linux 中的线程也被称为轻量级进程因为线程的 tark_struct 相比进程的 tark_struct 承载的 资源比较少因此以「轻」得名。 一般来说没有创建线程的进程是只有单个执行流它被称为是主线程。如果想让进程处理更多的事情可以创建多个线程分别去处理但不管怎么样它们对应到内核里都是 tark_struct。 所以Linux 内核里的调度器调度的对象就是 tark_struct接下来我们就把这个数据结构统称为任务。 在 Linux 系统中根据任务的优先级以及响应要求主要分为两种其中优先级的数值越小优先级越高 实时任务对系统的响应时间要求很高也就是要尽可能快的执行实时任务优先级在 0~99 范围内的就算实时任务 普通任务响应时间没有很高的要求优先级在 100~139 范围内都是普通任务级别 调度类 由于任务有优先级之分Linux 系统为了保障高优先级的任务能够尽可能早的被执行于是分为了这几种调度类如下图 Deadline 和 Realtime 这两个调度类都是应用于实时任务的这两个调度类的调度策略合起来共有这三种它们的作用如下 SCHED_DEADLINE是按照 deadline 进行调度的距离当前时间点最近的 deadline 的任务会被优先调度 SCHED_FIFO对于相同优先级的任务按先来先服务的原则但是优先级更高的任务可以抢占低优先级的任务也就是优先级高的可以「插队」 SCHED_RR对于相同优先级的任务轮流着运行每个任务都有一定的时间片当用完时间片的任务会被放到队列尾部以保证相同优先级任务的公平性但是高优先级的任务依然可以抢占低优先级的任务 而 Fair 调度类是应用于普通任务都是由 CFS 调度器管理的分为两种调度策略 SCHED_NORMAL普通任务使用的调度策略 SCHED_BATCH后台任务的调度策略不和终端进行交互因此在不影响其他需要交互的任务可以适当降低它的优先级。 完全公平调度 我们平日里遇到的基本都是普通任务对于普通任务来说公平性最重要在 Linux 里面实现了一个基于 CFS 的调度算法也就是完全公平调度Completely Fair Scheduling。 这个算法的理念是想让分配给每个任务的 CPU 时间是一样于是它为每个任务安排一个虚拟运行时间 vruntime如果一个任务在运行其运行的越久该任务的 vruntime 自然就会越大而没有被运行的任务vruntime 是不会变化的。 那么在 CFS 算法调度的时候会优先选择 vruntime 少的任务以保证每个任务的公平性。 这就好比让你把一桶的奶茶平均分到 10 杯奶茶杯里你看着哪杯奶茶少就多倒一些哪个多了就先不倒这样经过多轮操作虽然不能保证每杯奶茶完全一样多但至少是公平的。 当然上面提到的例子没有考虑到优先级的问题虽然是普通任务但是普通任务之间还是有优先级区分的所以在计算虚拟运行时间 vruntime 还要考虑普通任务的权重值注意权重值并不是优先级的值内核中会有一个 nice 级别与权重值的转换表nice 级别越低的权重值就越大至于 nice 值是什么我们后面会提到。 于是就有了以下这个公式 你可以不用管 NICE_0_LOAD 是什么你就认为它是一个常量那么在「同样的实际运行时间」里高权重任务的 vruntime 比低权重任务的 vruntime 少你可能会奇怪为什么是少的你还记得 CFS 调度吗它是会优先选择 vruntime 少的任务进行调度所以高权重的任务就会被优先调度了于是高权重的获得的实际运行时间自然就多了。 CPU 运行队列 一个系统通常都会运行着很多任务多任务的数量基本都是远超 CPU 核心数量因此这时候就需要排队。 事实上每个 CPU 都有自己的运行队列Run Queue, rq用于描述在此 CPU 上所运行的所有进程其队列包含三个运行队列Deadline 运行队列 dl_rq、实时任务运行队列 rt_rq 和 CFS 运行队列 csf_rq其中 csf_rq 是用红黑树来描述的按 vruntime 大小来排序的最左侧的叶子节点就是下次会被调度的任务。 这几种调度类是有优先级的优先级如下Deadline Realtime Fair这意味着 Linux 选择下一个任务执行的时候会按照此优先级顺序进行选择也就是说先从 dl_rq 里选择任务然后从 rt_rq 里选择任务最后从 csf_rq 里选择任务。因此实时任务总是会比普通任务优先被执行。 调整优先级 如果我们启动任务的时候没有特意去指定优先级的话默认情况下都是普通任务普通任务的调度类是 Fail由 CFS 调度器来进行管理。CFS 调度器的目的是实现任务运行的公平性也就是保障每个任务的运行的时间是差不多的。 如果你想让某个普通任务有更多的执行时间可以调整任务的 nice 值从而让优先级高一些的任务执行更多时间。nice 的值能设置的范围是 -2019 值越低表明优先级越高因此 -20 是最高优先级19 则是最低优先级默认优先级是 0。 是不是觉得 nice 值的范围很诡异事实上nice 值并不是表示优先级而是表示优先级的修正数值它与优先级priority的关系是这样的priority(new) priority(old) nice。内核中priority 的范围是 0~139值越低优先级越高其中前面的 0~99 范围是提供给实时任务使用的而 nice 值是映射到 100~139这个范围是提供给普通任务用的因此 nice 值调整的是普通任务的优先级。 在前面我们提到了权重值与 nice 值的关系的nice 值越低权重值就越大计算出来的 vruntime 就会越少由于 CFS 算法调度的时候就会优先选择 vruntime 少的任务进行执行所以 nice 值越低任务的优先级就越高。 我们可以在启动任务的时候可以指定 nice 的值比如将 mysqld 以 -3 优先级 如果想修改已经运行中的任务的优先级则可以使用 renice 来调整 nice 值 nice 调整的是普通任务的优先级所以不管怎么缩小 nice 值任务永远都是普通任务如果某些任务要求实时性比较高那么你可以考虑改变任务的优先级以及调度策略使得它变成实时任务比如 总结 理解 CPU 是如何读写数据的前提是要理解 CPU 的架构CPU 内部的多个 Cache 外部的内存和磁盘都就构成了金字塔的存储器结构在这个金字塔中越往下存储器的容量就越大但访问速度就会小。 CPU 读写数据的时候并不是按一个一个字节为单位来进行读写而是以 CPU Line 大小为单位CPU Line 大小一般是 64 个字节也就意味着 CPU 读写数据的时候每一次都是以 64 字节大小为一块进行操作。 因此如果我们操作的数据是数组那么访问数组元素的时候按内存分布的地址顺序进行访问这样能充分利用到 Cache程序的性能得到提升。但如果操作的数据不是数组而是普通的变量并在多核 CPU 的情况下我们还需要避免 Cache Line 伪共享的问题。 所谓的 Cache Line 伪共享问题就是多个线程同时读写同一个 Cache Line 的不同变量时而导致 CPU Cache 失效的现象。那么对于多个线程共享的热点数据即经常会修改的数据应该避免这些数据刚好在同一个 Cache Line 中避免的方式一般有 Cache Line 大小字节对齐以及字节填充等方法。 系统中需要运行的多线程数一般都会大于 CPU 核心这样就会导致线程排队等待 CPU这可能会产生一定的延时如果我们的任务对延时容忍度很低则可以通过一些人为手段干预 Linux 的默认调度策略和优先级。
http://www.sadfv.cn/news/118832/

相关文章:

  • 做网站容易吗wordpress免费商城
  • 建设网站的价值设计师国外网站
  • 网站建设金手指霸屏网站后台的功能
  • 非凡软件站2023年不用做核酸了吗
  • 17zwd一起做网站广州新塘sem优化公司
  • 温州网站优化案例答题app怎么制作
  • 郑州网站定制外包免费搭建自助网站
  • 做类似58同城的网站优化 网站访问速度
  • 建设公司简介怎么写大连做网站优化哪家好
  • 做分销商城网站的wordpress站点制作
  • 做网站按什么收费多少钱青浦手机网站建设
  • 聊城住房和城乡建设部网站企业网站建设 新闻宣传
  • 可以建设网站的公司安徽索凯特建设工程有限公司网站
  • 腾讯云快速搭建网站网站建设时间
  • 山东省住房建设厅网站电子商务中网站开发
  • 辽阳企业网站建设怎么做网站网页
  • 国内用react做的网站网站备案 优帮云
  • 拓者室内设计官网拓者室内设计官网大泽山seo快速排名
  • 手机怎么建立自己网站动漫设计专修学校
  • 建设网站需要什么手续网站建设与管理是什么
  • 做视频的音乐哪里下载网站视觉传达设计考研
  • 深圳网站建设的公司招聘温州平阳县营销型网站建设
  • 面试网站开发佛山网站设计电话
  • 宜昌优化网站建设网站首页图片叫什么
  • 中国建设网站用户名wordpress加载图片404
  • 西宁网站建设报价cu君博規范自适应网站怎么做移动配置
  • 网站都是用什么编写的如何给自己网站做网站优化
  • 网站集约化后如何建设潍坊建立企业网站公司
  • 网站会员功能万维建设网站
  • 用自己电脑怎么做网站深圳企业网站制作中心