筆記45 | 代碼性能優化建議[轉]

地址

筆記45 | 代碼性能優化建議[轉]


目錄

  • 前言
  • 避免創建不必要的對象
  • 選擇Static而不是Virtual
  • 常量聲明為Static Final
  • 避免內部的Getters/Setters
  • 使用增強的For循環
  • 使用包級訪問而不是內部類的私有訪問
  • 避免使用float類型
  • 使用庫函數
  • 謹慎使用native函數
  • 關于性能的誤區

前言

通常來說,高效的代碼需要滿足下面兩個原則:
a. 不要做冗余的工作
b. 盡量避免執行過多的內存分配操作
在優化App時其中一個難點就是讓App能在各種型號的設備上運行。不同版本的虛擬機在不同的處理器上會有不同的運行速度。你甚至不能簡單的認為“設備X的速度是設備Y的F倍”,然后還用這種倍數關系去推測其他設備。另外,在模擬器上的運行速度和在實際設備上的速度沒有半點關系。同樣,設備有沒有JIT也對運行速度有重大影響:在有JIT情況下的最優化代碼不一定在沒有JIT的情況下也是最優的。

避免創建不必要的對象

創建對象從來不是免費的。Generational GC可以使臨時對象的分配變得廉價一些,但是執行分配內存總是比不執行分配操作更昂貴。

隨著你在App中分配更多的對象,你可能需要強制gc,而gc操作會給用戶體驗帶來一點點卡頓。雖然從Android 2.3開始,引入了并發gc,它可以幫助你顯著提升gc的效率,減輕卡頓,但畢竟不必要的內存分配操作還是應該盡量避免。

因此請盡量避免創建不必要的對象,有下面一些例子來說明這個問題:

如果你需要返回一個String對象,并且你知道它最終會需要連接到一個StringBuffer,請修改你的函數實現方式,避免直接進行連接操作,應該采用創建一個臨時對象來做字符串的拼接這個操作。
當從已經存在的數據集中抽取出String的時候,嘗試返回原數據的substring對象,而不是創建一個重復的對象。使用substring的方式,你將會得到一個新的String對象,但是這個string對象是和原string共享內部char[]空間的。
一個稍微激進點的做法是把所有多維的數據分解成一維的數組:

一組int數據要比一組Integer對象要好很多。可以得知,兩組一維數組要比一個二維數組更加的有效率。同樣的,這個道理可以推廣至其他原始數據類型。
如果你需要實現一個數組用來存放(Foo,Bar)的對象,記住使用Foo[]與Bar[]要比(Foo,Bar)好很多。(例外的是,為了某些好的API的設計,可以適當做一些妥協。但是在自己的代碼內部,你應該多多使用分解后的容易)。
通常來說,需要避免創建更多的臨時對象。更少的對象意味者更少的gc動作,gc會對用戶體驗有比較直接的影響。


選擇Static而不是Virtual

如果你不需要訪問一個對象的值,請保證這個方法是static類型的,這樣方法調用將快15%-20%。這是一個好的習慣,因為你可以從方法聲明中得知調用無法改變這個對象的狀態。


常量聲明為Static Final

考慮下面這種聲明的方式

static int intVal = 42;
static String strVal = “Hello, world!”;

編譯器會使用一個初始化類的函數,然后當類第一次被使用的時候執行。這個函數將42存入intVal,還從class文件的常量表中提取了strVal的引用。當之后使用intVal或strVal的時候,他們會直接被查詢到。

我們可以用final關鍵字來優化:

static final int intVal = 42;
static final String strVal = “Hello, world!”;

這時再也不需要上面的方法了,因為final聲明的常量進入了靜態dex文件的域初始化部分。調用intVal的代碼會直接使用42,調用strVal的代碼也會使用一個相對廉價的“字符串常量”指令,而不是查表。

Notes:這個優化方法只對原始類型和String類型有效,而不是任意引用類型。不過,在必要時使用static final是個很好的習慣。


避免內部的Getters/Setters

像C++等native language,通常使用getters(i = getCount())而不是直接訪問變量(i = mCount)。這是編寫C++的一種優秀習慣,而且通常也被其他面向對象的語言所采用,例如C#與Java,因為編譯器通常會做inline訪問,而且你需要限制或者調試變量,你可以在任何時候在getter/setter里面添加代碼。

然而,在Android上,這不是一個好的寫法。虛函數的調用比起直接訪問變量要耗費更多。在面向對象編程中,將getter和setting暴露給公用接口是合理的,但在類內部應該僅僅使用域直接訪問。

在沒有JIT(Just In Time Compiler)時,直接訪問變量的速度是調用getter的3倍。有JIT時,直接訪問變量的速度是通過getter訪問的7倍。

請注意,如果你使用ProGuard,你可以獲得同樣的效果,因為ProGuard可以為你inline accessors.


使用增強的For循環

增強的For循環(也被稱為 for-each 循環)可以被用在實現了 Iterable 接口的 collections 以及數組上。使用collection的時候,Iterator會被分配,用于for-each調用hasNext()和next()方法。使用ArrayList時,手寫的計數式for循環會快3倍(不管有沒有JIT),但是對于其他collection,增強的for-each循環寫法會和迭代器寫法的效率一樣。

請比較下面三種循環的方法:

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

zero()是最慢的,因為JIT沒有辦法對它進行優化。
one()稍微快些。
two() 在沒有做JIT時是最快的,可是如果經過JIT之后,與方法one()是差不多一樣快的。它使用了增強的循環方法for-each。
所以請盡量使用for-each的方法,但是對于ArrayList,請使用方法one()。


使用包級訪問而不是內部類的私有訪問

參考下面一段代碼

public class Foo {private class Inner {void stuff() {Foo.this.doStuff(Foo.this.mValue);}}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);}
}

這里重要的是,我們定義了一個私有的內部類(Foo$Inner),它直接訪問了外部類中的私有方法以及私有成員對象。這是合法的,這段代碼也會如同預期一樣打印出”Value is 27”。

問題是,VM因為Foo和FooInnerFooInner中直接訪問Foo類的私有成員是不合法的。即使Java語言允許內部類訪問外部類的私有成員。為了去除這種差異,編譯器會產生一些仿造函數:

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

每當內部類需要訪問外部類中的mValue成員或需要調用doStuff()函數時,它都會調用這些靜態方法。這意味著,上面的代碼可以歸結為,通過accessor函數來訪問成員變量。早些時候我們說過,通過accessor會比直接訪問域要慢。所以,這是一個特定語言用法造成性能降低的例子。

如果你正在性能熱區(hotspot:高頻率、重復執行的代碼段)使用像這樣的代碼,你可以把內部類需要訪問的域和方法聲明為包級訪問,而不是私有訪問權限。不幸的是,這意味著在相同包中的其他類也可以直接訪問這些域,所以在公開的API中你不能這樣做。


避免使用float類型

Android系統中float類型的數據存取速度是int類型的一半,盡量優先采用int類型。

就速度而言,現代硬件上,float 和 double 的速度是一樣的。空間而言,double 是兩倍float的大小。在空間不是問題的情況下,你應該使用 double 。

同樣,對于整型,有些處理器實現了硬件幾倍的乘法,但是沒有除法。這時,整型的除法和取余是在軟件內部實現的,這在你使用哈希表或大量計算操作時要考慮到。


使用庫函數

除了那些常見的讓你多使用自帶庫函數的理由以外,記得系統函數有時可以替代第三方庫,并且還有匯編級別的優化,他們通常比帶有JIT的Java編譯出來的代碼更高效。典型的例子是:Android API 中的 String.indexOf(),Dalvik出于內聯性能考慮將其替換。同樣 System.arraycopy()函數也被替換,這樣的性能在Nexus One測試,比手寫的for循環并使用JIT還快9倍。


謹慎使用native函數

結合Android NDK使用native代碼開發,并不總是比Java直接開發的效率更好的。Java轉native代碼是有代價的,而且JIT不能在這種情況下做優化。如果你在native代碼中分配資源(比如native堆上的內存,文件描述符等等),這會對收集這些資源造成巨大的困難。你同時也需要為各種架構重新編譯代碼(而不是依賴JIT)。你甚至對已同樣架構的設備都需要編譯多個版本:為G1的ARM架構編譯的版本不能完全使用Nexus One上ARM架構的優勢,反之亦然。

Native 代碼是在你已經有本地代碼,想把它移植到Android平臺時有優勢,而不是為了優化已有的Android Java代碼使用。

如果你要使用JNI,請學習JNI Tips


關于性能的誤區

在沒有JIT的設備上,使用一種確切的數據類型確實要比抽象的數據類型速度要更有效率(例如,調用HashMap map要比調用Map map效率更高)。有誤傳效率要高一倍,實際上只是6%左右。而且,在JIT之后,他們直接并沒有大多差異。

在沒有JIT的設備上,讀取緩存域比直接讀取實際數據大概快20%。有JIT時,域讀取和本地讀取基本無差。所以優化并不值得除非你覺得能讓你的代碼更易讀(這對 final, static, static final 域同樣適用)。


我的Android征途

轉載于:https://www.cnblogs.com/xiangevan/p/10751672.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/370293.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/370293.shtml
英文地址,請注明出處:http://en.pswp.cn/news/370293.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

導彈攔截

鏈接 分析&#xff1a;經典DP題&#xff0c;最長不下降子序列的變種&#xff0c;同時需要記錄路徑&#xff0c;用pre[]數組記錄當前結點的前一個結點的方法很妙 1 #include "iostream"2 #include "cstdio"3 #include "cstring"4 #include "…

JUnit4參數化和理論示例

我始終依靠TestNG將參數傳遞給測試方法&#xff0c;以便為我的測試或套件提供一些靈活性。 但是&#xff0c;使用JUnit4可以實現相同的靈活性。 要使用它很簡單&#xff1a; package com.marco.test;import java.util.Arrays;import java.util.Collection;import junit.fram…

楊杰matlab神經網絡30例,MATLAB神經網絡30例

實例1 BP神經網絡在非線性函數擬合中的應用11.1 理論基礎11.1.1 BP網絡概述11.1.2 BP神經網絡的MATLAB函數21.2 非線性函數擬合方法6實例2 主元BP神經網絡在股票價格預測中的應用122.1 理論基礎122.1.1 主成分分析的原理122.1.2 主元神經網絡與股票預測142.2 股票價格的預測方法…

HTMLCSS 問題

1.子div使用浮動&#xff0c;父div高度自適應(個人感覺好用) 方法&#xff1a; css: <style> .clear{ clear:both} </style> html&#xff1a;在父div關閉之前添加<div class"clear"></div> 本文轉載于:猿2048?https://www.mk2048.com/…

python matplotlib數據可視化教程_matplotlib的Python數據可視化和探索——入門指南

matplotlib——最受歡迎的Python庫&#xff0c;用于數據可視化和探索我喜歡在Python中使用matplotlib。這是我學會掌握的第一個可視化庫&#xff0c;此后一直存在。matplotlib是最受歡迎的用于數據可視化和探索的Python庫&#xff0c;這是有原因的——它提供的靈活性和敏捷性是…

mysql 查詢所有子節點的相關數據

定義一個函數 CREATE DEFINERrootlocalhost FUNCTION getColumnChildLst(rootId INT) RETURNS varchar(1000) CHARSET utf8 BEGINDECLARE sTemp VARCHAR(1000);DECLARE sTempChd VARCHAR(1000);SET sTemp $;SET sTempChd cast(rootId as CHAR);WHILE sTempChd is not null DOS…

怎么用PHP實現年月日date,PHP date函數用法,php年月日寫法

日期和時間信息在 PHP 內部是以 64 位數字存儲的&#xff0c; 它可以覆蓋當前時間前后 2920 億年的時間&#xff0c;這個范圍之廣&#xff0c;足以滿足現有應用的實際需求。需要注意的是&#xff0c; 這些PHP時間函數都是依賴服務器的區域設置的&#xff0c; 所以在使用它們的時…

python氣象衛星云圖解析_【我教你系列】想要實時的地球圖像作為桌面?

Python 定時獲取衛星圖像做為桌面背景簡介這兩天看新聞的時候&#xff0c;突然發現最近有個臺風產生&#xff0c;并且在不斷的增強中。幸運的是從中央氣象臺預報的路徑來看&#xff0c;不會登陸我國。也正是通過這則新聞&#xff0c;我發現了一個不錯的衛星云圖網站。(ps:這篇文…

CSS權重的比較方法

CSS的權重如下&#xff1a; !important Infinity正無窮 行間樣式 1000 id 100 class|屬性|唯類 10 標簽|偽元素 1 通配符 0 256進制 當出現多個選擇器時 在同一行的選擇器權重相加即可 當兩個混合選擇器權重相同時優先選擇后面的選擇器 如&#xff1a; html <…

python_day8 面向對象常用 補充

__str__ 作用本來 打印 類對象是 打印的內存地址 但是在類中 增加 __str__ 參數 以后 再打印這個 類對象 就是顯示 __str__中的 return __del__作用 當 實例化的對象 在內存中 被釋放的時候執行 item操作通過 set get del 操作 item最終目的是將 類里面的 變量 像 字典一樣操作…

Spring中的@Cacheable開銷

Spring 3.1引入了很棒的緩存抽象層 。 最后&#xff0c;我們可以放棄所有本地化的方面&#xff0c;裝飾器和污染我們與緩存相關的業務邏輯的代碼。 從那時起&#xff0c;我們可以簡單地注釋重量級方法&#xff0c;并讓Spring和AOP機械完成工作&#xff1a; Cacheable("bo…

電工接線模擬仿真軟件_VERICUT數控加工仿真軟件,最強的數控加工模擬軟件,你知道么?...

VERICUT數控加工仿真軟件,最強的數控加工模擬軟件VERICUT軟件及功能簡介1、VERICUT軟件簡介VERICUT是美國CGTech公司開發一款專業的數控加工仿真軟件&#xff0c;是當前全球數控加工程序驗證、機床模擬、工藝程序優化軟件領域的領導者。該軟件自1988年開始推向市場以來&#xf…

php數據庫創建文件失敗怎么回事,安裝zblogPHP提示“創建c_option.php失敗”解決方法...

有zblog用戶反應在安裝zblog的最后一步時提示“創建c_option.php失敗”&#xff0c;如下圖&#xff1a;本文來說明下這個問題的原因和解決辦法。問題產生的原因&#xff1a;c_option.php是zblog的數據庫配置文件&#xff0c;當安裝完成的時候程序會自動創建這個文件。如果你的主…

一次搞清楚Mysql聯合索引,以及聯合索引究竟用了多少

一群DBA朋友聊天&#xff0c;突然拋出一個某公司聯合索引的面試題&#xff0c;當時好多人都蒙了&#xff0c;這次針對這個問題&#xff0c;做了個簡單的實驗&#xff0c;把聯合索引的作用一次搞清楚 問題大概是這樣的&#xff0c;聯合索引&#xff08;a,b,c,d&#xff09;下面這…

CSS Variables

CSS原生變量(CSS自定義屬性) 示例地址&#xff1a;https://github.com/ccyinghua/Css-Variables 一、css原生變量的基礎用法 變量聲明使用兩根連詞線"--"表示變量&#xff0c;"$color"是屬于Sass的語法&#xff0c;"color"是屬于Less的語法&a…

【基礎中的基礎】引用類型和值類型,以及引用傳遞和值傳遞

一直在博客園懟人&#xff0c;非常慚愧。所以鄭重決定&#xff1a; 好好寫一篇干貨&#xff0c;然后再接著懟人。 這是一起幫上陳百萬同學的求助&#xff0c;講了一會之后&#xff0c;我覺得很有些普世價值&#xff0c;干脆就發到園子來。面向小白&#xff0c;高手輕拍。 我們從…

Java 7:使用NIO.2進行文件過濾–第3部分

大家好。 這是使用NIO.2系列進行文件過濾的第3部分。 對于那些尚未閱讀第1 部分或第2部分的人 &#xff0c;這里有個回顧。 NIO.2是自Java 7起JDK中包含的用于I / O操作的新API。使用此新API&#xff0c;您可以執行與java.io相同的操作&#xff0c;以及許多出色的功能&#xf…

python眾數問題給定含有n個元素的多重集合s_分治法求眾數 給定含有n個元素的多重集合S 聯合開發網 - pudn.com...

分治法求眾數所屬分類&#xff1a;數據結構開發工具&#xff1a;C/C文件大小&#xff1a;240KB下載次數&#xff1a;3上傳日期&#xff1a;2018-01-04 20:19:09上 傳 者&#xff1a;九鼎說明&#xff1a; 給定含有n個元素的多重集合S&#xff0c;每個元素在S中出現的次數稱為該…

mysql 5.0 亂碼,解決MySQL 5.0.16的亂碼問題

導讀&#xff1a;問&#xff1a;怎樣解決MySQL 5.0.16的亂碼問題?答&#xff1a;MySQL 5.0.16的亂碼問題可以用下面的方法解決&#xff1a;1.設置phpMyAdminLanguage:Chinese simplified (zh-utf-8)MySQL 字符集&#xff1a;UTF-8 Unicode (utf8)MySQL 連接校對 gbk_chinese_c…

Hadoop Serialization -- hadoop序列化具體解釋 (2)【Text,BytesWritable,NullWritable】

回想&#xff1a;回想序列化&#xff0c;事實上原書的結構非常清晰&#xff0c;我截圖給出書中的章節結構&#xff1a;序列化最基本的&#xff0c;最底層的是實現writable接口&#xff0c;wiritable規定讀和寫的游戲規則 &#xff08;void write(DataOutput out) throws IOExce…