目录
2.2 性能优化(编译器能够有效优化已经转换成高效可执行代码源代码,重点参考深入理解计算机系统书籍第五章)
5.7.2 功能单元的性能 我们用延迟,发射时间,容量来表示功能单元的性能。
1 问题定义
有向图找环问题 有向图找出所有长度在3和7之内的环,其中需要包括共边环。
2 解决方案
2.1 算法和数据结构
算法用深度优先遍历,数据结构用邻接表。
2.2 性能优化(编译器能够有效优化已经转换成高效可执行代码源代码,重点参考深入理解计算机系统书籍第五章)
引用深入理解计算机系统的话,分步骤进行
1 消除不必要的工作,包括函数调用,条件测试,内存引用
2 理解处理器执行模型,构建for循环的数据流程以及关键路径来决定一个循环执行周期的下界。,然后利用处理器提供的指令级并行能力,同时执行多条指令
5.1 首先了解编译器的优化能力和局限性
只进行安全的优化,必须假设不同指针可能会指向内存同一位置,叫做内存别名使用。这就限制了编译器优化的性能。 还有一种就是函数调用 如果贸然优化,会导致错误发生。可能考虑用内联函数来解决
5.2 表示程序性能
为什么用时钟周期来度量执行多少条指令?当我们关心输入数据n和运行多少周期之间的关系时。我们可以得到一组关于(x1,y1),(x2,y2),(x3,y3)的数,那么使用最小二乘拟合, 可以得到n和运行时间的关系。 这里面关于最小二乘拟合其实是多元微积分的知识,一旦涉及到函数,我们就有该函数定义域,连续,导数,极值等等。我学习多元函数求极值那一块知识,在分析这个二元函数微积分时,我们有定理 在对x导数=0,且对y导数=0,就可以获得极值。这是一个定理。在这列通过二阶偏导判断得到满足对x,对y导数为0的时候,是极小值,
''' 这里有疑问就是你如何知道是一次还是二次函数?可以通过画图看曲线走势, '''
5.3 程序实例
5.4 消除循环的低效率
代码移动(识别执行多次,但是计算结果不会发生变化的计算。),编译器会帮忙你做代码移动,但是有时候它没那么聪明,所以需要我们显式的做下代码移动工作,
5.5 减少过程调用 过程中尽量不要较少函数调用,
5.6 消除不必要的内存引用。
写出汇编代码(减少内存引用。) 然后改进之后,对比汇编代码看看。 临时变量就放在栈里面,访问很快。而用malloc或者new所申请的空间在堆上,在内存上,访问较慢。
5.7 理解现代处理器(有两个瓶颈,延迟界限和吞吐量界限)
延迟界限是限制指令级并行的能力瓶颈,而吞吐量界限刻画了处理器功能单元的原始计算能力。
5.7.1 整体操作 了解现代处理器是如何进行操作,
包括两个大部分,指令控制单元和执行单元部分。指令控制单元不断取指令送到执行单元,但是分支处理不太一样。 指令从寄存器或者内存中取值,执行一个操作。并把结果存回寄存器或者内存中。这里面我们尽可能让结果放入寄存器中,这里应该有个图表示, ICU从指令高速缓存区读取指令,其中,指令译码把每一条指令切分多多条指令(放入先进先出队列),它一次可能读较多指令,然后发送到执行单元。每个时钟周期可能接受多条指令,分发到一组功能单元中, 这些功能单元专门用来处理不同类型的操作,比如算术运算,加载,存储,分支等。这也是neno技术的硬件支持,
5.7.2 功能单元的性能 我们用延迟,发射时间,容量来表示功能单元的性能。
5.7.3 处理器操作的抽象模型
这个是重点,这部分就是把你的代码抽象成数据模型,你可以清晰看到数据的动向,展示不同操作之间的数据相关是如何限制它们的执行顺序的。比如for循环执行过程中关键路径指的就是数据模型的主要矛盾,性能制约瓶颈就是关键路径的过程,这个过程你可以用计算器 的乘法,除法的延迟来近似效率。自然就可以近似得到最小二乘法的斜率了。 注意这种方法只能得到下界。 当然,它假设不关心循环的比较选择分支部分,只关心里面的数据流动。并且只关心这次迭代需要上次结果的关键路径流动。而不是全部计算,并不是循环中代码多就会执行慢的。
5.8 循环展开
第一可以减少条件分支,第二减少整个计算关键路径上的操作数量。它满足 k*的特质,其中k表示展开的k的倍数,m示几个分开的结果。比如2*2的展开,是可以有两条关键路径的。每条占n/2次。 循环展开就是并行处理啊,但是循环展开并不能破除性能制约瓶颈,只是减少循环次数。
5.9 提高并行性
但是m太多了不好,不能充分利用寄存器的性能。 利用多个累积变量保存结果,最后再一次性算出 但可能出现每个累计变量那部分数据不太一样,出现每个累积变量数据分布不一,那得不偿失。所以这个提高并行性是假定所有分块数据的分布是一样的。
5.11 一些制约因素
关键路径指明了执行该程序所需时间的一个基本下界。如果有关键路径,那么关键路径的所有延迟之和等于T,那么这个cheng'xu
5.11.1 寄存器溢出
循环展开也是有限度的,如果并行计算的容量超过了寄存器,就会溢出。那么计算机就会把那些溢出的变量放在堆栈上,
5.11.2 分支预测和预测错误处罚
用条件转移代替数据分布不一的分支预测好些。 不要过分关心可预测分支,这取决于数据的分布相同吗?
5.12 理解内存性能
(注意这跟上面的数据流图不太一样,上面关心的是数据量较少的运算,下面关心的是数据量大这个特点。那自然就设计到加载和存储的性能,这一部分也可以画数据流程图)
5.12.1 加载的性能
就是在循环中,某些值得确定必须要上行代码得结果才可以继续执行,这样就只能依赖,会比较慢,而不依赖上行结果的,可以并行,快些。 这部分也会加入关键路径之中,是因为它的加载和存储不依赖于循环的条件判断,自然跟前面的数据流向是一样的,也是关键路径。
5.12.3 存储的性能
3 理论分析
3.1 算法正确性证明
没有提供证明,建议参看算法导论关于算法正确性证明那一块。
3.2 性能分析优化点理论方面
1 深度优先搜索的复杂度分析是如何做到的?
使用聚集分析技术,它是平摊分析的一种,平摊分析主要包括三种。聚集分析,记账昂发,势能方法。
1.1 聚集分析是什么?
在聚集分析中,要证明对所有的n,由n个操作所构成的序列的总时间在最坏情况下为T(n)。平摊分析(Amortized Analysis)将数据结构的不同操作放到一起来考虑,而不是仅对某种操作的单一考虑。在平摊分析中,不会涉及概率问题。 平摊分析可以用来证明,在一系列操作中,即使某个操作代价很大,但平均代价仍是很小的。''' 请深刻理解平摊分析对某一个数据结构的操作总次数考虑 ,其中典型就是DFS和BFS用邻接链表时时间复杂度分析。 ''' ''' 那么借助聚集分析,我们可以得到我们算法的时间复杂度在O(v(avg(drgredd)的3次方-1)) 到 O(v(avg(drgredd)的7次方-1))之间 '''
1.2 记账方法没看