性能的思考

提升性能意味着可以用更少的资源做更多的事情。但是提升性能会带来额外的复杂度,这会增加线程的安全性和活跃性上的风险。

我们渴望提升性能,但是还是要以安全为首要的。首先要保证程序能够安全正常的运行,然后在需要的时候进行性能优化,并且优化后的程序要尽可能保持并发性,让多处理中每个cpu尽可能得不要空闲,但是如果程序的并发没有设计好,那么可能会出现并发程序没有利用好现代多处理器的优势而导致并发化后的性能还不如串行化性能。

相较于单线程,多线程有很多独有的性能开销因素:

(1)线程之间的协调(如,加锁、内存同步等)

(2)增加的上下文切换

(3)线程的创建和销毁

(4)线程调度

可伸缩性:当增加计算机资源时(资源指:CPU、内存、硬盘),程序的吞吐量会得到提升。

Amdahl定律

在增加计算资源的情况下,程序在理论上能够实现最高的加速比,这个值取决于程序中可并行组件与串行组件所占的比重。

假定F是必须被串行执行的部分,那么根据Amdahl定律,在包含N个处理器的机器中,最高的加速比为:

Speedup<=1/(F+(1−F)/n)

当N趋近于无穷大时,最大的加速比趋近于1/F。因此有50%的计算需要串行执行,那么最高的加速比只能是2。

利用率:加速比除以处理器的数量。

随着处理器数量的增加,可以很明显地看到,即使串行部分所占的百分比很小,也会极大地限制当增加计算资源时能够提升的吞吐率。

在所有的并发程序中都包含一些串行部分(比如工作线程从队列拿取任务的时候,就需要加锁(串行化))。

Amdahl定律告诉我们:在串行化程序占比固定下,加速器越多,加速比也会越高,程序的可伸缩性和处理能力也就越好,但是也需要适可而止,因为当任务数比较少,但是处理器又很多,此时处理器的利用率也会降低(当然如果你有钱,觉得处理器想买多少就买多少当我没说)。

在各个框架中隐含的串行部分

上面说到,不管什么样的并发程序中,都必定有串行部分,那么以任务队列为例,比如工作线程从队列拿取任务的时候,就需要加锁(串行化)

图中对两种线程安全的Queue进行了比较,一个是同步容器SynchronizedLinkedList,一个是并发容器ConcurrentLinkedQueue,这俩有啥区别呢?图中显示,当线程数越多时,并发容器ConcurrentLinkedQueue的吞吐率的增幅相当快,而同步容器SynchronizedLinkedList几乎没有任何变化!why?

(1)同步容器的同步方式几乎都是在一个方法上加锁(锁住整个方法),锁住整个方法会带来的问题:

(a)独占锁会降低代码的可伸缩性:因为大量的线程都会因为锁而阻塞,那么程序的串行化占比会上升,所以根据Amdahl定律,加速比就会降低,可伸缩                    性降低。

(b)锁的请求频率:对一个锁请求的频率越高,就说明发生竞争的可能性越大,会限制可伸缩性(可以采取分段锁解决)。

(c)锁的请求时间:锁住整个方法比锁住某个代码块的持锁时间更长,锁持有时间过长会让程序串行化,限制可伸缩性。

(2)并发容器在安全处理上更为细粒度,因为采用的非阻塞式的算法:

(a)基于CAS(底层硬件提供并发机制)的并发程序,可伸缩性更好。

(b)摒弃了基于锁的机制,所以每个线程进来后都不用在锁上产生竞争而阻塞,并且不会产生线程的上下文的切换。

减少上下文切换的开销

任务在运行和阻塞这两个状态之间转换时,就相当于一次上下文切换。

多个线程同时记录日志:在输出流的锁上发生竞争。

I/O操作阻塞:操作系统将这个被阻塞的线程从调度队列中移走并直到I/O操作结束。

请求服务的时间不应该过长:服务时间越长,意味着越多的锁竞争。

通过将I/O操作从处理请求的线程中分离出来,可以缩短处理请求的平均服务时间。调用LOG方法的线程将不会再因为等待输出流的锁或者I.O完成而被阻塞,它们只需要要将消息放入队列,虽然在消息队列上可能会发生竞争,但put操作相对于记录日志的I/O操作(可能需要调用系统调用),是一种更为轻量级的操作,因此在实际上阻塞的概率更小,只要队列没填满。由于发出日志请求的线程被阻塞的概率降低,因此该线程在处理请求时被竞争的出去的概率也会降低。

通过将I/O操作移动了另一个用户感知不到开销的线程上,通过把所有记录日志的I/O转移到一个线程,还消除了输出流上的竞争,因此又去掉一个竞争来源。这将提升整体的吞吐量。

Amdahl定律和可伸缩性的更多相关文章

  1. Amdahl定律理解

    其中,a为并行计算部分所占比例,k为并行处理的个数. 当1-a=0时,(没有串行,只有并行)最大加速比s=n: 当a=0时,(只有串行,没有并行)最小加速比s=1: 当k→∞时,s → 1 /(1-a ...

  2. 《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记

    造成开销的操作包含: 1. 线程之间的协调(比如:锁.触发信号以及内存同步等) 2. 添加�的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 执行速度涉及下 ...

  3. java并发编程实战:第十一章----性能和可伸缩性

    线程的最主要目的是提高程序的运行性能,但性能的提升会导致复杂性的提升,又会导致安全性和活跃性的风险 一.对性能的思考 提升性能意味着用更少的资源做更多地事情.要想通过并发来获得更好的性能,就要更有效地 ...

  4. java并发编程(4)性能与可伸缩性

    性能与可伸缩性 一.Amdahl定律 1.问题和资源的关系 在某些问题中,资源越多解决速度越快:而有些问题则相反: 注意:每个程序中必然有串行的部分,而合理的分析出串行和并行的部分对程序的影响极大:串 ...

  5. 《java并发编程实战》读书笔记8--死锁,性能与可伸缩性,锁粒度锁分解锁分段

    第10章 避免活跃性危险 10.1 死锁 -10.1.1 锁顺序死锁 最简单的一种死锁形式: -10.1.2 动态的锁顺序死锁 可以通过下面的方法来解决: -10.1.3 在协作对象之间发生死锁 -1 ...

  6. Java并发编程实战 第11章 性能与可伸缩性

    关于性能 性能的衡量标准有很多,如: 服务时间,等待时间用来衡量程序的"运行速度""多快". 吞吐量,生产量用于衡量程序的"处理能力",能够 ...

  7. 深入浅出 Java Concurrency (40): 并发总结 part 4 性能与伸缩性[转]

    性能与伸缩性 使用线程的一种说法是为了提高性能.多线程可以使程序充分利用闲置的资源,提高资源的利用率,同时能够并行处理任务,提高系统的响应性. 但是很显然,引入线程的同时也引入了系统的复杂性.另外系统 ...

  8. java高并发系列 - 第3天:有关并行的两个重要定律

    有关为什么要使用并行程序的问题前面已经进行了简单的探讨.总的来说,最重要的应该是处于两个目的. 第一,为了获得更好的性能: 第二,由于业务模型的需要,确实需要多个执行实体. 在这里,我将更加关注第一种 ...

  9. C# - 多线程 之 进程与线程

    并行~并发 并发 Concurrency,逻辑上的同时发生,一个处理器(在不同时刻或者说在同一时间间隔内)"同时"处理多个任务.宏观上是并发的,微观上是按排队等待.唤醒.执行的步骤 ...

随机推荐

  1. python selenium2 模拟点击+拖动 测试对象 58同城验证码

    #!/usr/bin/python # -*- coding: UTF-8 -*- # @Time : 2019/12/5 17:30 # @Author : shenghao/10347899@qq ...

  2. 移植 Linux 内核

    目录 更新记录 1.Linux 版本及特点 2.打补丁.编译.烧写.启动内核 3.内核源码文件结构 4.内核架构分析 4.1 内核配置 4.2 Makefile架构分析 4.3 Kconfig 架构文 ...

  3. windows 日志清理和设置

    Windows日志路径 c:/windows/system32/winevt/logs 这里的日志文件可以ctrl+a 选中后使用shift+delete进行删除,删不掉的可以点击跳过.   在“管理 ...

  4. 客户想要的 vs 客户实际预算:漫画解读软件开发模式 ​​​​

    转自:http://blog.jobbole.com/113230/ 1913 年,美利坚工业之神——亨利福特,发明了世界上第一条流水线,汽车工业从此进入了大规模生产的时代.丰田公司提出的丰田生产系统 ...

  5. Emeditor代码编辑器常见的正则表达式总结

    Emeditor 目前来说是我个人感觉非常不错的一款记事本软件, 其中查找替换功能由于支持正则表达式而显得非常强大. <tr[^>]*> 匹配:<tr xxxxxxxxxxxx ...

  6. PhpStorm添加PHP代码规范检查CodeSniffer(phpcs)和PHP代码静态分析工具Mess Detector(phpmd)

    一.安装 添加镜像,加速下载 ./composer.phar  config -g repo.packagist composer https://packagist.phpcomposer.com ...

  7. Django—auth模块

    auth模块简介 auth模块是对登录认证方法的一种封装,之前我们获取用户输入的用户名及密码后需要自己从user表里查询有没有用户名和密码符合的对象, 而有了auth模块之后就可以很轻松的去验证用户的 ...

  8. 异步处理的框架Sanic的使用方法和小技巧

    Sanic是异步处理的框架,运用Sanic可以开发快速异步响应的web程序.想必大家看到这个都会比较期待和兴奋. 那么如何使用Sanic来实现快速响应呢?我们先来看一看Sanic的基本介绍. Sani ...

  9. K8S搭建过程随笔_系统初始化

    组件 Kubernetes 1.14.2 Docker 18.09.6-ce Etcd 3.3.13 Flanneld 0.11.0 基础环境设置 192.168.11.188 k8s-master ...

  10. Java中Static关键字详解以及静态变量和成员变量的区别

    一.static关键字的特点 (1)修饰成员变量.成员方法(2)随着类的加载而加载(3)优先于对象存在(4)被所有对象共享(5)可以通过类名调用 它本身也可以通过对象名调用 例如:main()方法由j ...