請看一個測試:
1、快速排序100次,然后計算排序一次所需要的時間。
public QuickSort() { long beginTime = System.nanoTime(); //排100遍 int b[]=a.clone(); for(int i=0;i<100;i++){ quick(b); } long total = System.nanoTime() - beginTime; //18133 System.out.println("小數組快速排序所需平均時間:" + total/100); }
在我的機器上,大概時間是:
小數組快速排序所需平均時間:18133
2、計算使用system.out.println打印一個字符串使用的時間:
public static void main(String[] args) { long beginTime = System.nanoTime(); for(int i=0;i<100;i++){ System.out.println("hello World"); } long total = System.nanoTime() - beginTime; /** * 18214 */ System.out.println("平均每次磁盤IO時間:" + total/100); }
打印時間大概是:
平均每次磁盤IO時間:18334
3、記錄從局域網的機器上獲取一個數據所需要的時間,我們使用虛擬機的memcached作為測試對象。
public class MemcachedTest { private MemcachedClient memCachedClient ; MemcachedTest() throws IOException{ memCachedClient = new MemcachedClient( new InetSocketAddress("192.168.1.103", 11211)); } public static void main(String[] args) throws IOException { new MemcachedTest().test(); } private void test() throws IOException { long beginTime = System.nanoTime(); for(int i=0;i<100;i++){ memCachedClient.get("dfsdf" + i); } long totalTime = System.nanoTime()-beginTime; //計算平均時間,這里抹掉了小數點,平均:516486 System.out.println("從局域網的memcached取一次數據所需平均時間:" + (totalTime/100)); } }
打印時間:
從局域網的memcached取一次數據所需平均時間:528065
其實測試1是計算機最擅長的操作,通常這個操作在計算機的內存進行,測試2操作其實是一個IO操作,會涉及到一個向顯卡寫數據的IO,第三個操作,其實是一個網絡IO,計算機會向網卡發送一個指令,到另外一條機器接收到命令,最后再從網卡讀結果。
最后得出,各時間大概有如下關系:
快速排序≈一次磁盤IO≈1/29memcached所需要的時間。
可以看出,系統中一旦發生IO,其所需的時間通常比直接操作內存慢好幾百倍,因為一個快速排序,會有好幾百次的數據移動和比較。對於經過網絡的操作,通常其速度又會比磁盤慢至少一個數量級。
盡管現在各種分布式產品很流行,但是這些基礎常識,還是應該牢記在心,這樣才能夠寫出高效率,高穩定性的軟件。下面是我總結出的代碼中幾條涉及到性能的幾條准則:
一、能夠在內存解決的盡量不要訪問磁盤,能在本機磁盤解決的,盡量不要訪問網絡。
二、多線程並發訪問程序中,能不共享數據的盡量不要共享(StringBuilder優於StringBuffer);能夠使用並發工具類(Atomic相關類,ConcurrentHashMap,CAS等)的,盡量不要使用synchronized。
三、在訪問數據庫時,對於常用的查詢條件一定要建好復合索引。能夠不排序的盡量不要排序,如果需要排序的,一定把排序字段提前存好,而不要在查詢的時候再計算排序字段。
四、批量優於單個操作,很多產品,比如數據庫,memcached等,都有批量及單個操作。在一些任務類中,如果能夠批量操作,則優先使用批量接口。
五、某些計算時間長(比如用戶要導出大文件)的操作,盡量進行優化。如果系統中必須存在這樣的操作,那就用隊列來專門解決這些問題,一定要控制系統線程的數量。
另外,在項目中,大多數系統的瓶頸可能都在數據庫,通常,如果一個系統的用戶超過1w,緩存就非常必要了。其實,性能問題,不僅僅出在代碼級別,還有可能是在中間件上發生,比如apache的默認配置,就非常不適合大的並發系統,mysql的緩存,oracle的最大連接數等等,甚至前段的JS加載及運行,都可能帶來性能問題。精通一些中間件,同樣可以提高系統的性能和吞吐量。
通常性能都是在用戶使用時才被發現,因此,排查問題比較困難。關於他的話題,寫出好幾本書也闡述不完,在這里,寫的標題有點誇張,但是,不可置疑,軟件中的IO確實是系統瓶頸很重要的一個原因。