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耗時翻倍。繼續增加換行,發現非常符合這個規律。
- 執行結果:
- 以上是輸出到控制檯,大家可以試一下輸出到機械硬碟,輸出到固態硬碟。
- 總結:
- IO效率真的是比CPU效率低很多;
- IO增加,耗時會增加;
- 換行的IO增加,比不換行的IO增加,耗時明顯增加要快得多得多。
- 啟發:
- 日誌角度:
- 不要在生產的程式碼中嵌入任何System.out.print。原因有三點,a.沒儲存的日誌屬於無效日誌;b.效能下降(上面的例子只是以毫秒為單位);c、輸出可能重定向;
- 如果程式中IO比較多的時候,儘量實現IO執行緒和純CPU執行緒分離(通常的日誌框架就是這麼做的,例如log4j或者logback,通常是單獨的執行緒在執行)。佔CPU的執行緒處理核心業務邏輯,佔IO的執行緒處理日誌或其他非同步就可以完成的內容。
- 日誌輸出儘量不要太頻繁(例如在迴圈中高頻輸出日誌),能少一行日誌就少一行日誌,能少一截儘量少輸出一截(從垃圾回收角度來說也應該這麼做,日誌裡太多字串)
- 資料庫角度:
- 設計資料庫的時候,如果有些欄位可用列舉儘量用列舉在記憶體中儲存(減少磁碟IO);
- ……未完待續
- 日誌角度:
- 可參考資料:
- 一張表欄位多為何要拆表:https://www.cnblogs.com/hzhuxin/p/7985452.html