請問為什麼到底什麼樣的業務是計算密集型,什麼樣的業務是IO密集型?為什麼說PHP最初設計是針對電腦密集型的,node.js是針對IO密集型的?
漂亮男人2017-05-16 13:14:46
昨天看廖雪峰博客中剛好有一章節講到:計算密集型 vs. IO密集型,摘錄如下:
我們可以把任務分為運算密集和IO密集型。
運算密集任務的特點是要進行大量的運算,消耗CPU資源,例如運算圓周率、對影片進行高清解碼等等,全靠CPU的運算能力。這種運算密集型任務雖然也可以用多任務完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應等於CPU的核心數。
運算密集型任務由於主要消耗CPU資源,因此,程式碼運行效率至關重要。 Python這樣的腳本語言運作效率很低,完全不適合運算密集型任務。對於計算密集型任務,最好用C語言編寫。
IO密集型,涉及到網路、磁碟IO的任務都是IO密集型任務,這類任務的特徵是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和記憶體的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。常見的任務大多是IO密集型任務,例如Web應用。
IO密集型任務執行期間,99%的時間都花在IO上,花在CPU上的時間很少,因此,用運行速度極快的C語言替換用Python這樣運行速度極低的腳本語言,完全無法提升運作效率。對於IO密集型任務,最適合的語言就是開發效率最高(程式碼量最少)的語言,腳本語言是首選,C語言最差。
高洛峰2017-05-16 13:14:46
有一個被用爛了的栗子,解釋 Node.js 語言 非阻塞
,事件驱动
概念,大約是這樣的:
你到餐廳吃飯,餐廳裡的服務生在幫你點餐後,會直接把菜單塞給廚房,然後立刻去到下一位顧客處,而非在廚房等待(
阻塞
)。直到烹饪结束后,厨师大喊“来拿菜”(事件
)。服务员跑回厨房,把菜品端到你的桌子上(事件处理
/回调
)
在這個栗子中,我們可以簡單的理解為:服務生相當於 CPU
,而厨房的工作就是I/O
。顯然的,在一個飯店裡,服務員的工作並不復雜,」等待上菜的時間「多數是花在」等待廚房「上。這與現今大多數網站的情況相似:網站通常不需要做太多複雜的運算,但需要花費大量的時間等待 I/O 處理,例如資料庫查詢,例如圖片、影片的讀取。
於是Node.js 捨棄了傳統Web 服務中「每當一次請求來臨,都打開一個線程來單獨處理」的做法,而是採用事件驅動的模型,默認情況下,僅用單線程就可以負擔相當高的並發量。
於是在這種情境下,我們說:Node.js 更适合 IO密集型的任务处理
。
但如果我們把上面的栗子更換一下,比如說咱們不開飯館,開銀行。每當客戶到來,要求取出指定的款項,作為服務員,你需要根據客戶的帳戶等級計算利率,計算利息,計算分紅,等等……(隨意想到的比方,可能不太恰當),而“取錢並交給客戶」這個動作本身卻不複雜。
這時候,就不能指望像飯店那樣,只靠一個服務員就能應付大量的客戶,因為每個請求都需要獨佔服務員大量的時間(不可避免的阻塞
)。那麼此時,傳統的模型,例如 PHP 或許就變得更適合了。
以上,希望能解你所惑
为情所困2017-05-16 13:14:46
IO密集型:就是IO比較多的應用,例如網路傳輸、資料庫等呼叫。 web應用大多是這種
運算密集:顧名思義就是需要大量的CPU運算的應用類型。像雲端運算一類的應用應該屬於這種。
曾经蜡笔没有小新2017-05-16 13:14:46
什麼是計算密集?舉個例子,把SQLite資料庫放到Linux記憶體檔案系統/dev/shm上對100萬資料進行SELECT查詢操作,那麼這個SELECT查詢,在使用了B+樹索引時,在B+樹索引上的二分查找就是典型的密集計算,如果沒有使用索引,那單純的掃描整個表也是密集計算.所以說,像關係數據庫這種東西,普遍都是使用C/C++實現,保證性能和控制內存佔用.
什麼是IO密集?例如SQLite資料庫不在記憶體,而在普通機械磁碟上,這時寫操作(INSERT/UPDATE/DELETE)都是典型的IO密集操作,因為這時就算CPU再快,SQLite引擎再快,也會被機械磁碟的寫入操作拖慢.所以為了並發,SQLite後來引入了WAL(write-ahead log)
預寫式日誌支援,具體配置就是執行一下SQLite查詢:
PRAGMA synchronous = NORMAL;
PRAGMA journal_mode = WAL;
WAL機制的原理是: 修改並不會直接寫入資料庫檔案,而是寫入到另一個稱為WAL的檔案(data.db3-wal). 如果交易失敗,WAL中的記錄會被忽略,撤銷修改. 如果事務成功,它將在隨後的某個時間(PRAGMA synchronous = NORMAL)被寫回到資料庫文件中,提交修改. 同步WAL文件和資料庫文件的行為被稱為checkpoint(檢查點),它由SQLite自動執行, 預設是在WAL檔案累積到1000頁修改的時候(PRAGMA wal_autocheckpoint). 在適當的時候,也可以手動執行checkpoint,SQLite提供了相關的介面,執行PRAGMA wal_checkpoint 之後,WAL檔案會被清空. 在讀的時候,SQLite將在WAL文件中搜尋,找到最後一個寫入點,記住它,並忽略在此之後的寫入點(這保證了讀寫和讀讀可以並行執行). 隨後,它確定所要讀的資料所在頁是否在WAL文件中,如果在,則讀WAL文件中的資料,如果不在,則直接讀資料庫文件中的資料. 在寫的時候,SQLite將之寫入到WAL文件中即可,但是必須保證獨佔寫入,因此寫與寫之間不能並行執行. WAL在實現的過程中,使用了共享內存技術(data.db3-shm),因此,所有的讀寫進程必須在同一個機器上,否則,無法保證資料一致性.
像WAL和checkpoint這種概念,在其他資料庫比如MySQL中也存在,只不過MySQL會更複雜,能支援更大規模的並發寫入操作.像WAL+checkpoint
這種寫入方式,你就可以看做是一種非同步的寫入.
Node的JS解釋器基於Chromium的V8,而V8具有JIT即時編譯機制,所以Node的密集計算的性能是要比PHP強的.雖然PHP官方現在也在開發PHP的JIT試驗分之,但其性能仍然不如V8.不過就算Node計算性能好,也幾乎不會有人推薦在Node裡執行大規模的密集計算,因為密集計算耗必定阻塞Node服務,這和Node倡導的無阻塞理念相背.
Node利用JS事件驅動的特性,做到了無阻塞,但基於回調的事件編程方式不利於代碼維護.而且Node這種服務不能像Tomcat這類Java服務使用單進程多線程利用多核,而V8也並不是為多執行緒設計,所以Node官方只能自己搞了一個cluster多進程模組來利用多核心.
PHP比較中庸,計算速度比不上JIT語言,但在非JIT的通用腳步語言中,PHP並不慢,比方說PHP5就已經比Python快,PHP7更是比Python快得多,比如php-src /Zend/bench.php測試中,PHP 7.1的耗時只有5.4的1/4.
另外PHP的PHP-FPM和Apache MOD_PHP這類多進程的FastCGI運行方式,也很容易利用多核.而且還能開啟opcache緩存PHP腳本的opcode到共享內存.也就是說,假設你定義了很多函數在functions.php裡,opcache緩存該腳本在內存後,其後每次請求PHP都不必重新解析腳本,直接就能執行opcode,性能提升是非常明顯的,尤其對於復雜的PHP應用.