IO效能探索分析

樑樺發表於2019-05-10

IO效能(相對於CPU效能)探索分析

  • 體驗一:電腦經常卡頓
    • 公司發的膝上型電腦,硬體配置cpu i5六代,記憶體8G,機械硬碟無固態。每天編譯一個富客戶端GUI工程的時候,經常會導致電腦卡頓,CPU與記憶體往往都還沒有達到峰值,磁碟顯示100%

  • 體驗二:IO執行緒比UI執行緒後退出。
    • 客戶端應用在退出的時候,客戶端UI介面其實已經消失了,但是客戶端的日誌檔案往往在UI介面消失幾秒後才寫完——也就是IO執行緒比UI執行緒要慢好幾秒。

  • 體驗三:如下例子。(執行環境:8G記憶體,i5六代,固態硬碟,桌上型電腦)

  • 例一:計數10萬,計算毫秒數

    public class TestWithNoIO {
    
      public static void main(String[] args) {
    
    	long begin = System.currentTimeMillis();	
    	int i = 100000;
    	for (int j = 0; j < i; j++) {
    
    	}
    	System.out.println("testNonIO 耗時(毫秒):" + (System.currentTimeMillis() - begin));
      }
    }
    
    • 執行結果:
      testNonIO 耗時(毫秒):0
      執行許多次,數值穩定在1毫秒或者0毫秒

  • 例二:在for迴圈中增加System.out.print(""),注意是沒有空格的空字串,如下:

    public class TestWithNOLR {
    
      public static void main(String[] args) {
    	long begin = System.currentTimeMillis();
    	int i = 100000;
    	for (int j = 0; j < i; j++) {
    		System.out.print("");
    	}
    	System.out.println("testIOWithNoLR 耗時:"+(System.currentTimeMillis()-begin));
      }
    }
    
    • 執行結果:
      testIOWithNoLR 耗時:13
      執行多次,數值穩定在10毫秒左右。
    • 分析:雖然實際上什麼也沒有輸出,但是僅僅因為使用了IO,耗時已經明顯增加

  • 例三:這次我們在例二的基礎上,空字串裡新增一個空格。如下:

    public class TestWithNOLR {
    
      public static void main(String[] args) {
    	long begin = System.currentTimeMillis();
    	int i = 100000;
    	for (int j = 0; j < i; j++) {
    		System.out.print(" ");
    	}
    	System.out.println("testIOWithNoLR 耗時:"+(System.currentTimeMillis()-begin));
      }
    }
    
    • 執行結果:
      testIOWithNoLR 耗時:187
      執行多次,數值穩定在180毫秒左右(注意:空格輸出已被我截去)
    • 分析:IO有了實際輸出量(增加了輸出內容),耗時大大增加

  • 例四:這次在例三的基礎上,多增加一個空格。發現在例三的基礎上多了10毫秒的時間。

  • 例五:這次我們把IO由不換行的輸出字串,改為輸出換行

    public class TestWithLR {
    
      public static void main(String[] args) {
    	long begin = System.currentTimeMillis();
    	int i = 100000;
    	for (int j = 0; j < i; j++) {
    		System.out.println();
    	}
    	System.out.println("testIOWithLR 耗時:"+(System.currentTimeMillis()-begin));
      }
    }
    
    • 執行結果:
      testIOWithLR 耗時:198
      執行多次,我們發現數值穩定在190多毫秒,基本上和例四所消耗的時間持平(也就是輸出兩個空格)。

  • 例六:這次我們在例五的基礎上增加一個換行

    public class TestWithLR {
    
      public static void main(String[] args) {
    	long begin = System.currentTimeMillis();
    	int i = 100000;
    	for (int j = 0; j < i; j++) {
    		System.out.println();
    		System.out.println();
    	}
    	System.out.println("testIOWithLR 耗時:"+(System.currentTimeMillis()-begin));
      }
    }
    
    • 執行結果:
      testIOWithLR 耗時:393
      執行多次,我們發現數值穩定在400毫秒左右。
    • 分析:換行數量翻倍以後,IO耗時翻倍。繼續增加換行,發現非常符合這個規律。

  • 以上是輸出到控制檯,大家可以試一下輸出到機械硬碟,輸出到固態硬碟。

  • 總結:
    1. IO效率真的是比CPU效率低很多
    2. IO增加,耗時會增加
    3. 換行的IO增加,比不換行的IO增加,耗時明顯增加要快得多得多
  • 啟發:
    • 日誌角度:
      1. 不要在生產的程式碼中嵌入任何System.out.print。原因有三點,a.沒儲存的日誌屬於無效日誌;b.效能下降(上面的例子只是以毫秒為單位);c、輸出可能重定向;
      2. 如果程式中IO比較多的時候,儘量實現IO執行緒和純CPU執行緒分離(通常的日誌框架就是這麼做的,例如log4j或者logback,通常是單獨的執行緒在執行)。佔CPU的執行緒處理核心業務邏輯,佔IO的執行緒處理日誌或其他非同步就可以完成的內容。
      3. 日誌輸出儘量不要太頻繁(例如在迴圈中高頻輸出日誌),能少一行日誌就少一行日誌,能少一截儘量少輸出一截(從垃圾回收角度來說也應該這麼做,日誌裡太多字串)
    • 資料庫角度:
      1. 設計資料庫的時候,如果有些欄位可用列舉儘量用列舉在記憶體中儲存(減少磁碟IO);
      2. ……未完待續

  • 可參考資料:
    1. 一張表欄位多為何要拆表:https://www.cnblogs.com/hzhuxin/p/7985452.html

相關文章