你能获得的对程序最大的加速比就是当你第一次让它工作起来的时候。 在讲解如何优化程序性能之前,我们首先要明确写程序最主要的目标就是使它在所有可能的情况下都能正常工作,一个运行的很快的程序但是却是错误的结果是没有任何用处的,所以我们在进行程序性能优化之前,首先要保证程序能正常运行,且结果是我们需要的。 而且在很多情况下,让程序跑的更快是我们必须要解决的问题。比如一个程序要实时处理视频帧或者网络包,那么一个运行的很慢的程序就不能解决此问题。再比如一个计算任务计算量非常大,需要数日或者数周,如果我们哪怕只是让它运行的快20%也会产生重大影响。1、编写高效程序的切入点 ①、选择一组合适的算法和
上一篇博客我们简单介绍了Y86指令集体系,而这篇博客我们将介绍指令集体系的逻辑设计和硬件控制语言HCL,为后面去实现Y86打下基础。 在硬件设计中,用电子电路来计算对位进行运算的函数,以及在各种存储器元素中存储位。大多数现代电路技术都是用信号上的高电压或者低电压来表示不同的位值。在当前的技术中心,逻辑1是用1.0伏特的高电压表示,而逻辑0是用0.0伏特的低电压表示。要实现一个数字系统需要三个组成部分: ①、计算对位进行逻辑操作的函数的组合逻辑 ②、存储位的存储器元素 ③、控制存储器元素更新的时钟信号 本篇博客我们就介绍这些不同的组成部分,以及用来描述不同处理器设计的控制逻辑——H
本章我们将进入处理器体系结构介绍的神秘海洋中,我们熟悉的手机,电脑等设备的核心硬件都离不开处理器。处理器可以称的上是人类创造的最复杂的系统之一,一块手指大小的硅片,可以容纳一个完整的高性能处理器、大的高速缓存,以及用来连接外部的逻辑电路。而且由于摩尔定律,从性能上讲,今天一块芯片上的处理器,已经使得三十年前比房间那么大的超级计算机都相形见绌了。 那么可能有人会问,我们软件开发者,永远都不会自己去设计处理器,那我们为什么要学习处理器的实现? ①、首先处理器的设计是非常有趣而且重要的,处理器设计包括很多好的工程实践原理,它需要完成复杂的任务,而结构又要尽可能的简单和规则,我们去了解事物是怎
上一篇博客我们讲解了汇编语言中过程(函数)的调用实现。理解数据如何在调用者和被调用者之间传递,以及在被调用者当中局部变量内存的分配以及释放是最重要的。那么这篇博客我们将讲解数组的分配和访问。1、数组的基本原则 我们知道数组是某种基本数据类型数据的集合,对于数据类型T和整型常数N,数组的声明如下:TA[N] 上面的A称为数组名称。它有两个效果: ①、它在存储器中分配一个L*N字节的连续区域,这里L是数据类型T的大小(单位为字节) ②、A作为指向数组开头的指针,如果分配的连续区域的起始地址为xa,那么这个指针的值就是xa 即当我们用A[i]去读取数组元素的时候,其实我们访问的是xa+
上篇博客我们讲解了计算机汇编语言是如何实现循环结构的。本篇博客我们将介绍汇编语言中过程的实现方式。 过程在高级语言中也称为函数,方法。一个过程的调用包括将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。此外,它还必须在进入时为过程的局部变量分配空间,并在退出时释放空间。大多数机器,包括我们一直讲的IA32,只提供转移控制到过程和从过程中转移出控制这种简单指令。数据传递和局部变量的分配释放都是通过操纵程序栈来实现。 合理的构建方法并调用,能大大增加代码的复用性,也能使代码结构更加清晰,接下来我们就来详细的介绍。1、栈帧结构 IA32程序用程序栈来支持过程调用。机器用
前面我们所讲的所有指令,代码执行顺序都是一条接着一条顺序的执行。但是实际上在编码过程中,会有某些结构,比如条件语句(if-else),循环语句(for,do-while)和分支语句(switch)等等,都要求有条件的执行,根据数据测试的结果来决定操作执行的顺序。 在机器代码中,提供两种基本的低级机制来实现有条件的行为:测试数据值,然后根据测试的结果来改变控制流或者数据流。 那么本篇博客我们就来详细介绍在汇编语言中的流程控制。1、条件码 前面我们在操作数指示符和数据传送指令中介绍了整数寄存器,在32位CPU中包含一组8个存储32位值的寄存器,即整数寄存器。它可以存储一些地址或者整数的数
在上一篇博客算术和逻辑操作我们介绍了如下图几种常用的算术逻辑指令,但是大家发现没,这几种指令如果在IA32上只能操作32位寄存器,比如我用乘法指令IMUL得出的结果超过了32位,那就会产生结果溢出,那应该怎么办呢?1、特殊的算术操作指令指令 如上图,上面的几个指令支持有符号和无符号的全64位乘积以及整数除法,但是需要注意的是,存储结果的寄存器固定死了,是一对寄存器%edx(高32位)和%eax(低32位)组成的64位的四字。2、imull和mull指令 对于imull指令,上一章我们在讲算术和逻辑操作指令的时候,讲过这个指令,这是一个乘法指令,指令形式是imullSD,这里有两个操作数
上一篇博客我们介绍了几种数据传送指令,包括MOV,MOVS,MOVZ,PUSH和POP等,理解起来也不算难。本篇博客我们来接着看汇编语言的算术与逻辑运算指令,算术无非就是加减乘除,而逻辑运算也就是与或非,移位等操作。下面这张图是汇编里面的算术和逻辑操作: 上面除了leal(加载有效地址)指令通常用来执行简单的算术操作,其余的指令都是标准的一元或者二元操作,下面我们分别来介绍这几个指令操作。1、leal指令 leal指令也称为加载有效地址(loadeffectiveaddress)指令,它实际上是movl指令的变形。它的指令形式是从存储器读数据到寄存器,但实际上它根本没有引用存储器。
在上一篇博客程序编码以及数据格式中我们给出了一个简单的C程序,然后编译成了汇编代码。大家看不懂没关系,后面的博客我们将逐渐揭开一些汇编指令的神秘面纱。本篇博客我们将对操作数指示符和数据传送指令进行详细的介绍。1、整数寄存器 上一篇博客我们讲了在汇编语言中,如下的几个处理器状态是可见的: 一、程序计数器(在IA32中通常称为PC,用%eip表示):指示将要执行的下一条指令在存储器中的地址。 二、整数寄存器文件:包含8个命名的位置,可以存储一些地址或者整数的数据。有的用来记录某些重要的程序状态,有的则用来保存临时数据。 三、条件码寄存器:保存最近执行的算数或逻辑指令的状态信息,它们用来
在进行本章的讲解之前,我们先说明讲解的机器语言型号。上一篇汇编语言和机器语言我们讲过,机器语言是直接面向处理器(Processor:CPU)的程序设计语言,但是每一种这样的微处理器(CPU)由于硬件设计和内部结构的不同,所以每一种微处理器都有自己的机器指令集,也就是机器语言。而汇编语言是便于记忆的机器语言。本系列博客将会介绍两种相关的机器语言:IntelIA32和x86-64。前者是当今大多数计算机的主导语言,而后者是在64位机器上运行的扩展,我们先从IntelIA32开始。1、机器级代码 前面我们就说过,计算机系统使用了多种不同的抽象,利用更简单的抽象模型来隐藏实现的细节。对于机器级编
《深入理解计算机系统》第三章——程序的机器级表示。作者首先讲解了汇编代码和机器代码的关系,阐述了汇编承上启下的作用;接着从机器语言IA32着手,分别讲述了如何存储数据、如何访问数据、如何完成运算以及如何进行跳转。通过这些步骤,又告诉了我们分支语句、循环语句是怎么完成的,函数调用、栈帧结构以及递归过程。最后能通过编译器产生的汇编代码表示,我们要了解编译器和它的优化能力,知道编译器能为我们完成哪些工作。 而这篇博客我们将讲解汇编和机器代码的关系。首先下面一张图是C语言、汇编语言以及翻译过的机器语言,大家可以先有个大概的眼熟。 上图引用至:http://www.jianshu.com/p/c
上一篇博客我们讲解了二进制小数如何表示以及IEEE浮点标准。而且我们也提到过因为这种表示方法限制了浮点数的范围和精度,浮点数只能近似的表示一个数。 比如数字1/5,我们能用十进制小数0.2准确的表示,但是我们却不能把它准确的表示为一个二进制小数,我们只能通过增加二进制表示的长度来提高表示的精度。如下: 那我们该怎么办呢?1、舍入 对于不能精确的表示的数,我们采取一种系统的方法,找到“最接近”的匹配值,它可以用期望的浮点形式表现出来,这就是舍入。 舍入一共有四种方式,分别是向偶数舍入、向零舍入、向上舍入以及向下舍入。 可以看下面的例子: 向偶数舍入,是将数字向上或向下舍入,使得结
整数的表示和运算我们已经讲完了,在实际应用中,整数能够解决我们大部分问题。但是某些需要精确表示的数,比如某件商品的价格,某两地之间的距离等等,我们如果用整数表示将会有很大的出入,这时候浮点数就产生了。 在20世纪80年代以前,每个计算机厂商都设计了自己表示浮点数的规则,以及对浮点数执行运算的细节,这对于应用程序在不同机器上的移植造成了巨大的困难。而在这之后,也就是1985年左右,IEEE标准产生了,这是一个仔细制定的表示浮点数及其运算的标准,现在的计算机浮点数也都是采用这个标准。 浮点数不仅仅是为了让数值的表示更加精确,也是为了表示一些整数无法达到的数字,比如一些接近于0的数字,或者一
前面两篇博客我们详细讲解了计算机中整数的表示,包括有符号和无符号(补码编码)的详细介绍。那么这篇博客我们将对它们的运算有个详细的了解。 在讲解之前首先看下面的一个程序,看看输出结果是啥?#include<stdio.h>intmain(){inti=2147483647;printf("%d\n",i+1);printf("%d\n",i+i);return0;} 结果是: 我们预期的: i+1=2147483647+1=2147483648** i+i=2147483647+2147483647=4294967294** 为
上一篇博客我们讲解了计算机中整数的表示,包括无符号编码和补码编码,以及它们之间的互相转换,个人觉得那是非常重要的知识要点。这篇博客我们将介绍C语言中的有符号数和无符号数以及扩展和截断数字。1、C语言中的有符号数和无符号数 上一篇博客我们给出了C语言中在32位机器和64位机器中支持的整型类型数据,我们这里只给出32位机器上的: 尽管C语言标准没有指定有符号数要采用某种编码表示,但是几乎所有的机器都使用补码。通常大多数数字是默认有符号的,比如当声明一个像12345或者0xABC这样的常量的时候,这个值就被认为是有符号的。 C语言允许有符号数和无符号数之间的转换。在一台采用补码的机器上:
上一篇博客我们主要介绍了布尔代数和C语言当中的几个运算符。那么这一篇博客我们主要介绍在计算机中整数是如何表示的,诸如我们在编码过程中遇到的对数据类型进行强制转换可能会得到意想不到的结果在这篇博客里你会得到解答。1、什么是整数? 整数包含正整数,0,负整数。我们从小的数学常识,整数是无穷无尽的,即整数的大小没有限制。 但是在计算机中则不能这样理解,因为计算机是靠数字信号来表示数,计算机所能处理的整数的长度是由计算机的字长来决定的,所以,在计算机中,我们必须制定一个规则来表示整数。2、C语言中的整型数据类型 C语言是支持多种整型数据类型的,下面我们看一下在32位机器和64位机器中,C语言
本篇博客我们主要讲解计算机中的布尔代数以及C语言的几个运算符。1、布尔代数 我们知道二进制值是计算机编码、存储和操作信息的核心,随着计算机的发展,围绕数值0和1的研究已经演化出了丰富的数学知识体系。而布尔代数便是乔治.布尔(GeorgeBoole)将逻辑值True(真)和False(假)编码为二进制0和1,用来研究逻辑推理的一门数学学科。 对于布尔代数,我们需要知道以下几种常见的运算符: ①、布尔运算~对应逻辑运算非。也就是取反的意思,假设p是0,那么~p就是1;反之亦然。 ②、布尔运算&对应逻辑运算与。有且只有p和q都为1时,p&q才等于1。 ③、布尔运算|对应
上一篇博客我们讲解了信息的在计算机中是如何存储以及如何表示的。但是对于各个进制的转换LZ只是一笔带过了,后来LZ仔细研究了进制转换的原理,发现还是挺有感悟的。那么这篇博客就讲讲进制转换。1、进制的介绍 在讲进制之前,我们先看一下数制的定义:用一组固定的数字和一套统一的规则来表示数目的方法称为数制。** **而数制有进位计数制与非进位计数制之分。非进位计数制的数码表示的数值大小与它在数中的位置无关,这里我们不作过多的介绍。 进位计数制的数码所表示的数值大小则与它在数中所处的位置有关,常见的有二进制、十进制、十六进制,我们这里也只介绍这三种进制的转换。 进位计数制的要素: ①、数
前面我们介绍了《深入理解计算机系统》第一章的内容----计算机系统漫游。包括简单介绍了HelloWorld程序在计算机中是如何运行的,存储设备的层次结构以及操作系统的抽象概念。第一章的内容只是对很多概念有个简单了解,所以还是不懂的话也不要紧,后面都会对这些概念进行深入的探究。而这一章我们将介绍《深入理解计算机系统》第二章----信息的表示和处理。 程序=数据结构+算法,所以了解数据结构对我们写程序是非常有帮助的,这一章会详细探讨计算机数据的存储和表示,其中也会有大量的公式推导。不要害怕看不懂,LZ有很多也是第一遍看不懂,但是后面慢慢推导,多看几遍就看懂了,相信只要具备了高中代数知识的都能
上一篇博客我们讲解了存储设备的层次结构,并详细讲解了高速缓存的原理,以及可以利用高速缓存来提高程序性能。如果对JavaWeb熟悉的,我们可以理解高速缓存类似Web端的缓存机制。那么这一篇博客我们来引出并谈谈操作系统的抽象概念。1、操作系统 首先接着来讨论helloworld程序的例子。当外壳加载和运行hello程序,以及hello程序输出自己的消息时,外壳和hello程序都没有直接访问键盘、显示器、磁盘或者主存。取而代之的是,它们依靠操作系统提供服务。 那么什么是操作系统呢?我们可以把操作系统看成应用程序和硬件之间插入的一层软件,如下图: 如图可知:所有的应用程序对硬件的操作都必须通
上一章我们讲解了helloworld程序在计算机系统中是如何运行的。hello程序的机器指令最初是存放在磁盘上的,当程序加载时,他们被复制到主存;当处理器运行程序的时候,指令又从主存复制到处理器。相似的,数据串"HelloWorld"初始时在磁盘上,然后复制到主存,最后从主存上复制到显示设备。 从程序员的角度来看,上面的复制就是开销,减缓了程序的真正工作。因此如何将这些复制操作尽快完成则是系统设计者的一个主要目标。1、从磁盘加载可执行文件到主存 2、将输出字符串从内存写到显示器 3、高速缓存存储器 那么如何减少这种由数据复制所引起的开销呢? 根据机械原理,较大
上一篇序章我谈了谈程序员为啥要懂底层计算机结构,有人赞同也有人反对也好,这都是博主的个人见解,但是博客还是要坚持学下去。这篇博客以案例驱动的模式,通过跟踪一个简单HelloWorld程序的生命周期开始系统的学习,包括它被程序员创建,到在系统上运行,输出简单的消息,然后终止。LZ将沿着这个程序的声明周期,先简要的介绍一些逐步出现的关键概念、专业术语以及组成部分。后面将会详细展开。1、计算机系统 我们知道计算机系统是由硬件和软件组成的。它们共同工作来运行应用程序。虽然系统的实现方式随着时间不断变化,但是系统内在的概念却没有改变。所有计算机系统都有相似的硬件和软件组件,它们执行着相似的功能,我
万丈高楼平地起,计算机系统就像程序员金字塔的地基。理解了计算机系统的构造原理,在写程序的道路上才能越走越远。道理LZ很早就懂了,可是一直没下定决心好好钻研,或许是觉得日常工作中根本用不到这些,又或许是每次拿起书看到那些复杂的底层架构,看到存储器,寄存器,CPU,总线等等这些概念就头大。总之,由于各种各样的原因,对这块的知识一直没有认真花时间去钻研。那么你可能会问,那你写这篇博客的题目不就是准备学习这方面的知识吗?是的,LZ准备下定决心钻研了,至于原因如下: ①、经常用一些不知其所以然的技术,会感到不安 大家可以看看LZ前面写的博客,很多都是对框架的用法进行总结,至于为什么这个框架要