zoukankan      html  css  js  c++  java
  • android代码性能优化 performance tip 摘自官方文档

    官方文档真的是对android开发最好的指导,没有之一。这篇文章在training目录下,讲了以下几方面提高系统性能

    1. Avoid Creating Unnecessary Objects 避免一些临时的,路过过程处理时的不必要的对象创建
    2. Prefer Static Over Virtual 猜测意思是非对象的属性,设成static的,这样效率会提高20%,同时外部可以判断出这个是对象无法修改的。
    3. Use Static Final For Constants 即static量如果加了final,则这个量在编译成.dex文件时就创建了,使用时即可直接调用,否则是在类创建时去初始化这个量
    4. Avoid Internal Getters/Setters 类内部,不要用setter和getter方法,直接设置变量即可
    5. Use Enhanced For Loop Syntax 在java1.5以后,for:each循环遍历数组的效率要大大高于用i=0....的方法遍历
    6. Consider Package Instead of Private Access with Private Inner Classes 性能关键点,建议用包访问权限代替private访问权限
    7. Avoid Using Floating-Point  看不懂
    8. Know and Use the Libraries
    9. Use Native Methods Carefully  Native code 的使用主要服务于当你有an existing native codebase 想用于app, 而不是"speeding up" 你的java写的app
    10. Know And Use The Libraries
    11. Use Native Methods Judiciously
    12. Closing Notes


    Performance Tips

    This document primarily covers micro-optimizations that can improve overall app performance when combined, but it's unlikely that these changes will result in dramatic performance effects. Choosing the right algorithms and data structures should always be your priority, but is outside the scope of this document. You should use the tips in this document as general coding practices that you can incorporate into your habits for general code efficiency.

    There are two basic rules for writing efficient code:

    • Don't do work that you don't need to do.
    • Don't allocate memory if you can avoid it.

    One of the trickiest problems you'll face when micro-optimizing an Android app is that your app is certain to be running on multiple types of hardware. Different versions of the VM running on different processors running at different speeds. It's not even generally the case that you can simply say "device X is a factor F faster/slower than device Y", and scale your results from one device to others. In particular, measurement on the emulator tells you very little about performance on any device. There are also huge differences between devices with and without a JIT: the best code for a device with a JIT is not always the best code for a device without.

    To ensure your app performs well across a wide variety of devices, ensure your code is efficient at all levels and agressively optimize your performance.

    Avoid Creating Unnecessary Objects


    Object creation is never free. A generational garbage collector with per-thread allocation pools for temporary objects can make allocation cheaper, but allocating memory is always more expensive than not allocating memory.

    As you allocate more objects in your app, you will force a periodic garbage collection, creating little "hiccups" in the user experience. The concurrent garbage collector introduced in Android 2.3 helps, but unnecessary work should always be avoided.

    Thus, you should avoid creating object instances you don't need to. Some examples of things that can help:

    • If you have a method returning a string, and you know that its result will always be appended to aStringBuffer anyway, change your signature and implementation so that the function does the append directly, instead of creating a short-lived temporary object.
    • When extracting strings from a set of input data, try to return a substring of the original data, instead of creating a copy. You will create a new String object, but it will share the char[] with the data. (The trade-off being that if you're only using a small part of the original input, you'll be keeping it all around in memory anyway if you go this route.)

    A somewhat more radical idea is to slice up multidimensional arrays into parallel single one-dimension arrays:

    • An array of ints is a much better than an array of Integer objects, but this also generalizes to the fact that two parallel arrays of ints are also a lot more efficient than an array of (int,int) objects. The same goes for any combination of primitive types.
    • If you need to implement a container that stores tuples of (Foo,Bar) objects, try to remember that two parallel Foo[] and Bar[] arrays are generally much better than a single array of custom (Foo,Bar) objects. (The exception to this, of course, is when you're designing an API for other code to access. In those cases, it's usually better to make a small compromise to the speed in order to achieve a good API design. But in your own internal code, you should try and be as efficient as possible.)

    Generally speaking, avoid creating short-term temporary objects if you can. Fewer objects created mean less-frequent garbage collection, which has a direct impact on user experience.

    Prefer Static Over Virtual


    If you don't need to access an object's fields, make your method static. Invocations will be about 15%-20% faster. It's also good practice, because you can tell from the method signature that calling the method can't alter the object's state.

    Use Static Final For Constants


    Consider the following declaration at the top of a class:

    staticint intVal =42;staticString strVal ="Hello, world!";

    The compiler generates a class initializer method, called <clinit>, that is executed when the class is first used. The method stores the value 42 into intVal, and extracts a reference from the classfile string constant table for strVal. When these values are referenced later on, they are accessed with field lookups.

    We can improve matters with the "final" keyword:

    staticfinalint intVal =42;staticfinalString strVal ="Hello, world!";

    The class no longer requires a <clinit> method, because the constants go into static field initializers in the dex file. Code that refers to intVal will use the integer value 42 directly, and accesses to strVal will use a relatively inexpensive "string constant" instruction instead of a field lookup.

    Note: This optimization applies only to primitive types and String constants, not arbitrary reference types. Still, it's good practice to declare constants static final whenever possible.

    Avoid Internal Getters/Setters


    In native languages like C++ it's common practice to use getters (i = getCount()) instead of accessing the field directly (i = mCount). This is an excellent habit for C++ and is often practiced in other object oriented languages like C# and Java, because the compiler can usually inline the access, and if you need to restrict or debug field access you can add the code at any time.

    However, this is a bad idea on Android. Virtual method calls are expensive, much more so than instance field lookups. It's reasonable to follow common object-oriented programming practices and have getters and setters in the public interface, but within a class you should always access fields directly.

    Without a JIT, direct field access is about 3x faster than invoking a trivial getter. With the JIT (where direct field access is as cheap as accessing a local), direct field access is about 7x faster than invoking a trivial getter.

    Note that if you're using ProGuard, you can have the best of both worlds because ProGuard can inline accessors for you.

    Use Enhanced For Loop Syntax


    The enhanced for loop (also sometimes known as "for-each" loop) can be used for collections that implement the Iterable interface and for arrays. With collections, an iterator is allocated to make interface calls to hasNext() and next(). With an ArrayList, a hand-written counted loop is about 3x faster (with or without JIT), but for other collections the enhanced for loop syntax will be exactly equivalent to explicit iterator usage.

    There are several alternatives for iterating through an array:

    staticclassFoo{     int mSplat;}
    Foo[] mArray =...
    publicvoid zero(){     int sum =0;     for(int i =0; i < mArray.length;++i){         sum += mArray[i].mSplat;     }}
    publicvoid one(){     int sum =0;     Foo[] localArray = mArray;     int len = localArray.length;
       
    for(int i =0; i < len;++i){         sum += localArray[i].mSplat;     }}
    publicvoid two(){     int sum =0;     for(Foo a : mArray){         sum += a.mSplat;     }}

    zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.

    one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.

    two() is fastest for devices without a JIT, and indistinguishable from one() for devices with a JIT. It uses the enhanced for loop syntax introduced in version 1.5 of the Java programming language.

    So, you should use the enhanced for loop by default, but consider a hand-written counted loop for performance-critical ArrayList iteration.

    Tip: Also see Josh Bloch's Effective Java, item 46.

    Consider Package Instead of Private Access with Private Inner Classes


    Consider the following class definition:

    publicclassFoo{     privateclassInner{         void stuff(){             Foo.this.doStuff(Foo.this.mValue);         }     }
       
    privateint mValue;
       
    publicvoid run(){         Innerin=newInner();         mValue =27;         in.stuff();     }
       
    privatevoid doStuff(int value){         System.out.println("Value is "+ value);     }}

    What's important here is that we define a private inner class (Foo$Inner) that directly accesses a private method and a private instance field in the outer class. This is legal, and the code prints "Value is 27" as expected.

    The problem is that the VM considers direct access to Foo's private members from Foo$Inner to be illegal because Foo and Foo$Inner are different classes, even though the Java language allows an inner class to access an outer class' private members. To bridge the gap, the compiler generates a couple of synthetic methods:

    /*package*/staticintFoo.access$100(Foo foo){     return foo.mValue;}/*package*/staticvoidFoo.access$200(Foo foo,int value){     foo.doStuff(value);}

    The inner class code calls these static methods whenever it needs to access the mValue field or invoke thedoStuff() method in the outer class. What this means is that the code above really boils down to a case where you're accessing member fields through accessor methods. Earlier we talked about how accessors are slower than direct field accesses, so this is an example of a certain language idiom resulting in an "invisible" performance hit.

    If you're using code like this in a performance hotspot, you can avoid the overhead by declaring fields and methods accessed by inner classes to have package access, rather than private access. Unfortunately this means the fields can be accessed directly by other classes in the same package, so you shouldn't use this in public API.

    Avoid Using Floating-Point


    As a rule of thumb, floating-point is about 2x slower than integer on Android-powered devices.

    In speed terms, there's no difference between float and double on the more modern hardware. Space-wise,double is 2x larger. As with desktop machines, assuming space isn't an issue, you should prefer double tofloat.

    Also, even for integers, some processors have hardware multiply but lack hardware divide. In such cases, integer division and modulus operations are performed in software—something to think about if you're designing a hash table or doing lots of math.

    Know and Use the Libraries


    In addition to all the usual reasons to prefer library code over rolling your own, bear in mind that the system is at liberty to replace calls to library methods with hand-coded assembler, which may be better than the best code the JIT can produce for the equivalent Java. The typical example here is String.indexOf() and related APIs, which Dalvik replaces with an inlined intrinsic. Similarly, the System.arraycopy() method is about 9x faster than a hand-coded loop on a Nexus One with the JIT.

    Tip: Also see Josh Bloch's Effective Java, item 47.

    Use Native Methods Carefully


    Developing your app with native code using the Android NDK isn't necessarily more efficient than programming with the Java language. For one thing, there's a cost associated with the Java-native transition, and the JIT can't optimize across these boundaries. If you're allocating native resources (memory on the native heap, file descriptors, or whatever), it can be significantly more difficult to arrange timely collection of these resources. You also need to compile your code for each architecture you wish to run on (rather than rely on it having a JIT). You may even have to compile multiple versions for what you consider the same architecture: native code compiled for the ARM processor in the G1 can't take full advantage of the ARM in the Nexus One, and code compiled for the ARM in the Nexus One won't run on the ARM in the G1.

    Native code is primarily useful when you have an existing native codebase that you want to port to Android, not for "speeding up" parts of your Android app written with the Java language.

    If you do need to use native code, you should read our JNI Tips.

    Tip: Also see Josh Bloch's Effective Java, item 54.

    Performance Myths


    On devices without a JIT, it is true that invoking methods via a variable with an exact type rather than an interface is slightly more efficient. (So, for example, it was cheaper to invoke methods on a HashMap map than aMap map, even though in both cases the map was a HashMap.) It was not the case that this was 2x slower; the actual difference was more like 6% slower. Furthermore, the JIT makes the two effectively indistinguishable.

    On devices without a JIT, caching field accesses is about 20% faster than repeatedly accesssing the field. With a JIT, field access costs about the same as local access, so this isn't a worthwhile optimization unless you feel it makes your code easier to read. (This is true of final, static, and static final fields too.)

    Always Measure


    Before you start optimizing, make sure you have a problem that you need to solve. Make sure you can accurately measure your existing performance, or you won't be able to measure the benefit of the alternatives you try.

    Every claim made in this document is backed up by a benchmark. The source to these benchmarks can be found in the code.google.com "dalvik" project.

    The benchmarks are built with the Caliper microbenchmarking framework for Java. Microbenchmarks are hard to get right, so Caliper goes out of its way to do the hard work for you, and even detect some cases where you're not measuring what you think you're measuring (because, say, the VM has managed to optimize all your code away). We highly recommend you use Caliper to run your own microbenchmarks.

    You may also find Traceview useful for profiling, but it's important to realize that it currently disables the JIT, which may cause it to misattribute time to code that the JIT may be able to win back. It's especially important after making changes suggested by Traceview data to ensure that the resulting code actually runs faster when run without Traceview.

    For more help profiling and debugging your apps, see the following documents:

     
    再转一篇文章,也是基于官方这个说明的。
    现代的手持设备,与其说是电话,更像一台拿在手中的电脑。但是,即使是“最快”的手持设备,其性能也赶不上一台普通的台式电脑。
    
    这就是为什么我们在书写Android应用程序的时候要格外关注效率。这些设备并没有那么快,并且受电池电量的制约。这意味着,设备没有更多的能力,我们必须把程序写的尽量有效。
    
    本文讨论了很多能让开发者使他们的程序运行更有效的方法,遵照这些方法,你可以使你的程序发挥最大的效力。
    
    简介
    
    对于占用资源的系统,有两条基本原则:
    • 不要做不必要的事
    • 不要分配不必要的内存
    所有下面的内容都遵照这两个原则。
    
    有些人可能马上会跳出来,把本节的大部分内容归于“草率的优化”(xing:参见[The Root of All Evil]),不可否认微优化(micro-optimization。xing:代码优化,相对于结构优化)的确会带来很多问题,诸如无法使用更有效的数据结构和算法。但是在手持设备上,你别无选择。假如你认为Android虚拟机的性能与台式机相当,你的程序很有可能一开始就占用了系统的全部内存(xing:内存很小),这会让你的程序慢得像蜗牛一样,更遑论做其他的操作了。
    
    Android的成功依赖于你的程序提供的用户体验。而这种用户体验,部分依赖于你的程序是响应快速而灵活的,还是响应缓慢而僵化的。因为所有的程序都运行在同一个设备之上,都在一起,这就如果在同一条路上行驶的汽车。而这篇文档就相当于你在取得驾照之前必须要学习的交通规则。如果大家都按照这些规则去做,驾驶就会很顺畅,但是如果你不这样做,你可能会车毁人亡。这就是为什么这些原则十分重要。
    
    当我们开门见山、直击主题之前,还必须要提醒大家一点:不管VM是否支持实时(JIT)编译器(xing:它允许实时地将Java解释型程序自动编译成本机机器语言,以使程序执行的速度更快。有些JVM包含JIT编译器。),下面提到的这些原则都是成立的。假如我们有目标完全相同的两个方法,在解释执行时foo()比bar()快,那么编译之后,foo()依然会比bar()快。所以不要寄希望于编译器可以拯救你的程序。
    
    避免建立对象
    
    世界上没有免费的对象。虽然GC为每个线程都建立了临时对象池,可以使创建对象的代价变得小一些,但是分配内存永远都比不分配内存的代价大。
    
    如果你在用户界面循环中分配对象内存,就会引发周期性的垃圾回收,用户就会觉得界面像打嗝一样一顿一顿的。
    
    所以,除非必要,应尽量避免尽力对象的实例。下面的例子将帮助你理解这条原则:
    
    当你从用户输入的数据中截取一段字符串时,尽量使用substring函数取得原始数据的一个子串,而不是为子串另外建立一份拷贝。这样你就有一个新的String对象,它与原始数据共享一个char数组。
    如果你有一个函数返回一个String对象,而你确切的知道这个字符串会被附加到一个StringBuffer,那么,请改变这个函数的参数和实现方式,直接把结果附加到StringBuffer中,而不要再建立一个短命的临时对象。
    一个更极端的例子是,把多维数组分成多个一维数组。
    
    int数组比Integer数组好,这也概括了一个基本事实,两个平行的int数组比(int,int)对象数组性能要好很多。同理,这试用于所有基本类型的组合。
    如果你想用一种容器存储(Foo,Bar)元组,尝试使用两个单独的Foo[]数组和Bar[]数组,一定比(Foo,Bar)数组效率更高。(也有例外的情况,就是当你建立一个API,让别人调用它的时候。这时候你要注重对API借口的
    设计而牺牲一点儿速度。当然在API的内部,你仍要尽可能的提高代码的效率) 总体来说,就是避免创建短命的临时对象。减少对象的创建就能减少垃圾收集,进而减少对用户体验的影响。 使用本地方法 当你在处理字串的时候,不要吝惜使用String.indexOf(), String.lastIndexOf()等特殊实现的方法(specialty methods)。这些方法都是使用C/C++实现的,比起Java循环快10到100倍。 使用实类比接口好 假设你有一个HashMap对象,你可以将它声明为HashMap或者Map: Map myMap1 = new HashMap();HashMap myMap2 = new HashMap(); 哪个更好呢? 按照传统的观点Map会更好些,因为这样你可以改变他的具体实现类,只要这个类继承自Map接口。传统的观点对于传统的程序是正确的,但是它并不适合嵌入式系统。调用一个接口的引用会比调用实体类的引用多花费一倍的时间。 如果HashMap完全适合你的程序,那么使用Map就没有什么价值。如果有些地方你不能确定,先避免使用Map,剩下的交给IDE提供的重构功能好了。(当然公共API是一个例外:一个好的API常常会牺牲一些性能) 用静态方法比虚方法好 如果你不需要访问一个对象的成员变量,那么请把方法声明成static。虚方法执行的更快,因为它可以被直接调用而不需要一个虚函数表。另外你也可以通过声明体现出这个函数的调用不会改变对象的状态。 不用getter和setter 在很多本地语言如C++中,都会使用getter(比如:i = getCount())来避免直接访问成员变量(i = mCount)。在C++中这是一个非常好的习惯,因为编译器能够内联访问,如果你需要约束或调试变量,你可以在任何时候添加代码。 在Android上,这就不是个好主意了。虚方法的开销比直接访问成员变量大得多。在通用的接口定义中,可以依照OO的方式定义getters和setters,但是在一般的类中,你应该直接访问变量。 将成员变量缓存到本地 访问成员变量比访问本地变量慢得多,下面一段代码: for (int i = 0; i < this.mCount; i++)dumpItem(this.mItems[i]); 再好改成这样:  int count = this.mCount;Item[] items = this.mItems; for (int i = 0; i < count; i++)dumpItems(items[i]); (使用"this"是为了表明这些是成员变量) 另一个相似的原则是:永远不要在for的第二个条件中调用任何方法。如下面方法所示,在每次循环的时候都会调用getCount()方法,这样做比你在一个int先把结果保存起来开销大很多。 for (int i = 0; i < this.getCount(); i++)dumpItems(this.getItem(i)); 同样如果你要多次访问一个变量,也最好先为它建立一个本地变量,例如:  protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {if (isHorizontalScrollBarEnabled()) {int size = mScrollBar.getSize(false);if (size <= 0) {size = mScrollBarSize;}mScrollBar.setBounds(0, height - size, width, height);mScrollBar.setParams(computeHorizontalScrollRange(),computeHorizontalScrollOffset(),computeHorizontalScrollExtent(), false);mScrollBar.draw(canvas);}} 这里有4次访问成员变量mScrollBar,如果将它缓存到本地,4次成员变量访问就会变成4次效率更高的栈变量访问。 另外就是方法的参数与本地变量的效率相同。 使用常量 让我们来看看这两段在类前面的声明: static int intVal = 42;static String strVal = "Hello, world!"; 必以其会生成一个叫做<clinit>的初始化类的方法,当类第一次被使用的时候这个方法会被执行。方法会将42赋给intVal,然后把一个指向类中常量表的引用赋给strVal。当以后要用到这些值的时候,会在成员变量表中查找到他们。 下面我们做些改进,使用“final"关键字: static final int intVal = 42;static final String strVal = "Hello, world!"; 现在,类不再需要<clinit>方法,因为在成员变量初始化的时候,会将常量直接保存到类文件中。用到intVal的代码被直接替换成42,而使用strVal的会指向一个字符串常量,而不是使用成员变量。 将一个方法或类声明为"final"不会带来性能的提升,但是会帮助编译器优化代码。举例说,如果编译器知道一个"getter"方法不会被重载,那么编译器会对其采用内联调用。 你也可以将本地变量声明为"final",同样,这也不会带来性能的提升。使用"final"只能使本地变量看起来更清晰些(但是也有些时候这是必须的,比如在使用匿名内部类的时候)(xing:原文是 or you have to, e.g. for use in an anonymous inner class) 谨慎使用foreach foreach可以用在实现了Iterable接口的集合类型上。foreach会给这些对象分配一个iterator,然后调用 hasNext()和next()方法。你最好使用foreach处理ArrayList对象,但是对其他集合对象,foreach相当于使用 iterator。 下面展示了foreach一种可接受的用法: public class Foo {int mSplat;static Foo mArray[] = new Foo[27]; public static void zero() {int sum = 0;for (int i = 0; i < mArray.length; i++) {sum += mArray[i].mSplat;}} public static void one() {int sum = 0;Foo[] localArray = mArray;int len = localArray.length;for (int i = 0; i < len; i++) {sum += localArray[i].mSplat;}} public static void two() {int sum = 0;for (Foo a: mArray) {sum += a.mSplat;}}} 在zero()中,每次循环都会访问两次静态成员变量,取得一次数组的长度。 retrieves the static field twice and gets the array length once for every iteration through the loop. 在one()中,将所有成员变量存储到本地变量。 pulls everything out into local variables, avoiding the lookups. two()使用了在java1.5中引入的foreach语法。编译器会将对数组的引用和数组的长度保存到本地变量中,这对访问数组元素非常好。但是编译器还会在每次循环中产生一个额外的对本地变量的存储操作(对变量a的存取)这样会比one()多出4个字节,速度要稍微慢一些。 综上所述:foreach语法在运用于array时性能很好,但是运用于其他集合对象时要小心,因为它会产生额外的对象。 避免使用枚举 枚举变量非常方便,但不幸的是它会牺牲执行的速度和并大幅增加文件体积。例如: public class Foo {public enum Shrubbery { GROUND, CRAWLING, HANGING }} 会产生一个900字节的.class文件(Foo$Shubbery.class)。在它被首次调用时,这个类会调用初始化方法来准备每个枚举变量。每个枚举项都会被声明成一个静态变量,并被赋值。然后将这些静态变量放在一个名为"$VALUES"的静态数组变量中。而这么一大堆代码,仅仅是为了使用三个整数。 这样: Shrubbery shrub = Shrubbery.GROUND;会引起一个对静态变量的引用,如果这个静态变量是final int,那么编译器会直接内联这个常数。 一方面说,使用枚举变量可以让你的API更出色,并能提供编译时的检查。所以在通常的时候你毫无疑问应该为公共API选择枚举变量。但是当性能方面有所限制的时候,你就应该避免这种做法了。 有些情况下,使用ordinal()方法获取枚举变量的整数值会更好一些,举例来说,将: for (int n = 0; n < list.size(); n++) {if (list.items[n].e == MyEnum.VAL_X)// do stuff 1else if (list.items[n].e == MyEnum.VAL_Y)// do stuff 2} 替换为:  int valX = MyEnum.VAL_X.ordinal();int valY = MyEnum.VAL_Y.ordinal();int count = list.size();MyItem items = list.items(); for (int n = 0; n < count; n++){int valItem = items[n].e.ordinal(); if (valItem == valX)// do stuff 1else if (valItem == valY)// do stuff 2} 会使性能得到一些改善,但这并不是最终的解决之道。 将与内部类一同使用的变量声明在包范围内 请看下面的类定义: public class Foo {private int mValue; public void run() {Inner in = new Inner();mValue = 27;in.stuff();} private void doStuff(int value) {System.out.println("Value is " + value);} private class Inner {void stuff() {Foo.this.doStuff(Foo.this.mValue);}}} 这其中的关键是,我们定义了一个内部类(Foo$Inner),它需要访问外部类的私有域变量和函数。这是合法的,并且会打印出我们希望的结果"Value is 27"。 问题是在技术上来讲(在幕后)Foo$Inner是一个完全独立的类,它要直接访问Foo的私有成员是非法的。要跨越这个鸿沟,编译器需要生成一组方法:  static int Foo.access$100(Foo foo) {return foo.mValue;}static void Foo.access$200(Foo foo, int value) {foo.doStuff(value);} 内部类在每次访问"mValue"和"doStuff"方法时,都会调用这些静态方法。就是说,上面的代码说明了一个问题,你是在通过接口方法访问这些成员变量和函数而不是直接调用它们。在前面我们已经说过,使用接口方法(getter、setter)比直接访问速度要慢。所以这个例子就是在特定语法下面产生的一个“隐性的”性能障碍。 通过将内部类访问的变量和函数声明由私有范围改为包范围,我们可以避免这个问题。这样做可以让代码运行更快,并且避免产生额外的静态方法。(遗憾的是,这些域和方法可以被同一个包内的其他类直接访问,这与经典的OO原则相违背。因此当你设计公共API的时候应该谨慎使用这条优化原则) 避免使用浮点数 在奔腾CPU出现之前,游戏设计者做得最多的就是整数运算。随着奔腾的到来,浮点运算处理器成为了CPU内置的特性,浮点和整数配合使用,能够让你的游戏运行得更顺畅。通常在桌面电脑上,你可以随意的使用浮点运算。 但是非常遗憾,嵌入式处理器通常没有支持浮点运算的硬件,所有对"float"和"double"的运算都是通过软件实现的。一些基本的浮点运算,甚至需要毫秒级的时间才能完成。 甚至是整数,一些芯片有对乘法的硬件支持而缺少对除法的支持。这种情况下,整数的除法和取模运算也是有软件来完成的。所以当你在使用哈希表或者做大量数学运算时一定要小心谨慎。
  • 相关阅读:
    pycharm快捷键
    对迭代器操作的python 模块
    编译入门:传说中的编译是在做什么
    机器学习原理与算法(剧场版)机器学习中的最优化问题
    机器学习原理与算法(六) 支持向量机
    机器学习原理与算法(五) 生成学习算法
    机器学习原理与算法(四) 广义线性模型
    机器学习原理与算法(三) 监督学习之分类问题
    机器学习原理与算法(一) 机器学习概述
    Altera FPGA SoC搭建步骤
  • 原文地址:https://www.cnblogs.com/linxiaojiang/p/3475894.html
Copyright © 2011-2022 走看看