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