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

网站上面怎么做链接重庆维力安网站建设

网站上面怎么做链接,重庆维力安网站建设,网上开店流程步骤,seo推广顾问Disruptor 的开发语言#xff0c;并不是很多人心目中最容易做到性能极限的 C/C#xff0c;而是性能受限于 JVM 的 Java。其实只要通晓硬件层面的原理#xff0c;即使是像 Java 这样的高级语言#xff0c;也能够把 CPU 的性能发挥到极限。 一、Padding Cache Line#xff…Disruptor 的开发语言并不是很多人心目中最容易做到性能极限的 C/C而是性能受限于 JVM 的 Java。其实只要通晓硬件层面的原理即使是像 Java 这样的高级语言也能够把 CPU 的性能发挥到极限。 一、Padding Cache Line体验高速缓存的威力 Disruptor 里面一段神奇的代码。这段代码里Disruptor 在 RingBufferPad 这个类里面定义了 p1p2 一直到 p7 这样 7 个 long 类型的变量。这些变量没有实际意义只是帮助我们进行缓存行填充Padding Cache Line使得我们能够尽可能地用上 CPU 高速缓存CPU Cache。 abstract class RingBufferPad {protected long p1, p2, p3, p4, p5, p6, p7; } CPU Cache 装载内存里面的数据不是一个一个字段加载的而是加载一整个缓存行。举个例子如果我们定义了一个长度为 64 的 long 类型的数组。那么数据从内存加载到 CPU Cache 里面的时候会一次性加载固定长度的一个缓存行。 我们现在的 64 位 Intel CPU 的计算机缓存行通常是 64 个字节Bytes。一个 long 类型的数据需要 8 个字节所以一下子会加载 8 个 long 类型的数据。也就是说一次加载数组里面连续的 8 个数值。这样的加载方式可以加快遍历数组元素时的速度。因为后面连续 7 次的数据访问都会命中缓存不需要重新从内存里面去读取数据。 但是不使用数组而是使用单独的变量的时候这里就会出现问题了。在 Disruptor 的 RingBuffer环形缓冲区的代码里面定义了一个 RingBufferFields 类里面有 indexMask 和其他几个变量用来存放 RingBuffer 的内部状态信息。 CPU 在加载数据的时候自然也会把这个数据从内存加载到高速缓存里面来。不过这个时候高速缓存里面除了这个数据还会加载这个数据前后定义的其他变量。问题就来了Disruptor 是一个多线程的服务器框架在这个数据前后定义的其他变量可能会被多个不同的线程去更新数据、读取数据。这些写入以及读取的请求会来自于不同的 CPU Core。于是为了保证数据的同步更新我们不得不把 CPU Cache 里面的数据重新写回到内存里面去或者重新从内存里面加载数据。 CPU Cache 的写回和加载都不是以一个变量作为单位的。这些动作都是以整个 Cache Line 作为单位的。所以当 INITIAL_CURSOR_VALUE 前后的那些变量被写回到内存的时候这个字段自己也写回到了内存这个常量的缓存也就失效了。要再次读取这个值的时候还需重新从内存读取。这也就意味着读取速度大大变慢了。 面临这样一个情况Disruptor 里有一种神奇的代码技巧就是缓存行填充。Disruptor 在 RingBufferFields 里面定义的变量的前后分别定义了 7 个 long 类型的变量。前面的 7 个来自继承的 RingBufferPad 类后面的 7 个则是直接定义在 RingBuffer 类里面。这 14 个变量没有任何实际的用途。我们既不会去读他们也不会去写他们。 ......abstract class RingBufferPad {protected long p1, p2, p3, p4, p5, p6, p7; }abstract class RingBufferFieldsE extends RingBufferPad {...... private final long indexMask;private final Object[] entries;protected final int bufferSize;protected final Sequencer sequencer;...... }public final class RingBufferE extends RingBufferFieldsE implements Cursored, EventSequencerE, EventSinkE {...... protected long p1, p2, p3, p4, p5, p6, p7;...... } RingBufferFields 里面定义的这些变量都是 final 的第一次写入之后不会再进行修改。所以一旦它被加载到 CPU Cache 之后只要被频繁地读取访问就不会再被换出 Cache 了。这也就意味着对于这个值的读取速度会是一直是 CPU Cache 的访问速度而不是内存的访问速度。 二、使用 RingBuffer利用缓存和分支预测 利用 CPU Cache 性能的思路贯穿了整个 Disruptor。Disruptor 整个框架其实就是一个高速的生产者 - 消费者模型Producer-Consumer下的队列。生产者不停地往队列里面生产新的需要处理的任务而消费者不停地从队列里面处理掉这些任务。 实现一个队列最合适的数据结构应该是链表。只要维护好链表的头和尾就能很容易实现一个队列。生产者只要不断地往链表的尾部不断插入新的节点而消费者只需要不断从头部取出最老的节点进行处理就好了。实际上Java 自己的基础库里面就有 LinkedBlockingQueue 这样的队列库可以直接用在生产者 - 消费者模式上。 不过Disruptor 里面并没有用 LinkedBlockingQueue而是使用了一个 RingBuffer 这样的数据结构这个 RingBuffer 的底层实现是一个固定长度的数组。比起链表形式的实现数组的数据在内存里面会存在空间局部性。 数组的连续多个元素会一并加载到 CPU Cache 里面来所以访问遍历的速度会更快。而链表里面各个节点的数据多半不会出现在相邻的内存空间自然也就享受不到整个 Cache Line 加载后数据连续从高速缓存里面被访问到的优势。 除此之外数据的遍历访问还有一个很大的优势就是 CPU 层面的分支预测会很准确。可以更有效地利用 CPU 里面的多级流水线使程序就会跑得更快。 三、无锁的 RingBuffer 一缓慢的锁 Disruptor 作为一个高性能的生产者 - 消费者队列系统一个核心的设计就是通过 RingBuffer 实现一个无锁队列。 Java 里面的基础库里像 LinkedBlockingQueue 这样的队列库比起 Disruptor 里用的 RingBuffer 要慢上很多。慢的第一个原因我们说过因为链表的数据在内存里面的布局对于高速缓存并不友好而 RingBuffer 所使用的数组则不然。 另外一个重要的因素是 LinkedBlockingQueue 对锁的依赖较强。在生产者 - 消费者模式里我们可能有多个消费者同样也可能有多个生产者。多个生产者都要往队列的尾指针里面添加新的任务就会产生多个线程的竞争。于是在做这个事情的时候生产者就需要拿到对于队列尾部的锁。同样地在多个消费者去消费队列头的时候也就产生竞争。同样消费者也要拿到锁。 就算只有一个生产者一个消费者也是需要锁的。一般来说在生产者 - 消费者模式下消费者要比生产者快。不然的话队列会产生积压队列里面的任务会越堆越多。任务不能及时完成内存也会放不下。虽然生产者 - 消费者模型下有一个队列来作为缓冲区但是大部分情况下这个缓冲区里面是空的。也就是说即使只有一个生产者和一个消费者者这个生产者指向的队列尾和消费者指向的队列头是同一个节点。因此生产者和消费者之间一样会产生锁竞争。 在 LinkedBlockingQueue 上这个锁机制是通过 Java 基础库 ReentrantLock 来实现的。这个锁是一个用 Java 在 JVM 上直接实现的加锁机制锁机制需要由 JVM 来进行裁决。锁的争夺会把没有拿到锁的线程挂起等待也就需要经过一次上下文切换Context Switch。 上下文切换的过程需要把当前执行线程的寄存器等的信息保存到线程栈里面。而这个过程也必然意味着已经加载到高速缓存里面的指令或者数据又回到了主内存里面会进一步拖慢性能。 加锁和不加锁自增到5亿性能对比 package com.xuwenhao.perf.jmm;import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class LockBenchmark{public static void runIncrement(){long counter 0;long max 500000000L;long start System.currentTimeMillis();while (counter max) {counter;}long end System.currentTimeMillis();System.out.println(Time spent is (end-start) ms without lock);}public static void runIncrementWithLock(){Lock lock new ReentrantLock();long counter 0;long max 500000000L;long start System.currentTimeMillis();while (counter max) {if (lock.tryLock()){counter;lock.unlock();}}long end System.currentTimeMillis();System.out.println(Time spent is (end-start) ms with lock);}public static void main(String[] args) {runIncrement();runIncrementWithLock(); Time spent is 207ms without lock Time spent is 9603ms with lock 二无锁的 RingBuffer 加锁很慢所以 Disruptor 的解决方案就是“无锁”。这个“无锁”指的是没有操作系统层面的锁。实际上Disruptor 还是利用了一个 CPU 硬件支持的指令称之为 CASCompare And Swap比较和交换。在 Intel CPU 里面这个对应的指令就是 cmpxchg。 Disruptor 的 RingBuffer 是这么设计的和直接在链表的头和尾加锁不同Disruptor 的 RingBuffer 创建了一个 Sequence 对象用来指向当前的 RingBuffer 的头和尾。这个头和尾的标识不是通过指针来实现的而是通过一个序号。 在这个 RingBuffer 当中进行生产者和消费者之间的资源协调采用的是对比序号的方式。当生产者想要往队列里加入新数据的时候它会把当前的生产者的 Sequence 的序号加上需要加入的新数据的数量然后和实际的消费者所在的位置进行对比看看队列里是不是有足够的空间加入这些数据而不会覆盖掉消费者还没有处理完的数据。 在 Sequence 的代码里面就是通过 compareAndSet 这个方法并且最终调用到了 UNSAFE.compareAndSwapLong也就是直接使用了 CAS 指令。 public boolean compareAndSet(final long expectedValue, final long newValue){return UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, expectedValue, newValue);}public long addAndGet(final long increment){long currentValue;long newValue;do{currentValue get();newValue currentValue increment;}while (!compareAndSet(currentValue, newValue));return newValue; 这个 CAS 指令也就是比较和交换的操作并不是基础库里的一个函数。它也不是操作系统里面实现的一个系统调用而是一个 CPU 硬件支持的机器指令。在我们服务器所使用的 Intel CPU 上就是 cmpxchg 这个指令。 compxchg [ax] (隐式参数EAX累加器), [bx] (源操作数地址), [cx] (目标操作数地址) cmpxchg 指令一共有三个操作数第一个操作数不在指令里面出现是一个隐式的操作数也就是 EAX 累加寄存器里面的值。第二个操作数就是源操作数并且指令会对比这个操作数和上面的累加寄存器里面的值。如果值是相同的那一方面CPU 会把 ZF也就是条件码寄存器里面零标志位的值设置为 1然后再把第三个操作数也就是目标操作数设置到源操作数的地址上。如果不相等的话就会把源操作数里面的值设置到累加器寄存器里面。 单个指令是原子的这也就意味着在使用 CAS 操作的时候不再需要单独进行加锁直接调用就可以了。没有了锁CPU 这部高速跑车就像在赛道上行驶不会遇到需要上下文切换这样的红灯而停下来。虽然会遇到像 CAS 这样复杂的机器指令就好像赛道上会有 U 型弯一样不过不用完全停下来等待 CPU 运行起来仍然会快很多。 那么CAS 操作到底会有多快呢我们还是用一段 Java 代码来看一下。 package com.xuwenhao.perf.jmm;import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class LockBenchmark {public static void runIncrementAtomic(){AtomicLong counter new AtomicLong(0);long max 500000000L;long start System.currentTimeMillis();while (counter.incrementAndGet() max) {}long end System.currentTimeMillis();System.out.println(Time spent is (end-start) ms with cas);}public static void main(String[] args) {runIncrementAtomic();} 和上面的 counter 自增一样只不过这一次自增采用了 AtomicLong 这个 Java 类。里面的 incrementAndGet 最终到了 CPU 指令层面在实现的时候用的就是 CAS 操作。可以看到它所花费的时间虽然要比没有任何锁的操作慢上一个数量级但是比起使用 ReentrantLock 这样的操作系统锁的机制还是减少了一半以上的时间。 当想要追求最极致的性能的时候我们会从应用层、贯穿到操作系统乃至最后的 CPU 硬件搞清楚从高级语言到系统调用乃至最后的汇编指令这整个过程是怎么执行代码的。而这个也是学习组成原理的意义所在。 【推荐阅读】Disruptor 官方文档里面不仅包含了怎么用好 Disruptor也包含了整个 Disruptor 框架的设计思路是一份很好的阅读学习材料。另外Disruptor 的官方文档里还有很多文章、演讲详细介绍了这个框架很值得深入去看一看。Disruptor 的源代码其实并不复杂很适合用来学习怎么阅读开源框架代码。 课程链接深入浅出计算机组成原理_组成原理_计算机基础-极客时间
http://www.w-s-a.com/news/965727/

相关文章:

  • dw做网站的实用特效广东住房与城乡建设厅网站
  • 模板网站 动易哪方面的网站
  • 怎么给网站做外链邵连虎郑州做网页的公司
  • 重庆网站开发哪家好宁波网站建设caiyiduo
  • 手机网站建设价格手机网站模版更换技巧
  • 哈尔滨松北区建设局网站美妆网站建设
  • 不需要网站备案的空间网站推广的基本方法是哪四个
  • 如何检查网站死链劳动仲裁院内部网站建设
  • 江西省住房和城乡建设网站合同管理系统
  • 网站建设质量保证福州网络推广
  • 高唐网站建设公司广州南站在哪个区
  • 广西柳州网站制作公司郴州网红打卡景点
  • 做网站要固定ip拍摄公司宣传片制作
  • 专业微网站电话号码做软件难吗
  • 邢台网站制作哪家强上海做网站设计
  • 大连网站建设外贸wordpress添加文章属性
  • 商城网站建设合同范本网上哪里可以免费学编程
  • 服务器公司网站博客wordpress怎么编辑
  • 网站建设网络推广柯西乡塘网站建设
  • 企业做网站需要多少钱企业资质查询系统官网
  • 网站建设需要知识百度统计数据
  • 自已如何做网站建设通网站会员共享密码
  • 做网站学习什么wordpress 文件夹
  • 前端移动网站开发wordpress图文混排
  • 企业网站建站那种好商城类网站怎么优化
  • 手机微网站怎么制作的网上找设计师
  • 网站建设包括哪些方面学校网站 建设
  • 贵阳网站优化公司建筑设计师用什么软件
  • 网站建设的小说静态网页模板免费网站
  • 芜湖建设厅官方网站wordpress自动设置缩略图