编写高效的程序需要下面几类活动:选择1组适合的算法和数据结构。这是很重要的,好的数据结构有时能帮助更快的实现某些算法,这也要求编程人员能够熟知各种经常使用的数据结构和算法。

编写出使编译器能够有效优化以转换成高效可履行的源代码。因此,理解编译器优化的能力和局限性是很重要的。编写程序方式中看上去只是1点小小的变动,都会引发编译器优化方式很大的变化。有些编程语言比其他语言容易优化很多。C语言的某些特性,例如履行指针运算和强迫类型转换的能力,使得编译器很难对其进行优化。

并行技术,针对处理运算量特别大的计算,将1个任务分成多个部份,这些部份可以在多核和多处理器的某种组合上并行地计算。

让编译器展开循环

说到程序优化,很多人都会提到循环展开技术。现在编译器可以很容易地履行循环展开,只要优化级别设置的足够高,许多编译器都能例行公事的做到这1点。用命令行选项“-funroll-loops”调用gcc,会履行循环展开。

性能提高技术:高级设计,为手边的问题选择适当的算法和数据结构,要特别警觉,避免使用会渐进地产生糟性能的算法或编码技术。

基本编码原则。避免限制优化的因素,这样编译器就可以产生高效代码。消除连续的函数调用。在可能时将计算移到循环外,斟酌有选择的让步程序的模块性以取得更大效力。

消除没必要要的存储器援用。引入临时变量来保存中间结果,只有在最后的值计算出来时,才能将结果放到数组或全局变量中。

低级优化。尝试各种与数组代码相对的指针情势。

通过展开通过展开循环下降循环开消。

通过诸如迭代分割之类的技术,找到使用流水线化的功能单元的方法。

说到性能提高,可能有人会有1些说法:(1)不要过早优化,优化是万恶之源;

(2)花费很多时间所作的优化可能效果不明显,不值得;

(3)现在内存、CPU价格都这么低了,性能的优化已不是那末重要了。

……

其实我的看法是:我们或许没必要特地把之前写过的程序拿出来优化下,花费N多时间只为提升那末几秒或几分钟的时间。但是,我们在重构他人的代码或自己最初开始构思代码时,就需要知道这些性能提高技术,1开始就遵照这些基本原则来写代码,写出的代码也就不需要让他人来重构以提高性能了。另外,有的很简单的技术,比如说将与循环无关的复杂计算或大内存操作的代码放到循环外,对全部性能的提高真的是较明显的。

如何使用代码剖析程序(code profiler,即性能分析工具)来调优代码?

程序剖析(profiling)其实就是在运行程序的1个版本中插入了工具代码,以肯定程序的各个部份需要多少时间。

Unix系统提供了1个profiling叫GPROF,这个程序产生两类信息:首先,它肯定程序中每一个函数花费了多少CPU时间。

其次,它计算每一个函数被调用的次数,以履行调用的函数来分类。还有每一个函数被哪些函数调用,本身又调用了哪些函数。

使用GPROF进行剖析需要3个步骤,比如源程序为prog.c。

1)编译: gcc -O1 -pg prog.c -o prog(只要加上-pg参数便可)

2)运行:./prog

会生成1个gmon.out文件供 gprof分析程序时候使用(运行比平时慢些)。

3)剖析:gprof prog

分析gmon.out中的数据,并显示出来。

剖析报告的第1部份列出了履行各个函数花费的时间,依照降序排列。

剖析报告的第2部份是函数的调用历史。具体例子可参考网上资料。

GPROF有些属性值得注意:计时不是很准确。它的计时基于1个简单的间隔计数机制,编译过的程序为每一个函数保护1个计数器,记录花费在履行该函数上的时间。对运行时间较长的程序,相对准确。

调用信息相当可靠。

默许情况下,不显示库函数的调用。相反地,库函数的时间会被计算到调用它们的函数的时间中。

静态链接和动态链接1个很重要的区分是:动态链接时没有任何动态链接库的代码和数据节真实的被拷贝到可履行文件中,反之,链接器只需拷贝1些重定位和符号表信息,便可使得运行时可以解析对动态链接库中代码和数据的援用。

存储器映照

指的是将磁盘上的空间映照为虚拟存储器区域。Unix进程可使用mmap函数来创建新的虚拟存储器区域,并将对象映照到这些区域中,这属于低级的分配方式。

1般C程序会使用malloc和free来动态分配存储器区域,这是利用堆的方式。

造成堆利用率很低的主要缘由是碎片,当虽然有未使用的存储器但不能用来满足分配要求时,就会产生这类现象。

有两种情势的碎片:内部碎片和外部碎片。二者的区分以下:内部碎片是在1个已分配的块比有效载荷大时产生的。例如,有些分配器为了满足对其束缚添加额外的1字的存储空间,这个1字的空间就是内部碎片。它就是已分配块大小和它们的有效载荷大小之差的和。

外部碎片是当空闲存储器合计起来足够满足1个分配要求,但是没有1个单独的空闲块足够大可以来处理这个要求时产生的。

现代OS提供了3种方法实现并发编程:进程。用这类方法,每一个逻辑控制流都是1个进程,由内核来调度和保护。由于进程有独立的虚拟地址空间,想要和其他流通讯,控制流必须使用进程间通讯(IPC)。

I/O多路复用。这类情势的并发,利用程序在1个进程的上下文中显示地调度它们自己的逻辑流。逻辑流被摹拟为“状态机”,数据到达文件描写符后,主程序显示地从1个状态转换到另外一个状态。由于程序是1个单独的进程,所以所有的流都同享1个地址空间。

线程。线程是运行在1个单1进程上下文中的逻辑流,由内核进行调度。线程可以看作是进程和I/O多路复用的合体,像进程1样由内核调度,像I/O多路复用1样同享1个虚拟地址空间。

(1)基于进程的并发服务器

构造并发最简单的就是使用进程,像fork函数。例如,1个并发服务器,在父进程中接受客户端连接要求,然后创建1个新的子进程来为每一个新客户端提供服务。为了了解这是如何工作的,假定我们有两个客户端和1个服务器,服务器正在监听1个监听描写符(比如描写符3)上的连接要求。下面显示了服务器是如何接受这两个客户真个要求的。

进程并发示例

关于进程的优劣,对在父、子进程间同享状态信息,进程有1个非常清晰的模型:同享文件表,但是不同享用户地址空间。进程有独立的地址控件爱你既是优点又是缺点。由于独立的地址空间,所以进程不会覆盖另外一个进程的虚拟存储器。但是另外一方面进程间通讯就比较麻烦,最少开消很高。

(2)基于I/O多路复用的并发编程

比如1个服务器,它有两个I/O事件:1)网络客户端发起连接要求,2)用户在键盘上键入命令行。我们先等待那个事件呢?没有那个选择是理想的。如果accept中等待连接,那末没法响应输入命令。如果在read中等待1个输入命令,我们就不能响应任何连接要求(这个条件是1个进程)。

针对这类窘境的1个解决办法就是I/O多路复用技术。基本思想是:使用select函数,要求内核挂起进程,只有在1个或多个I/O事件产生后,才将控制返给利用程序。

I/O多路复用的优劣:由于I/O多路复用是在单1进程的上下文中的,因此每一个逻辑流程都能访问该进程的全部地址空间,所以开消比多进程低很多;缺点是编程复杂度高。

(3)基于线程的并发编程

每一个线程都有自己的线程上下文,包括1个线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在1个进程里的线程同享该进程的全部虚拟地址空间。由于线程运行在单1进程中,因此同享这个进程虚拟地址空间的全部内容,包括它的代码、数据、堆、同享库和打开的文件。所以我认为不存在线程间通讯,线程间只有锁的概念。线程履行的模型。线程和进程的履行模型有些类似。每一个进程的生明周期都是1个线程,我们称之为主线程。但是大家要成心识:线程是对等的,主线程跟其他线程的区分就是它先履行。

1般来讲,线程的代码和本地数据被封装在1个线程例程中(就是1个函数)。该函数通常只有1个指针参数和1个指针返回值。

在Unix中线程可以是joinable(可结合)或detached(分离)的。joinable可以被其他线程杀死,detached线程不能被杀死,它的存储器资源有系统自动释放。

线程存储器模型,每一个线程都有它自己的独立的线程上下文,包括线程ID、栈、栈指针、程序计数器、条件码和通用目的寄存器。每一个线程和其他线程同享剩下的部份,包括全部用户虚拟地址空间,它是由代码段、数据段、堆和所有的同享库代码和数据区域组成。不同线程的栈是对其他线程不设防的,也就是说:如果1个线程以某种方式得到1个指向其他线程的指针,那末它可以读取这个线程栈的任何部份。

甚么样的变量多线程可以同享,甚么样的不可以同享?

有3种变量:全局变量、本地自动变量(局部变量)和本地静态变量,其中本地自动变量每一个线程的本地栈中都存有1份,不同享。而全局变量和静态变量可以同享。

程序员学完深入理解计算机系统,深入理解计算机系统9个重点笔记相关推荐

  1. 多线程设置操作时间间隔_听说,有位Java程序员学完这59道多线程面试题之后,直接面上阿里...

    前言 多线程是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能.具有这种能力的系统包括对称多处理机.多核心处理器以 ...

  2. 程序员学数学读哪本书?(文末抽奖赠书)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 在互联网一直流传了一个这样的段子-- "一流程序员靠数学 ...

  3. 程序员学数学读哪本书?(文末赠送精美礼品)

    关注我们丨文末赠书 在互联网一直流传了一个这样的段子-- "一流程序员靠数学,二流靠算法,三流靠逻辑,四流靠SDK,五流靠Google和StackOverFlow,六流靠百度和CSDN.低端 ...

  4. php学数据结构,PHP 程序员学数据结构与算法之《栈》

    介绍 "要成高手,必练此功". 要成为优秀的程序员,数据结构和算法是必修的内容.而现在的Web程序员使用传统算法和数据结构都比较少,因为很多算法都是包装好的,不用我们去操心具体的实 ...

  5. java static关键字_好程序员Java教程分享static关键字的理解

    好程序员Java教程分享static关键字的理解,static关键字含义可以理解为静态的. 1. 当其修饰属性时,该属性为整个类公有,所有的对象操作的都是同一个静态属性.所以调用时应该使用类名去调用, ...

  6. 系列文章----.Net程序员学用Oracle系列

    .Net程序员学用Oracle系列(18):PLSQL Developer 攻略 .Net程序员学用Oracle系列(17):数据库管理工具(SQL Plus) .Net程序员学用Oracle系列(1 ...

  7. java c++ python哪个好_程序员学java好还是python语言好 c++又如何

    技术程序员学Java.C++.Python... 分析对比,到底学什么语言吃香,哪种语言最靠谱,如何学好这些语言呢? 几天前,我们在知识上看到这样一个问题:"java,C++,Python, ...

  8. python送程序员收入_程序员学Python后惊叹,这么丰厚的收入是Java给不了的!

    原标题:程序员学Python后惊叹,这么丰厚的收入是Java给不了的! 你都用 Python 来做什么?? 发现很多人都在学习 Python ,但是没有明确的说明可以做什么,主流的功能是什么? 想知道 ...

  9. java程序员学技术都会访问那些网站?

    Java程序员学技术的网站 哔哩哔哩 https://www.bilibili.com/ CSDN https://www.csdn.net/nav/java 掘金 https://juejin.cn ...

最新文章

  1. BrokenPipeError: [WinError 109] 管道已结束。
  2. js跨浏览器事件处理
  3. windows 连接Linux
  4. springmvc参数绑定
  5. Flink SQL Client的datagen的用法(转载+自己验证)
  6. iOS:多线程技术GCD的使用
  7. C++工作笔记-对结构体的进一步认识
  8. java+mysql校园学校宿舍管理系统源码
  9. 五个免费UML建模工具推荐
  10. c++11 string转ing_pdfkit | 利用python实现html文件转pdf
  11. 计算机组成原理学习 笔记一
  12. KubeCon+CloudNativeCon首登中国,时速云受邀发表主题演讲
  13. java 小根堆 排序_堆排序(java实现)
  14. informix多张表UPDATE时不能用别名
  15. CBNetV2论文的译读笔记
  16. oracle 循环块,Oracle语句块PL/SQL循环判断
  17. CSS实现输入框的高亮效果-------Day50
  18. SAS入门教程1---SAS系统简介
  19. DUC HDC 笔记
  20. 不要嫌我啰嗦~继续话痨python

热门文章

  1. 电脑开机为什么老是要两次?
  2. 步进电机控制,RPM与PPS单位关系分析
  3. Altium Designer的几种常用布线技巧
  4. IOS 开发高手课 学习笔记(第二部分)
  5. 看图说话:从图片到文字
  6. 【原创】linux的vscode连接不上Extensions,报错:Cannot connect to VS CODE MARKETPLACE Extensions
  7. spring报错parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml]
  8. 亲,你心念念的Axure9.0来了,请查收~ 内附Axure安装包+汉化包+授权码获取方式
  9. android工具am的用法,Android中AM、PM、dumpsys命令使用总结
  10. 指针p++与p+1的区别