摘要:有一句名言計算機科學中的任何問題都可以通過加上一層間接層來解決,一層不夠就再加一層。計算機科學的確是靠著一層又一層的抽象與封裝解決了巨量的問題。
David John Wheeler有一句名言“計算機科學中的任何問題都可以通過加上一層間接層來解決”,一層不夠就再加一層。后半句是我加的 (* ̄︶ ̄) ,雖然有點玩笑的意思,但是也的確能說明一些問題。計算機科學的確是靠著一層又一層的抽象與封裝解決了巨量的問題。
我們來簡單回顧一下:
開始的時候是程序員直接輸入二進制指令來操縱硬件的,不僅性能低下還很耗費用戶時間;
于是后來出現了操作系統,用文件、進程與線程、地址空間抽象了磁盤、CPU與內存,統一和簡化了硬件訪問方式;
機器語言對用戶不友好,于是便出現了匯編語言、中級語言(如C)、高級語言(如Java)的包裝,其最終執行還是要轉化為機器語言;
裸高級語言大家還用得不爽,覺得開發效率低,于是又出現了各種框架(如Spring、Hibernate)
......
這樣一層一層抽象包裝下來,我們要想實現一個功能比如定時寫文件等已經變成了很簡單的事,只需要幾行代碼就搞定了。
但是抽象層數過多就會導致我們頂層的用戶有時候會出現一些莫名其妙的問題,我們用一個實際的案例偽共享來說明一下
public class FalseSharing { private static AtomicLong time = new AtomicLong(0); public static void main(String... args) throws InterruptedException { int testNum = 50; for (int i = 0 ; i< testNum;i++){// 測試50次 Thread thread = new Thread(new Job()); thread.start(); thread.join(); } System.out.println(time.get()/1000/testNum + " us,avg"); } static class Job implements Runnable{ @Override public void run() { int number = 8; int iterationNumber = 20000; CountDownLatch countDownLatch = new CountDownLatch(number); Obj[] objArray = new Obj[number]; for (int i = 0;i < number;i++) { objArray[i] = new Obj(); } long start = System.nanoTime(); for (int i = 0;i < number;i++){ int ii = i; Thread thread = new Thread(new Runnable() { int iterationNumberInner = iterationNumber; @Override public void run() { while (iterationNumberInner-->0){ objArray[ii].aLong+=1L; } countDownLatch.countDown(); } }); thread.start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.nanoTime(); time.getAndAdd(end-start); } } @Contended private static final class Obj{ private volatile long aLong = 8L;//8Bytes // private volatile long a=2L,b=2L,c=2L,d=2L,e=2L,f=2L,g=2L;//***** } }
全部代碼在此,為了避免Java JIT(這也是一層抽象)的影響,我們每次執行都要加參數-Xint來強制使用解釋模式。
在我的機器上(4core,8processor,Core-i7),直接運行這段代碼得到結果1是4594 us,avg這個級別,在結果1基礎上把//*****一行取消注釋得到結果2是3916 us,avg 這個級別,在結果1基礎上運行參數加上-XX:-RestrictContended使得@Contended起作用就能得到結果3是3466 us,avg。
這時候頂層用戶就會莫名奇妙了,怎么多了幾個字段運行時間反而減小了?怎么加上@Contended后時間就更短了?
從Java代碼這一層次的抽象來看,完全是沒有問題的,那么問題究竟在哪呢?
我們知道一個CPU中的每個核是有自己的Cache的,高級別的L1是自己私有的,更低級別的L2、L3等可能是私有的,也可能是不同核共享的。這些不同級別的緩存(一次訪問時間在幾個ns左右)是用來彌補CPU的快速(一個周期通常零點幾個ns)和內存訪問的慢速(一次訪問時間在幾十個ns)之間的鴻溝的,而且是以CacheLine Size: N Bytes(Core-i7是64)為基本單位的,依據局部性原理一次性把內存中該訪問變量周圍的N Bytes內容拷貝到Cache中,如果一個對象不夠N Bytes,就有可能和幾個對象共用一個CacheLine,這樣一個線程刷新Cacheline就會導致其他線程的緩存失效,要去更低級別的Cache甚至內存訪問,就大大降低了訪問速度。
這樣回到剛才的問題,多加幾個字段能在一定程度上增大該對象所占空間,減小共用CacheLine的幾率,所以訪問時間減少了,而@Contended則使得一個對象一個CacheLine,直接幫我們避免了偽共享,所以訪問時間更少了。要解決這個問題,光知道Java這一層抽象(語法、JDK API等)是不可能的,還得懂操作系統、甚至CPU芯片原理這些層抽象才行。
再比如說,JVM幫我們把C/C++的手動內存管理封裝了一層抽象做到內存自動管理從而解放了我們,我們當然用得很爽,但是如果我們不懂這一層的抽象與封裝,那么程序OOM的時候就只能傻眼了。
最后總結一下,計算機科學中的任何問題都可以通過加上一層間接層來解決,這是很正確的,但是也正是因為一層一層的抽象和包裝,導致出了問題后很難定位,你都不知道問題究竟是出現在哪一層。所以要想提高技術水平不僅要知其然(看得見最頂層的包裝)也要知其所以然(看得見底層的包裝),每一層如果都懂或者說了解一些,那么出了問題很大程度上都可以憑直覺定位,即使不能憑直覺也可以通過各種手段debug,只會最頂層的抽象很多時候就只能望bug興嘆了。
訪問原文,來自MageekChiu。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69638.html
摘要:是一個典型的模式架構,用戶通過終端將字符流傳遞給。仿照的工作原理,我們在協議之上設計了,見下圖真實實現中,是應用層的通訊協議。僵尸進程問題我們知道由于缺少而導致僵尸進程無法回收的問題迄今存在。進入構建環境執行命令。 Coding WebIDE 做個國內首個基于 Docker 技術的云端開發平臺于4月1日正式上線。本文主要和大家分享和探討 Docker 在 Web IDE 中運用的一些經...
摘要:只能在不同的時候選用不同的假設和不同的理論來解釋問題,許來西的文章講到科學一定程度上通過放棄一貫性換取了實用性,放棄自洽性換取了它洽性。然而遺憾的是本身只提供了模塊和洋蔥模型的最小封裝。 在寫干貨之前,我想先探(qiang)討(diao)兩個問題,模式的局限性?模式有什么用? 最近看到一篇文章對我啟發很大,許來西在知乎的回答《哲學和科學有什么關聯?》,全篇較長,這里摘錄我要引出的一點:...
摘要:首先是最頂層的抽象,這個里面最基礎的就是和,記憶中和的抽象是類似的,將計算結果和偏導結果用一個抽象類來表示了。不過,本身并沒有像其它兩個庫一樣提供,等模型的抽象類,因此往往不會直接使用去寫模型。 本文將從deep learning 相關工具庫的使用者角度來介紹下github上stars數排在前面的幾個庫(tensorflow, keras, torch, theano, skflow, la...
摘要:李飛飛花名飛刀,阿里巴巴集團副總裁,高級研究員,達摩院首席數據庫科學家,阿里云智能事業群數據庫產品事業部負責人,杰出科學家。是阿里云的云原生數據庫,目前已有非常深厚的技術積累。 阿里妹導讀:云計算大潮來襲,傳統數據庫市場正面臨重新洗牌的情境,包括云數據庫在內的一批新生力量崛起,動搖了傳統數據庫的壟斷地位,而由云廠商主導的云原生數據庫則將這種改變推向了高潮。 云時代的數據庫將面臨怎樣的...
閱讀 1836·2023-04-26 00:59
閱讀 3134·2021-11-15 18:10
閱讀 3079·2021-09-22 16:02
閱讀 768·2021-09-02 15:15
閱讀 3719·2019-08-30 15:56
閱讀 1921·2019-08-30 15:54
閱讀 2862·2019-08-29 16:31
閱讀 2039·2019-08-29 16:10