代码的未来----读书笔记(3)

第6章 多核时代的编程

1、摩尔定律

       摩尔定律所指的只是集成电路中晶体管数量呈集合级数增长这一趋势,而计算机性能的提高、价格的下降,以及其他各种变化,都是晶体管数量增长所带来的结果。

       最近的电脑中已经逐渐普及的多核和超线程(Hyper Threading)等技术,都是利用晶体管数量来提高运算性能的尝试。

       关于cpu,现在发展出更进一步的投机执行技术。所谓投机执行,就是对条件分支后的跳转目标进行预测后,不仅仅是执行取出命令的操作,还会进一步执行实际的运算操作。当然,当条件分支的预测错误时,需要取消刚才的操作,但当预测正确时,对性能的提升就可以比仅进行分支预测来得更加高效。

 

2、摩尔定律的极限

       在过去的40年里一直不断改变世界的摩尔定律,出于以下几个理由,芯片集成度的提高似乎已经接近了极限:

(1) 第一个极限就是导线宽度

(2) 即便能够形成更加细微的电路,还会发生另外一些问题

(3) 精密电路中还会产生发热问题

       摩尔定律已经接近极限,这是不争的事实。退一万步说,即使集成电路的精密化真的能够按现有的速度一直演进下去,总有一天一个晶体管会变得比一个原子还小

       不过,距离这一终极极限尚且还有一定的余地。现在所面临的课题,解决起来的确很有难度,但并没有到达无法克服的地步

 

3、不再有免费的午餐

       最近GPGPU收到了越来越多的关注,由于GPU与传统CPU的计算模型有着本质的区别,因此需要采用专门的编程技术

       即便什么都不做,CPU也会变得越来越快的时代结束了,今后为了活用新的硬件(比如显卡、通用计算硬件),软件开发者必须要付出更多的努力----这样的情况,作者将其称为“免费午餐的终结”

 

4、多核编译

       比如make -j4;

       或者使用ccache的工具,可以有效提高编译的速度。ccache是通过将编译结果进行缓存,来减少再次编译的工作量,从而提高编译速度的。其使用方法也很简单:

CC='ccache gcc'make -j4

       或者使用distcc,他是一种利用多台计算机来改善编译速度的工具。

 

5、非阻塞的I/O

       其实简单来说就是一种异步的编程方式,即当有事件发生的时候再来处理。

       在需要处理大量链接的服务器上,如果使用线程的话,内存负荷和线程切换的开销都会变得非常巨大。因此,监听“有输入进来”等事件并进行应对处理,采用单线程来实现更加高效。像这样通过“事件及应对处理”的方式来工作的软件架构,被称为事件驱动模型(event driven model)

       这种模型虽然可以提高效率,但也有缺点。在采用单线程来进行处理的情况下,当事件处理过程中由于某些原因需要进行等待时。程序整体就会停止运行。这也意味着即便产生了新的事件,也无法进行应对了。

 

6、非阻塞编程

       在编程世界中,减负(采用更好的算法、减少无谓的开销、用空间来换时间)、拖延(进行时间估算,对重要的事件首先进行处理,不太重要的先进行延迟)、委派(将任务进行分割,分别指派给不同的核执行)是非常重要的,特别是拖延和委派恐怕还不为大家所熟悉,但今后应该会愈发成为一种重要的编程技巧。

 

7、事件驱动编程

在传统的过程型编程中,各种操作时按照预先设定好的顺序来执行的:



       相对地,在事件驱动框架所提供的事件驱动编程中,不存在事先设定好的工作顺序,而是对来自外部的“事件”作出响应,并调用与该事件相对应的“回调函数”。这里所说的事件,包括“来自外部的输入”、“到达实现设定的时间”、“发生异常情况”等情况。在事件循环框架中,主循环程序一般是用一个循环来等待事件的发生,当检测到事件发生时,找到并启动与该事件相对应处理程序(回调函数)。当回调函数运行完毕后,再次返回到循环中,等待下一个事件:


       我们可以认为,过程型编程类似于每个单独的员工完成工作的方式,而事件驱动编程则类似于公司整体完成工作的方式。当发生客户下订单的事件时,销售部门(事件循环)会在接到订单后将工作转交给各业务部门(回调函数)来完成,这和事件驱动编程的模型有异曲同工之妙。

 

8、事件循环的利弊

       要实现和事件循环相同的功能,除了用回调函数之外,还可以采用启动线程的方式。不过,回调只是一种普通的函数调用,相比之下,线程启动所需要的开销要大得多。而且,每个线程都需要占用一定的栈空间(Linux中默认为每个线程8MB左右)。

       当然,我们可以使用线程池技术,事先准备好一些线程分配给回调函数来使用,但即便如此,在资源消耗方面还是单线程方式更具有优势。此外,使用单线程方式,还不必像多线程那样进行排他处理,由此引发的问题也会相对较少。

       另一方面,单线程方式也有他的缺点。虽然单线程在轻量化方面具备优势,但也同时意味着无法利用多核。此外,如果回调函数不小心产生了阻塞,就会导致事件处理的整体停滞,但在多线程/线程池方式中就不存在这样的问题

 

9、node.js

       一种用于js的事件驱动框架。提到js,大家都知道它是一种嵌入在浏览器中、工作在客户端环境下的编程语言,而node.js却是在服务器端工作的

 

10、ZeroMQ

       ZeroMQ提供了下列底层通信手段。无论使用哪种手段,都可以通过统一的Api进行访问,这一点可以说是ZeroMQ的魅力:

(1) tcp

       根据tcp套接字的性质,从其他计算机也可以进行连接,但由于ZeroMQ不存在身份认证这样的安全机制,因此建议不要在互联网上公布ZeroMQ的端口号

(2) ipc

       ipc用于在同一台计算机上进行进程间通信,使用文件路径来进行连接。实际通信中使用何种方式与实现无关,在Unix系操作系统上采用的是Unix套接字,在windows上也许是用一般套接字

(3) inproc

       inproc用于同一进程中的线程间通信。由于线程之间是共享内存的,因此这种通信方式是无需复制的。使用inproc通信,可以在活用线程的同时,避免麻烦的数据共享,不仅通信效率高,编写的程序也易读

(4) multicast

       multicast对多个对象的TCP连接反复通信

 

11、ZeroMQ的连接模型

(1)REQ/REP

       表示向服务器请求(request),服务器向客户端返回应答(reply)这样的连接模型。REQ/REP是一种双向通信。

(2) PUB/SUB

       即服务器发布(publish)信息时,在该服务器上注册(subscribe,订阅)过的客户端都会收到信息。

       这种模型在需要向大量客户端一起发送通知,以及数据分发部署等场合非常方便。PUB/SUB是一种单向通信。

(3) PUSH/PULL

       PUSH/PULL是向队列中添加和取出信息的一种模型。PUSH/PULL模型的应该范围很广,如果只有一个数据添加方和一个数据或获取方的话,可以用类似Unix管道的方式来使用,如果是由一台服务器PUSH信息,而多态客户端来PULL的话,则可以用类似任务队列的方式来使用

(4) PAIR

       一种一对一的双向通信,比如P2P中DHT的非中心节点中的心跳检测

 

12、小结

ZeroMQ是一个用简单的Api实现进程间通信的库。和直接使用套接字相比,它在一对多、多对多通信的实现上比较容易。在对多CPU的运用中,横跨多台计算机的多进程间通信是不可或缺的。因此在需要考虑可扩展性的软件开发项目中,像ZeroMQ这样的进程间通信库,今后应该会变得越来越重要


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值