精华内容
下载资源
问答
  • 优化工作程序是一种着眼于提高工作程序的有效性的方法(设定衡量标准),通过这种方法,可以持续地满足客户的合理要求
  • 程序优化

    万次阅读 2016-01-17 18:29:48
    程序优化是指利用软件开发工具对程序进行调整和改进,让程序充分利用资源,提高运行效率,缩减代码尺寸的过程。按照优化的侧重点不同,程序优化可分为运行速度优化和代码尺寸优化。 运行速度优化是指在充分掌握软...

    一、 序言

    程序优化是指利用软件开发工具对程序进行调整和改进,让程序充分利用资源,提高运行效率,缩减代码尺寸的过程。按照优化的侧重点不同,程序优化可分为运行速度优化和代码尺寸优化。

    运行速度优化是指在充分掌握软硬件特性的基础上,通过应用程序结构调整等手段来降低完成指定任务所需执行的指令数。在同一个处理器上,经过速度优化的程序比未经优化的程序在完成指定任务时所需的时间更短,即前者比后者具有更高的运行效率。代码尺寸优化是指,采取措施使应用程序在能够正确完成所需功能的前提下,尽可能减少程序的代码量。

    然而,在实际的程序设计过程中,程序优化的两个目标(运行速度和代码大小)通常是互相矛盾的。为了提高程序运行效率,往往要以牺牲存储空间、增加代码量为代价,例如程序设计中经常使用的以查表代替计算、循环展开等方法就容易导致程序代码量增加。而为了减少程序代码量、压缩存储器空间,可能又要以降低程序运行效率为代价。因此,在对程序实施优化之前, 应先根据实际需求确定相应的策略。在处理器资源紧张的情况下,应着重考虑运行速度优化;而在存储器资源使用受限的情况下,则应优先考虑代码尺寸的优化。在下面的讨论中,主要以速度优化为主,同时对空间进行一些讨论。

    二、 优化原则

    在决定优化前,需要问自己的几个问题:为什么要优化、哪些程序需要优化、优化的目标是什么、能够接受由此带来的可能的资源消耗(人力、维护、空间等)吗?

    1. 如果说程序足够快,足以满足应用的需要,那还有优化的必要性吗?我认为没有必要。因为优化是一种时间与空间及代码可读性的权衡,可能带有一定的负作用,比如增加维护成本等。优化的目的是在现有程序不能满足应用的要求情况下,更加充分合理的利用现有资源去改进程序的运行效率。

    2. 确定程序过程中最花费时间的地方,决定哪些程序需要优化。在进行任何优化之前,必须找到程序的瓶颈所在。按80对20的一般性原则,在程序运行过程中,80%的时间消耗在20%的程序代码中。更有甚者,比如在很多图像处理中,95%的时间消耗在内层循环中的图像数据运算;而在另一些程序中,95%的时间消耗在读写数据文件上,而在数据的处理时间却少于5%。所以,在优化过程中,首先照顾那些最常用且最消耗时间的程序,才能起到显而易见的效果。优化不常用的代码不但浪费时间,而且使得代码不清晰,难于调试。

    3. 设计优化目标。程序优化是一门平衡的艺术,速度的优化往往要以牺牲程序的可读性或者增加代码长度及空间为代价,所以一定要设计合理的优化目标,将程序优化带来的资源消耗控制在合理的范围内。

    在清楚了以上问题之后,可以考虑从以下三个层级进行程序优化,尝试从不同的层级思考优化方案,它们依次产生更显著的优化代码:

    1. 算法优化

    程序的目的是通过计算机语言来解决现实存在的问题。在程序设计过程中,首先面对现实问题,然后提出解决问题的办法,设计相应的数据结构和算法。所以,在设计时要仔细考虑是选择链表还是数组,采用线性查找还是二分查找。尝试打破一些传统规则,发掘和怀疑自己的某些假定,恢复问题的本来面目,选择和构造适合于问题的算法。

    2. C/C++优化

    C/C++语言优化主要根据其运行机制,使用合理的实现方式,避免不必要的时间消耗。此层次不触及算法,面向的是程序代码,而不是问题本身,所以是以一种局部的思维方式去优化程序。改变变量存储位置、参数传递优化、对像构造方式、除法优化等都属于这一级,这个级别的优化需要对C/C++的运行机制有很好理解以及掌握大量小的优化技巧和知识,需要不断的学习和积累。

    3. 汇编优化

    汇编语言最接近机器语言,每条汇编指令对应一条机器指令。在汇编语言中可以直接操作寄存器,调整指令执行顺序。由于汇编语言直接面对硬件平台,而不同的硬件平台的指令集及指令周期均有较大差异。所以针对不同平台,可以选择寄存器优化、指令调整、循环展开等优化方法。

    在上面三个层次中,有时候层次之间的界限并不明显,在哪一个层次产生更显著的优化效果,需要依据具体的问题来分析。但有一点是确定的,在优化时一定要按上面的顺序来依次考虑问题,首先考虑在算法层面进行优化,如果在算法层次没有优化的空间,再考虑在C/C++层次的优化,最后才是依据硬件平台进行汇编优化。

    最后,给出优化过程中必须遵循以下原则:

    1. 程序必须正确,然后才有优化;

    2. 优化是有方向和侧重点的,不只是单纯的速度优化,它是时间与空间的平衡;

    3. 优化算法设计优先。充分优化的笨拙算法实现始终比不上一个更好算法的普通实现;

    4. 优化不只是实践,它是一种理论与实践的结合。优化需要掌握很多软件和硬件平台优化的技巧,但是更重的是C/C++运行机制的理解及对数据结构和算法的把握;

    5. 代码的优化是永无止境的,要避免过犹不及;

    6. 程序优化不是在软件编程结束后才进行,而是融入在程序编写过程中;

    7. 如果确信程序不需要优化,那就根本不进行优化;

    8. 清晰简洁的代码,很多时候就是最好的优化;

    三、 算法优化

    优化的第一步是构造适合问题的最佳算法。在程序设计过程,我们常常使用经典算法和传统模式去解决问题,一般情况下都保证程序的正确性。而很多经典算法都对问题作了一些假设,在面对实际问题时,为了获得更高的程序效率,我们需要重新检视这些假设,并尝试不同的角度思考问题,寻求有针对性于问题的新算法。

    算法设计中应该熟悉算法,知道各种算法的优缺点,针对不同的问题选择不同的算法。在某些情况下,需要对多种不同的算法进行测试,从中找到最适合问题的算法。将比较慢的顺序查找法用较快的二分查找法代替,插入排序或冒泡排序法用快速排序、堆排序代替,都可以大大提高程序执行的效率。选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除操作,那使用链表要快得多。如果需要较多随机存取表中元素,则最好采用线性表作为数据存储结构。

    另外,不要对一个简单的问题,使用复杂的算法。比如说,如果使用二分查找,甚至线性查找也是足够快,就没有必要使用Hash表来处理小数据量表,即“杀鸡焉用牛刀”。

    四、 C/C++优化

    此层次优化是以局部的思维方式看程序,不触及算法层级,它面向的是代码,而不是问题。语句调整,循环优化、参数传递优化等都属于这一级,这个级别的优化需要对C/C++的运行机制有很好理解以及掌握大量小的优化技巧和知识,需要不断的积累。与此层次对应的是编译器,即当前C语言将编译成哪些汇编指令。如今,多数编译器都支持对程序速度和程序大小的优化,有些编译器还允许用户选择可供优化的内容及优化的程度。简单的语句调整、公共表达式提取、废代码删除等,当前的很多编译器也能做到了。然而,大部分编译器必须是保守的,除非用户指定了采用特定的优化方式,否则编译器只能依据最可靠的方法将C/C++语言,翻译机器语言,所以需要了解一些编译器的优化能力,以使自己的代码配合编译器做好优化。

    以下文中,所有生成汇编代码,均是由GUN ARM编译器生成,优化级别为O3(生成汇编方法,可以参看附录)。

    1. C/C++变量优化

    C/C++语言中变量依据其定义的方式不同,其存放位置可以分为寄存器、栈区、堆区和静态存储区三种区域。

    (1)寄存器上分配。当函数中定义的局部变量不多,且没有对局部变量的取地址操作时,则会将该变量会分配在寄存器中。当进行运算时,直接读寄存器,速度非常快。

    (2)在栈上分配。用户在函数体中定义了较多局部变量后,或对变量进行取地址操作,通过结构体返回值,则相应的变量会放在栈空间。函数执行结束后,这些存储单元自动被释放。一般的情况,由于栈区中数据在函数中都会被重复用到,加载时都能够Cache命中,一个周期内完成,效率很高,但是其分配的内存容量有限。

    (3)从静态存储区域分配。在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量、static变量。在ARM9平台,从静态区加载数据到寄存器,一般需要3个周期。如果在循环中,无序访问数据造成Cache不能命中,那么每次都需要3个周期加载,则比较费时。所以尽量让数据顺序访问,提高Cache命中率和访问速度。

    (4)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请的内存,程序员自己负责用free或delete释放内存,其访问速度和静态区相同。

    clip_image001

    建议1:把重复使用的指针型参数拷贝到本地局部变量。

    参看下面的代码:

    clip_image002

    比较左边的C代码,差别在于将step指向的值赋给局部变量temp。程序员一般会认为,A框中列出的 C代码,节约了一个整形变量的存储空间,而且直接用指针进行运算,少了一条赋值语句,速度应该更快。其实不然,将两部分C代码汇编后,再比较右边汇编指令,并没有节约存储空间(原因是变量分配在寄存器中),而且b中汇编代码到在计算每二个表达式时,少用了一条LDR指令。为什么呢?因为编译器不能断言step指针,是否和t1指向同一个地址,即在计算第一个表达式后,其指向值是否发生了改变。为了保证程序正确性,在第二次引用时,只能再次从内存加载step,增加了一个LDR指令周期数。对于这种指针型参数,编译器不能做到优化,如果step处于循环中,情况则更加糟糕。所以只能由程序员自己控制,配合编译器做好优化工作。

    建议2:尽量选用32位的数据类型变量。

    ARM 指令集支持有符号/无符号的8 位、16 位、32位整型变量。恰当的使用变量类型,不仅可以节省目标代码指令,并且可以提高代码运行效率。在程序编写过程中,应该尽可能地避免使用char、short 型的局部变量,因为操作8位/16位局部变量往往比操作32位变量需要更多指令, 请对比下列函数和它们的汇编代码。

    clip_image003

    首先比较左边的C代码,唯一的差别是计数器变量i的定义不同。一般认为,采用short类型变量比int节约了两个字节。其实并没有,因为ARM是32位运算(除非处于Thumb模式),寄存器也是32位,并不存在节约了两个字节。在i定义为32整型时,在对应的汇编代码中可以说编译器已经做到极致优化,其将循环结束条件都进行了优化。而在计数器定义为16位整形时,编译器则不但没有优化循环条件,而且还将计数器每次加1之后,利用移位操作对其进行宽度调整,将32位整形其转化为16位整型,增加了两条MOV指令。所以,在变量定义中,尽量使用32位宽度的数据类型,小于32位宽度的变量不但没有节约内存,还增加了很多无用的指令操作。在定义局部变量时,选用32位的数据有利于编译器优化。

    建议3:尽量在需要使用一个对象时才声明,并用初始化的方法赋初值。

    只要你定义了一个变量而且其类型带有一个构造函数或析构函数,那么当程序运行到这个变量定义式时,你就得承担构造成本;当这个变量离开作用域时,你得承担析构成本。即使这个变量最终没有被使用,仍需要耗费这些成本,所以要尽量避免这种情形。

    看如下面代码,定义一个CString类,其中有构造函数,析构函数及拷贝构造和赋值函数和一个获得字符串长度的函数。

    clip_image004

    下面利用CString定义如下两段基本相同的代码,只是局部变量str定义的位置不同。

    clip_image005

    对两种情况,进行一次调用函数对比(除去GetLength操作),如下。

    代码A:pStr->GetLength()==0时,1个构造函数 + 1个析构函数;

    pStr->GetLength()!=0时,1个构造函数 + 1个析构函数 + 1个赋值函数;

    代码B:pStr->GetLength()==0时,没有函数调用;

    pStr->GetLength()!=0时,1个拷贝构造函数 + 1个析构函数;

    可以看出对于A代码,无论什么情况,至少有1次构造和析构函数调用。最好情况,也比B情况下,多一次赋值函数调用。所以尽可能的滞后局部变量定义,最好延期到能够给它赋初值为止,可以提高程序速度。

    下面再看看循环体中局部变量定义,请看如下代码:

    clip_image006

    对循环体两种情况,进行一次调用函数对比,如下。

    代码A: 1个构造函数 + 1个析构函数 + n个赋值函数;

    代码B: n个拷贝构造函数 + n个析构函数;

    对于这两种情况就,需要考虑每个函数调用花费是多大了,然后再决定采用哪种写法。

    对于基本数据类型…(未完)

    2. 参数传递优化

    ARM在函数调用时,如果参数少于四个,则通过R0-R3传递参数。如果多于四个参数,则会将参数从右向左的顺序入栈。所以,在函数设计时,尽量限制函数的参数,不要超过4个,这样可以省去参数入栈操作,提高函数调用效率。

    另外,在函数调用过程中,如果通过传值的形式传递结构体参数和返回值时,则可能造成拷贝构造函数的调用。对于此类传递,尽可能选用指针或引用传递,不会增加额外的函数调用负担。看下面的函数中参数的传递方式。

    C.常量传递

    A.传值方式

    void fun_2(CString *pStr)

    {

    ...

    return;

    }

    B.指针传递

    void fun_1(CString pStr)

    {

    ...

    return;

    }

    clip_image007

    void fun_3(const CString *pStr)

    {

    ...

    return;

    }

    函数func_1以传值的方式传递参数,必然会调用CString的拷贝构造函数和析构函数,函数运行过程中不会改变调用者的内容。

    函数func_2以指针的方式传递参数,不会调用CString的拷贝构造函数和析构函数,但函数运行过程中,可以对调用者参数所指向的内容进行改变,会造成不安全性。

    为了保证调用者的值不会被改变,同时也不调用拷贝函数,那么就可以func_3方式传递参数,在类型前加上const关键字。

    3. 合理使用内联函数

    在编译内联函数时,编译器首先对参数和返回值进行检查,确认正确后,内联函数的代码就会直接替换函数调用。这样,就可以省去函数调用的开销,提高函数的执行效率。但是,每一内联函数的调用处都会有其一份拷贝,无疑增大了代码体积,程序加载后消耗更多的内存空间。所以,内联函数是典型的“以空间换时间”的优化策略。如果函数体内代码的执行时间远大于函数调用开销,那么内联的意义就不大了。

    所以,使用内联函数时注意以下事项:

    (1)将大多数内联函数限制在小型、被频繁调用的函数身上。

    (2)内联会导致目标代码体积变大,在空间有限的情况下,不宜使用内联函数。

    现在的编译器,都会拒绝将过于复杂的函数内联,自动地取消不值得内联的函数。

    4. 分支优化

    条件分支(if语句、switch语句)是编程中经常使用的基本操作,然而在某些时候(如循环)它可能带来严重的性能问题。在ARM上,分支是通过跳转指令B来实现。在ARM7和ARM9中,跳转指令B需要3个指令周期数,是占用指令周期数比较长的一条指令(原因是会清空流水线)。所以,如果在循环中大量使用条件分支,则严重影响程序效率。在XScale中,ARM处理器能对条件分支做出预测。如果分支预测正确,那么跳转指令B只需要花费1个指令周期,而如果预测错误,那么需要增加4个指令周期。这就是分支预测错误的惩罚。

    下面将讨论条件分支的一些有效优化方法。

    1.把条件分支移动到循环外

    先看如下代码,

    clip_image008

    对于A代码中奇偶模式的随机分支,即使当前最好的CPU也不可能做出好的预测,对于这种情况就要改写成两个for循环,一个处理偶数,一个处理奇数,如代码B。

    2.单独处理条件分支。

    图像处理算法,经常需要判断边界像素点,进行特殊处理;可以考虑的优化方案是把边界区域和内部区域分开处理;或者条件允许的话,扩大原图像的边界,形成"哨兵"数据,这样访问像素的时候就不用考虑越界的问题了。

    3.合并多个条件来减少条件分支

    编写代码过程中,常常使用 if((pStr1==0)&&( pStr2==0)&&( pStr3==0)),编译器将生成3个条件跳转指令,而且使分支可预测性大大降低,可以改写为: if((pStr1|pStr2|pStr3)==0) 从而同时改进代码和分支预测准确率。经测试,在GUN ARM编译器的O1优化级别上,能够对于上述语句自动优化。对下面的语句,编译器不能做到优化(因为编译器不能断定b0,b1,b2>=0),会产生三个比较指令和三个跳转指令,这种情形就需要程序员自己优化。

    if((b0>=64)||(b1>=64)||(b2>=64))  //b0,b1,b2>=0

    改写为:if((b0|b1|b2)>=64)

    5. 循环优化

    在ARM中的循环结构中,循环的终止条件将影响其执行效率,因为ARM指令有条件执行特性,所以在书写循环时尽量以0为终止条件。这样编译器可以用一条BNE(若非零则跳转)指令代替CMP (比较)和BLE (若小于则跳转)两条指令,既减小代码尺寸,又加快了运行速度。

    比较下面代码,其中R0=i,以10为终止条件比以0为终止条件的循环多了一条比较指令。

    clip_image009

    建议4:在循环书写过程中,如果循环体内代码与计数器i的增减顺序无关,尽量采用0作为循环终止条件。

    另外,一些不需要在循环中参加运算的任务放到循环外面,包括表达式、函数的调用、指针运算、数组访问等。

    6. 乘/除法优化

    在ARM中没有除法指令,所有的除法操作都是调用C库函数实现除法运算,模运算(%)也是通过除法运算来实现,一般要花费20~100个周期,是最慢的基本算术运算。

    建议5:在编程过程中,尽量避免除法。

    一种可能减少整数除法的地方是连除,由乘法代替。这个替换的副作用是有可能在乘积时会溢出,所以只能在一定范围的除法中使用。

    有些除法可用乘法代替,例如: if ( (x / y) > z)可变通为 if ( x > (y * z)) 。

    在能满足精度,且存储器空间冗余的情况下,也可考虑使用查表法代替除法。

    当除数为2的幂次方时,用移位操作代替除法。现在大部分编译器都能做到移位优化。如下所示:

    a=a/8,(当a>0)改为:a=a>>3,(当a<0)改为:a=(a+1)>>3;

    a=a%8,(当a>0)改为:a=a&7;

    另外,乘以任何一个整数都可以用移位和加法来代替乘法。ARM中加法和移位可以通过一条指令来完成,且执行时间少于乘法指令。例如: i = i * 5(3个指令周期) 可以用i = (i<<2) + i (1个指令周期)来代替。

    7. 浮点优化

    大多数ARM处理器硬件并不支持浮点运算。这就意味着编译器要把每一个浮点运算转换成一个子程序调用,用整型运算来模拟浮点运算。在使用浮点时,可以对其进行转化整形运算,将提高运行速度。如,图像缩放过程中,需要计算原图像和目标图像的比值,那么就有小数位,如果采用浮点数运算,则会影响程序速度。那么可以用移位操作放大被除数,将浮点数转化为整数来表示,看下面代码。

    clip_image010

    对于上面的将浮点转化为整型运算后,一般的情况下,程序的运行速度要快2~3倍。在转化过程中,要注意防止移位溢出。比如示例4-7的代码中,要求原图像的宽度和高度都必须小于65536,否则左移16位会造成溢出。

    五、 汇编优化

    汇编优化相对应的是具体硬件平台。用汇编重写并不是简单把高级语言改写为汇编实现,那样的汇编很可能没有当今编译器产生的代码好,所以如果决定用汇编实现,那就应该按照汇编的角度来规划自己的实现,适当的参考编译器生成的汇编码是可取的(特别是新手)。另外,在某些领域,使用处理器的新特性和新的指令集(如MMX) 等,将产生巨大的性能收益,这些地方经常采用汇编来实现。

    1. 流水线

    为了加快CPU的处理频率,现代CPU都设计了多级流水线,ARM处理器也不例外。ARM7采用3级流水线,ARM9采用5级流水线。下面以ARM9简单说明。

    clip_image011

    l 取指 在PC地址处取出指令。指令被加载到内核,然后进入流水线。

    l 译码 对前一个周期中取到的指令进行译码。

    l 运算 执行译码操作所得指令。

    l 加载1 通过LDR指令从内存加载特定数据。如果不是加载指令,那么这个步骤无效。

    l 加载2 通过LDRB、LDRH加载字节或半字数据。如果不是加载指令,那么这个步骤无效。

    2. 流水线阻塞

    如果在运算中一条指令需要前一条指令的执行结果,而这时结果还没有出来,那么处理器就会等待,这称为流水线数据相关。

    如果在运算中,一条指令运算需要前一条指令的运算部件,而前一条指令运算没有结束,那么处理器会等待,这称为流水线部件相关。典型是乘法部件。

    clip_image012

    在上面的汇编代码A中,LDR指令需要2个指令周期,其后紧跟的ADD指令需要使用LDR的结果,这时数据还未加载成功,相应产生了数据相关。那么,处理器只能等待加载成功,然后再进行ADD指令,流水线产生了1个指令周期的间隙。

    在汇编代码B中,第一条MUL指令在运算中占用了乘法部件,需要2个指令周期才能结束运算。其后的第二条指条也需要使用乘法部件,造成了部件相关,处理器等待第一个乘法结束后再进行第二个乘法,也产生了1个指令周期的间隙。

    3. 流水线优化

    由前面叙述可知,ARM指令执行是在指令流水线中进行,一条指令执行的时间会受其相邻指令的影响。流水线延迟或阻断会对处理器的性能造成影响,因此应该尽量保持流水线畅通。优化过程中可以通过调整代码中的指令序列,在指令间隙之间插入不存在相关性的指令,以使流水线满负荷运转。另外,跳转指令B将清空流水线重新加载指令,也严重影响流水线畅通。所以,要尽可能避免。对上面产生相关的指令序列进行调整如下。将序列中不相关的CMP指令插入到相关指令之间,填满流水线,去掉了一个指令周期的间隙,提高了流水线的吞吐率。

    clip_image013

    4. 寄存器分配优化

    CPU 对寄存器的存取要比对内存的存取快得多, 因此为变量分配一个寄存器,将有助于代码的优化和运行效率的提高。整型、指针等类型的变量都可以分配寄存器;一个结构的部分或者全部也可以分配寄存器。给循环体中需要频繁访问的变量分配寄存器也能在一定程度上提高程序效率。寄存器优化的基本原则是最大限度的使用寄存器,最少次数访问存储器。

    5. 合理使用条件指令

    ARM指令集的一个重要特征就是大多数的指令均可包含一个可选的条件码。当程序状态寄存器CPSR中的条件标志满足指令条件时,带条件码的指令才会执行。默认情况下,ARM指令并不会更新ARM寄存器CPSR中的N,Z,C,V标志。对大多数的指令,若要更新这些标志,则需要对指令助记符加后缀S。例外的是不写入目标寄存器的CMP指令,直接更新CPSR的标志位,因此不需要S后缀。利用条件执行通常可以省去单独的判断指令,因而可以减小代码尺寸并提高程序效率。

    例1:

    下面以一个if分支判断为例说明,

    clip_image014

    R0 = a, R1 = b使用条件指令的代码,相对未用条件代码,少了一条CMP指令。

    例2:

    clip_image015

    其中:R1 = a, R2 = x, R3 = y。

    在未使用条件指令的情况下,如果分支L1,则需要5个周期数,如果分支L2,则需要8个周期数。如果分支的情况各占50%,则未用条件指令平均需要(5*0.5+8*0.5)=6.5周期数。而使用条件指令,则恒定需要3个周期数。平均少用了3.5个周期。

    注意:

    对于示例5-5中,如果分支中所需要较多的操作,则不适合选用条件指令。因为,条件指令无论是否执行,都至少占用一个周期。

    6. 循环展开:

    流水线阻断的情况可通过循环拆解等方法加以改善。一个循环可以考虑拆解以减小跳转指令在循环过程中所占的比重,进而提高代码效率。以while循环为例生成的汇编指令

    clip_image016

    其中,R0 = i。

    这个循环开销包括一条设置条件标志的减法指令SUBS和跳转指令BGT.在ARM9平台上,减法指令SUBS占用1个周期数,跳转指令B占用3个周期数,也就是说循环一次的最小成本为4个指令周期数。如果在do something所占用4个指令周期,则有效指令周期数和逻辑控制指令周期数各占用50%。如果循环体内代码,一次处理四个对像(如3中代码),则有效指令周期数占一次循环指令总周期数的比重为80%,提高的代码执效率。

    注意:

    1. 对于较简单循环体函数,有很好的效果,如果循环体很大,则循环展开的意义不大。

    2. 循环展开后,程序的代码量将会增大,所以空间有限的情况下,要在时间与空间上权衡,避免过度展开。

    3. 循环展开后,要合理的处理计数器,避免访问越界。计数器最好以0为终点,这样可以省去与终点的比较指令。

    7. 有效使用地址模式

    LDR/STR指令中的自动索引(auto-indexing)功能时可以完成在加载过程中,往基址寄存器上加一个偏移量的操作,供后面的指令使用。

    例如:指令LDR R1, [R2], #4 完成R1= (*R2)及 R2 += 4两个操作,是后索引(post-indexing)的例子;而指令 LDR R1, [R2, #4]! 完成 R1 = *(R2 + 4) 和 R2 +=4 两个操作,是前索引(pre-indexing)的例子。上述指令将加载和地址变化合二为一,减少了一条指令,特别适合于数组处理中。

    8. SIMD单指令多数据

    在不带MMX指令的ARM处理器(ARM7和ARM9TDMI等)上,一般是不支持SIMD单指令多数据处理。但是合理的安排数据,还是可以进行并行操作的。比如两幅图像按alpha(0--255)合成中,合成公式为

    dst = (src1 * alpha + src2 * (255- alpha) ) / 256 (式1)

    = ((src1–src2) * alpha + src2*255) / 256。 (式2)

    图像中每Pixel中的A、R、G、B四个通道各占8位,对式2中,如果按A、G和R、B分解,用一个32位寄存器来保存,然后进行减法、乘法运算和移位操作,那么就可每次进行两个通道的并行计算。参看下面的代码和运算过程:

    clip_image017

    在计算R、B之后,再计算A、G两个通道,最后再将两次计算的结果进行合并,完成一个像素的混合。在上述多路并行运算中也要注意防止溢出,此示例中由于alpha的值处于0到255之间,所以不会溢出。

    9. MMX优化

    ...(未写)


    附录:

    1. 在Elastos 的GNU ARM环境使用GCC产生汇编代码的方法

    命令:gcc –O3 –S test.c

    参数说明:

    –On表示编译器对程序的优化级别,共有O1、O2、O3三个级别。如果不带此参数说明,则不进行编译器优化;

    –S 表示生成.S汇编文件;

    test.c 表示需要生汇编的C代码;

    2. GNU ARM 汇编和armasm宏汇编的格式差别

    每种编译器对其汇编指令格式都有不同的规定,下面主要区别GUN ARM和armasm宏汇编的区别。

    GNU Arm编译器对于函数调用采用以global开头,后面给出一个加下划线的外部联结标号,标号通过后面有一个冒号来识别。注释采用”@”符号。

    .global _ARGB32Blit2ARGB32

    _ARGB32Blit2ARGB32:

    STMFD SP!, {R4-R12,LR} @注释

    ...

    LDMFD SP, {R4-R12, PC}

    Armasm编译器对要求在任何ARM指令或Thumb指令出现以前,用一个AREA保留字定义一个范围。所有汇编文件必须用END保留字表示结束。标号的定义必须在一行的顶格书写。注释采用”;”符号。

    AREA sample, CODE, READONLY

    EXPORT ARGB32Blit2ARGB32

    ARGB32Blit2ARGB32

    STMFD SP!, {R4-R12,LR} ;注释

    ...

    展开全文
  • 使用智遥工作流,优化SAP请购流程

    千次阅读 2013-09-30 15:24:32
    使用智遥工作流,优化SAP请购流程

    传统请购流程,都是用户在SAP系统中填写请购单,然后再打印出来,递交给上级领导审批。领导审批完了,再到SAP系统中更新release标识。若中途请购单内容需要变更,则需要重新打印,审批。

    智遥工作流,能方便与SAP系统进行集成。轻松解决所有SAP单据审批问题。解决方案如下:
    1. 用户在sap中填写请购单内容。
    2. 后台每天定时检查需要审批的请购单,自动发给相应的领导进行审批;用户也可以根据需求手工到智遥工作流中进行发起。
    3. 领导直接在智遥工作流软件中进行审批,领导看到的单据内容实时从SAP抓取;保证内容是最新的。
    4. 领导审批完后,智遥工作流根据审批结果,自动更新release标识。
    对比传统审批方式,使用智遥工作流,具有以下几个优点:
    1. 减少打印。
    2. 提高审批效率。(整个流程通过电脑网络完成,无需人员走来走去)
    3. 少犯错。(数据实时更新,每次审批数据都是最新的)
    4. 方便流程追踪。(流程处于哪个环节,一清二楚)
    5. 方便审核。(所有审批数据都保存在系统中,查询方便)

    展开全文
  • 最近在工作中,对于图片压缩效果的需求比较迫切,在这里记录一下目前为止,优化多次的图片压缩流程。 在实际工作中,我们发现,只用一种方法并不能满足实际需要,总有一些图片比较特殊,所以我们选择使用gulp构建...

    前端性能优化之图片压缩详细流程

    最近在工作中,对于图片压缩效果的需求比较迫切,在这里记录一下目前为止,优化多次的图片压缩流程。

    在实际工作中,我们发现,只用一种方法并不能满足实际需要,总有一些图片比较特殊,所以我们选择使用gulp构建工具和手工在线压缩结合。

    工作目录

    这里简略写了一下必要的文件夹和文件,实际情况文件层次可以更加丰富

    myDemo
    -----dist
    		-css
        	-img
        	-js
        	html
    -----src
        	-less
        	-img
        	-js
        	html
    -----gulpfile.js
    -----package.json
    

    gulp插件使用

    一、 gulp-imagemin:

    1. 支持的图片格式:png,jpg,svg和gif
    2. 优势:压缩的常用格式多,压缩速度快,结合imagemin-pngquant imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo 可以提升总体压缩比,这也是这个插件为什么受众较多的原因。
    3. 缺点:有些时候,对gif动图的压缩比例微弱,甚至于有些png或者jpg图压缩不了。

    二、gulp-tinypng-nokey:

    1. 支持的图片格式:png,jpg
    2. 优势:突破使用官网api每月500张的限制,可以不用key,压缩比例高。
    3. 缺点:不能压缩gif,在我们的工作中,gif的压缩有很大需求,所以这点就限制了我们的使用。限制于网速,网速慢时,压缩时间较长,在实际工作中,不是很稳定。

    流程图演说与展示

    流程主要分为两大块:gulp构建工具压缩和手工在线压缩:

    一、gulp构建
    1.使用gulp-imagemin对所有格式(工作中实际格式为gif,png,jpg)图片进行总体压缩

    2.由于gulp-tinypng-nokey不能压缩gif,所以在上一步之后我们要程序判断图片格式,将图片分为gif和非gif进行下一步处理。

    • 2.1 gif组图进行程序判断图片大小,这里我们设的值为1.5m,可以灵活调整
    • 2.2 非gif组图(jpg,png)进行程序判断图片大小,这里我们设的值为200kb,可以灵活调整。

    3.接上2中判断完图片临界值之后,又分成两种情况:

    • 3.1 gif组图中,大于1.5m的gif图片,放入一个数组list;小于1.5m的gif图片,也就是大小我们的压缩要求的gif图,直接放入dist/img目录中。
    • 3.2 非gif组图(png,jpg)中,大于200kb的图,开启gulp-tinypng-nokey的压缩,压缩之后再次进行程序判断图片大小,仍旧大于200kb的图,放入一个数组list,已经小于200kb的图就放入dist/img目录中。

    4.将所有图片放入dist/img目录中或数组list之后,gulp构建结束。
    在这里插入图片描述

    如果此时的list数组为空,也就是所有图片都已经最优化了,图片压缩流程结束。
    但如果list数组不为空,则需要进行手工在线压缩:

    二、进行手工在线压缩

    在这里我推荐几个在线压缩工具:
    1.Tiomghttps://www.tiomg.org/
    Tiomg是一个免费在线图片压缩工具网站,上次图片可达30M,不限图片数量。

    2.Compressorhttps://compressor.io/compress

    3.Giftofspeedhttps://www.giftofspeed.com
    Giftofspeed有一个功能,可以输入你的网址(这里选用了不二家的首页测试),然后这个工具会建议对图片进行优化,这样可以发现很多问题。
    测试地址:https://www.giftofspeed.com/image-delivery/

    如图,
    在这里插入图片描述

    展开全文
  • 微信小程序优化建议_小程序优化建议setDatasetData是微信小程序开发中使用最频繁的接口,也是最容易引发性能问题的接口。在介绍常见的错误用法前,先简单介绍一下setData背后的工作原理。工作原理微信小程序的视图层...

    微信小程序优化建议_小程序优化建议

    setData

    setData微信小程序开发中使用最频繁的接口,也是最容易引发性能问题的接口。在介绍常见的错误用法前,先简单介绍一下setData背后的工作原理。

    工作原理

    微信小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的evaluateJavascript所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。

    evaluateJavascript的执行会受很多方面的影响,数据到达视图层并不是实时的。同一进程内的 WebView 实际上会共享一个 JS VM,如果 WebView 内 JS 线程正在执行渲染或其他逻辑,会影响 evaluateJavascript 脚本的实际执行时间,另外多个 WebView 也会抢占 JS VM 的执行权限;另外还有 JS 本身的编译执行耗时,都是影响数据传输速度的因素。

    常见的 setData 操作错误

    1. 频繁的去 setData

    在我们分析过的一些案例里,部分微信小程序会非常频繁(毫秒级)的去setData,其导致了两个后果:

    • Android 下用户在滑动时会感觉到卡顿,操作反馈延迟严重,因为 JS 线程一直在编译执行渲染,未能及时将用户操作事件传递到微信小程序的逻辑层,逻辑层亦无法及时将操作处理结果及时传递到视图层;
    • 微信小程序渲染有出现延时,由于 WebView 的 JS 线程一直处于忙碌状态,逻辑层到页面层的通信耗时上升,视图层收到的数据消息时距离发出时间已经过去了几百毫秒,渲染的结果并不实时;

    2. 每次 setData 都传递大量新数据

    setData的底层实现可知,我们的数据传输实际是一次evaluateJavascript脚本过程,当数据量过大时会增加脚本的编译执行时间,占用 WebView JS 线程。

    3. 后台态页面进行 setData

    当页面进入后台态(用户不可见),不应该继续去进行setData,后台动态页面的渲染用户是无法感受的,另外后台态页面去setData也会抢占前台页面的执行。

    图片资源

    目前微信小程序图片资源的主要性能问题在于大图片和长列表图片上,这两种情况都有可能导致 iOS 客户端内存占用上升,从而触发系统回收小程序页面。

    图片对内存的影响

    在 iOS 上,微信小程序的页面是由多个 WKWebView 组成的,在系统内存紧张时,会回收掉一部分 WKWebView。从过去我们分析的案例来看,大图片和长列表图片的使用会引起 WKWebView 的回收。

    图片对页面切换的影响

    除了内存问题外,大图片也会造成小程序页面切换的卡顿。我们分析过的案例中,有一部分微信小程序会在页面中引用大图片,在页面后退切换中会出现掉帧卡顿的情况。

    当前我们建议开发者尽量减少使用大图片资源。

    代码包大小的优化

    小程序一开始时代码包限制为 1MB,但我们收到了很多反馈说代码包大小不够用,经过评估后我们放开了这个限制,增加到 2MB 。代码包上限的增加对于开发者来说,能够实现更丰富的功能,但对于用户来说,也增加了下载流量和本地空间的占用。

    开发者在实现业务逻辑同时也有必要尽量减少代码包的大小,因为代码包大小直接影响到下载速度,从而影响用户的首次打开体验。除了代码自身的重构优化外,还可以从这两方面着手优化代码大小:

    控制代码包内图片资源

    小程序代码包经过编译后,会放在微信的 CDN 上供用户下载,CDN 开启了 GZIP 压缩,所以用户下载的是压缩后的 GZIP 包,其大小比代码包原体积会更小。 但我们分析数据发现,不同小程序之间的代码包压缩比差异也挺大的,部分可以达到 30%,而部分只有 80%,而造成这部分差异的一个原因,就是图片资源的使用。GZIP 对基于文本资源的压缩效果最好,在压缩较大文件时往往可高达 70%-80% 的压缩率,而如果对已经压缩的资源(例如大多数的图片格式)则效果甚微。

    及时清理没有使用到的代码和资源

    在日常开发的时候,我们可能引入了一些新的库文件,而过了一段时间后,由于各种原因又不再使用这个库了,我们常常会只是去掉了代码里的引用,而忘记删掉这类库文件了。目前小程序打包是会将工程下所有文件都打入代码包内,也就是说,这些没有被实际使用到的库文件和资源也会被打入到代码包里,从而影响到整体代码包的大小。

    展开全文
  • 杂文 - 优化工作效率

    千次阅读 2015-07-04 11:13:41
    1. 优化你的环境关闭邮件\QQ\微信\浏览器 找到FlowTime -> 超人时间 搞定熟悉工具 -> 快捷键2. 优化你的精力定时休息 -> 番茄工作法 ... 优化你的工作流程制定清晰的计划 -> 不确定不要开始工作,
  • Oracle工作流的优化

    2010-01-05 17:06:00
    对于Oracle工作流的优化方式主要分两大类,一类是流程设计上的优化,一类是数据库性能的优化流程设计优化:仔细选择同步处理和异步处理模式。如果选择同步处理模式,则在提交后系统立即进行工作流处理,在流程结束...
  • 运营流程优化

    2011-06-20 11:31:00
    同时,针对已有的工作方式, 提出改进方案, 最终形成比较高效的运营工作流程, 并进行推广实施。以下是优化前后运营流程图: 优化前的运营流程 : 优化后的运营流程: ...
  • 流程优化感悟

    千次阅读 2013-11-19 22:02:55
    刚JV的时候,领导要求整理出公司的各类流程。 那时候,我因为将公司级第二乱的项目带上了正规、得到了客户的认可,项目最成功交付。我当时策划了做全国两条产品线的持续集成,并开始做前期规划。 也许就是因为前面...
  • 优化程序性能总结

    千次阅读 2015-10-23 14:34:57
    系统层次关注系统的控制流程和数据流程优化主要考虑如何减少消息传递的个数;如何使系统的负载更加均衡;如何充分利用硬件的性能和设施;如何减少系统额外开销(比如上下文切换等)。 算法层次关注算法的选择(用...
  • 网站进行优化流程及步骤

    千次阅读 2010-12-07 11:08:00
    网站进行优化流程及步骤
  • abas的工作审批流编辑器,它可以帮助员工优化他们的工作流程,因为它有一个可以预先设置好的工作审批流程,可以使工作流自动触发,从而使工作有条不紊的按照流程进行。
  • 嵌入式程序优化

    千次阅读 2012-03-04 19:27:20
    嵌入式系统由于受功耗、成本和体积等因素的制约,嵌入式微处理器的处理能力与桌面系统处理器相比也存在较大差距,故... 嵌入式应用程序优化,指在不改变程序功能的情况下,通过修改原来程序的算法、结构,并利用软
  • ABAP程序优化心得

    千次阅读 2011-08-23 10:44:04
    影响ABAP程序的运行效率主要是在程序中大量数据的取得,如果取数不得方法,很影响报表的运行效率,所有优化ABAP程序主要是优化数据取数的方法。下面这几点可以有效的提高取数的效率,从而来提高程序的运行效率。 1....
  • SLAM流程优化后端

    千次阅读 2019-07-09 20:28:19
    SLAM的优化后端完成工作主要是对视觉前端得到的不够准确的相机位姿和重建地图进行优化微调。在视觉前端中,不管是进行位姿估计还是建图,都是利用相邻帧之间的关系来完成的,这种依赖局部约束且不停地链式进行的算法...
  • MySQL查询优化工作原理解析

    万次阅读 2016-05-28 21:06:31
    手册上MYSQL查询优化器概述;个人对MySQL优化器的理解;分析优化优化过程中的信息;调节MySQL优化器的优化
  • 微信小程序性能优化方案

    千次阅读 2020-12-17 18:44:21
    微信小程序性能优化方案 提高加载性能 小程序代码包准备(下载代码包) 开发者代码注入 页面渲染优化 提升渲染性能 setData工作原理 优化方法
  • 程序性能优化

    千次阅读 2020-08-16 16:11:17
    体验评分是一项给小程序的体验好坏打分的功能,它会在小程序运行过程中实时检查,分析出一些可能导致体验不好的地方,并且定位出哪里有问题,以及给出一些优化建议。 使用流程: 1、打开开发者工具,在详情里切换...
  • 程序优化

    千次阅读 2020-01-15 00:33:50
    程序优化篇 前言 项目优化这个话题已经讨论到烂大街了,但是我们日常中vue,react这些项目都是依托于webpack,grunt,gulb等常见的工具类进行项目的构建,打包,所以我们只需要对这些工具进行一些配置来优化即可,...
  • 程序性能优化

    千次阅读 2010-06-05 21:36:00
    设计层面的性能优化: 1、问题定义:良好的问题定义可以避免用户对问题需求的过高估计。问题定义和程序效率之间具有复杂的相互影响。
  • 如何改善和优化ERP流程管理

    千次阅读 2008-11-27 19:01:00
    众所周知,ERP流程管理与优化是ERP项目中的核心环节,也是ERP项目的关键所在。有很多人就会问了,ERP的流程优化做到什么样的阶段,才成功呢?个人认为,企业流程的改善是没有终点的,其是一个持续完善的过程。不过,...
  • C程序优化方法

    千次阅读 2010-12-29 01:01:00
    程序进行优化, 通常是指优化程序代码或程序执行速度。优化代码和优化速度实际上是一个予盾的统一, 一般是优化了代码的尺寸, 就会带来执行时间的增加, 如果优化程序的执行速度, 通常会带来代码增加的...
  • 业务流程分析和优化

    千次阅读 2014-09-13 13:19:00
    1 BPM方法理论 流程管理的思想,经历了从workflow...后来逐渐形成了BPM的思想,BPM从workflow阶段只关注流程的执行,到具有企业高度的流程管理,乃至流程治理,涵盖设计、建模、执行和优化周期。BPM出现了多个相...
  • 测试流程优化详解

    千次阅读 2020-03-06 17:58:32
    以下将从5个阶段来优化测试流程优化 一、需求阶段 1.梳理需求流程 a.规范需求的迭代时间 有的团队提需求的时间不统一,今天一个,明天一个,每个需求都是紧急需求。可以规定需求的周期,比如双周迭代,三周...
  • 个人因在找工作的时候遇到过好几次这个面试题!所以结合开发文档总结了一下,希望对大家有帮助。 优化有两点:第一个是渲染优化,第二个就是加载数据优化。 setData 频繁的去 setData 在我们分析过的一些案例里,...
  • BPI(业务流程优化),更多的强调局部、渐进性性的针对现有流程进行小规模的改善,而不是彻底性的否定或颠覆,这种工作方法更适合于企业自我开展的内部流程优化工作。 而我们倡导的流程梳理的概念,更适用于作为一...
  • Java程序性能优化——设计优化

    千次阅读 2016-07-15 00:27:47
    OK,之前写了一篇文章:“23种设计模式介绍以及在Java中的应用”详细介绍了如何将设计模式应用到Java编程中,而本文旨在介绍如何利用他们优化我们的程序,使其性能更佳。 设计模式的详细介绍请参照上面链接中的...
  • 新站上线SEO优化流程

    千次阅读 2014-03-05 13:42:48
    其实网站优化的技术并不...百度的考核期也要好几个月,没有耐心的人是一定坚持不下来的,但是我们只要了解网站优化流程就能让一切尽在掌握之中。下面我就为大家介绍一下网站优化流程。 1、网站定位 可以说在网站
  • LabVIEW程序优化相关技巧

    千次阅读 2015-06-06 19:51:03
    最近在做毕设最后一点优化工作收尾,对于程序优化仍是一个很虚无缥缈的概念和过程。参考了相关书籍,记录如下: 从增加程序运行速度和效率的角度: 1、执行处禁止VI调试可以大大提高VI运行速度,降低内存利用。 2...
  • 国家某某大型单位总部与全国各省公司的系统性能优化工作正式启动了,此次大型的性能优化工作给我们的压力确实是非常大的。  据了解,我们这支优化团队,目前在国内属于唯一的一支专业的性能优化团队了,优化的面...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 793,119
精华内容 317,247
关键字:

优化工作流程