1. 程式人生 > 程式設計 >現代計算機模型-J.U.C併發系列(1)

現代計算機模型-J.U.C併發系列(1)

系列介紹

本系列主要重點介紹Java中的J.U.C併發程式設計,從原理,理論到實踐的過程,帶你一步步瞭解各種知識點,把所有技術點構成一個閉環,形成一個知識體系。

希望在J.U.C系列對你有新的瞭解和認知。

第一步,我想從計算機的底層模型來做為我這個系列的開頭,因為你只有理解了計算機的原理和結構,才能對於Java的一些設計(J.U.C,Sync,JMM)才有更加深刻的理解和使用。

本章節不涉及到Java相關的知識點。

現代計算機理論模型

現代計算機模型是基於-馮諾依曼計算機模型

image.png

也稱馮·諾伊曼模型(Von Neumann model)或普林斯頓結構(Princeton architecture),是一種將程式指令儲存器和資料儲存器合併在一起的計算機設計概念結構。依據馮·諾伊曼結構設計出的計算機稱做馮.諾依曼計算機,又稱儲存程式計算機。

計算機在執行指令時,會從儲存器中一條條指令取出,通過譯碼(控制器),從儲存器中取出資料,然後進行指定的運算和邏輯等操作,然後再按地址把運算結果返回記憶體中去。

接下來,再取出下一條指令,在控制器模組中按照規定操作。依此進行下去。直至遇到停止指令。

程式與資料一樣存貯,按程式編排的順序,一步一步地取出指令,自動地完成指令規定的操作是計算機最基本的工作模型。這一原理最初是由美籍匈牙利數學家馮.諾依曼於1945年提出來的,故稱為馮.諾依曼計算機模型。

計算機五大核心組成部分

  • 控制器(Control):

    • 是整個計算機的中樞神經,其功能是對程式規定的控制資訊進行解 釋,根據其要求進行控制,排程程式、資料、地址,協調計算機各部分工作及記憶體與外設的訪問等。
  • 運算器(Datapath)

    • 運算器的功能是對資料進行各種算術運算和邏輯運算,即對資料進 行加工處理。
  • 儲存器(Memory)

    • 儲存器的功能是儲存程式、資料和各種訊號、命令等資訊,並在需 要時提供這些資訊。
  • 輸入(Input system)

    • 略,輸入裝置有鍵盤、滑鼠器等。
  • 輸出(Output system)

    • 略。印表機等。

計算機模型

上圖為計算機模型流程圖

  • 計算器
    • 實際上就是CPU的工作
  • 儲存器
    • 計算器中的記憶體(RAM)

上圖的重點只需要看中間的部分,本質的邏輯就是CPU、儲存。CPU是如何儲存資料,計算;CPU、儲存是如何互動通訊的。

現代計算機硬體結構原理

下圖為計算器硬體的結構原理圖

未命名檔案.png

拓展槽:指的記憶體條。

我們可以把重點放到CPU,I/O匯流排,拓展槽上面,那為何結構是這樣設計的?

無論是CPU、儲存器、或者我們的計算器中的顯示器、滑鼠、鍵盤都是通過I/O匯流排來做互動通訊。

I/O匯流排可以理解為一條高速通道,在這其中,CPU的頻率最高的達到GHz,而記憶體條頻率遠遠無法和CPU相比擬,而玩過遊戲的小夥伴也知道,對計算器的視訊記憶體也是會在I/O匯流排上面做通訊,如此之多的模組都在這上面,而CPU又是極高的頻率。

所以CPU的結構原理就會有一個CPU Cache的設計,就是會把收到的指令複製一遍存到CPU Cache中,進行計算。 執行速度來對比的:暫存器 > L1 > L2 > L3 > 記憶體條,而記憶體條的讀寫速率遠遠小於CPU Cache,所以這個也是會有CPU Cache的設計產生的原因之一。

因為記憶體條的頻率遠遠小於CPU,所以才會有了CPU Cache的出現,記憶體條把編譯後的指令,通過I/O匯流排放到CPU Cache中,進行計算和儲存。

CPU

未命名檔案 (1).png

CPU內部結構劃分,主要有三種型別的單元

  • 控制單元

    • 控制單元是整個CPU的指揮控制中心,由指令暫存器IR(Instruction Register)、指令譯碼器ID(Instruction Decoder)和 操作控制器OC(Operation Controller) 等組成,對協調整個電腦有序工作極為重要。它根據使用者預先編好的程式,依次從儲存器中取出各條指 令,放在指令暫存器IR中,通過指令譯碼(分析)確定應該進行什麼操作,然後通過操作控制器OC,按確定的時序,向相應的部件發出微操作控制訊號。操作控制器OC中主要包括:節拍脈衝發生器、控制矩陣、時鐘脈衝發生器、復位電路和啟停電路等控制邏輯。
  • 運算單元

    • 運算單元是運算器的核心。可以執行算術運算(包括加減乘數等基本運算及其附加運算)和邏輯運算(包括移位、邏輯測試或兩個值比較)。相對控制單元而言,運算器接受控制單元的命令而進行動作,即運算單元所進行的全部操作都是由控制單元發出的控制訊號來指揮的,所以它是執行部件。
  • 儲存單元

    • 儲存單元包括 CPU 片內快取Cache和暫存器組,是 CPU 中暫時存放資料的地方,裡面儲存著那些等待處理的資料,或已經處理過的資料,CPU 訪問暫存器所用的時間要比訪問記憶體的時間短。 暫存器是CPU內部的元件,暫存器擁有非常高的讀寫速度,所以在暫存器之間的資料傳送非常快。採用暫存器,可以減少 CPU 訪問記憶體的次數,從而提高了 CPU 的工作速度。暫存器組可分為專用暫存器和通用暫存器。專用暫存器的作用是固定的,分別寄存相應的資料;而通用暫存器用。

CPU暫存器

每個CPU都包含一系列的暫存器,它們是CPU內記憶體的基礎。CPU在暫存器上執行操作的速度遠大於在主存上執行的速度。這是因為CPU訪問暫存器的速度遠大於主存。

CPU快取

即高速緩衝儲存器,是位於CPU與主記憶體間的一種容量較小但速度很高的儲存器。由於CPU的速度遠高於主記憶體,CPU直接從記憶體中存取資料要等待一定時間週期,Cache中儲存著CPU剛用過或迴圈使用的一部分資料,當CPU再次使用該部分資料時可從Cache中直接呼叫,減少CPU的等待時間,提高了系統的效率。

記憶體

一個計算機還包含一個主存。所有的CPU都可以訪問主存。主存通常比CPU中的快取大得多。、

上面的圖,指的是記憶體是如何和CPU進行互動工作的,我們大概知道這個記憶體結構就可以了,希望幫助大家瞭解一下整體的結構,瞭解計算機是這樣的工作方式的,達成一個認知即可。

問題舉例1

	public static void main(String[] args) {
		int i = 0;
		i = 1 + 1;
		System.out.println(i);
	}
複製程式碼

假如執行命令的main方法的時候,CPU、記憶體會按照如下的流程進行讀取儲存

1 . 初始化忽略,從 i = 1 + 1,開始,記憶體會把這條指令傳送到CPU中 2. CPU暫存器會去讀取(load)i的記憶體地址,然後交由ALU進行計算,計算結果(i=2)會以此快取到L1,L2,L3; 3. CPU會在空閒的時候再把結果同步到記憶體到,不會立馬同步到記憶體中,同步的條件,只有在自身的快取記憶體空間不足,才會進行寫入同步到記憶體中,那有沒有什麼辦法可以把結果硬性的同步到記憶體中? 這裡先引申出一個概念 : MESI快取一致性協議

CPU多核快取架構

image.png

問題舉例2

有兩個執行緒T1,T2分別到CPU1,CPU2去執行,執行以下程式碼方法

    private static int i = 0;
	public static void main(String[] args) {
		i +=1;
		System.out.println(i);
	}
複製程式碼

按照上面的結構,每個CPU都是獨立,並且每個執行緒都保持有自己的對於i的一個副本,也就是i + 1,每個CPU在回寫同步資料結果的時候,並不知道其他的CPU也在針對i的記憶體地址的結果進行計算回寫,所以有可能有出現計算錯誤。

當各自的執行緒在CPU執行完指令之後,實際的結果並非是 i + 1(T1) + 1(T2)的結果,有可能是 i = 2,這個就會出現我們的資料一致性問題。

快取一致性問題

image.png

在多處理器系統中,每個處理器都有自己的快取記憶體,而它們又共享同一主記憶體(MainMemory)。基於快取記憶體的儲存互動很好地解決了處理器與記憶體的速度矛盾,但是也引入了新的問題:快取一致性(CacheCoherence)。當多個處理器的運算任務都涉及同一塊主記憶體區域時,將可能導致各自的快取資料不一致的情況,如果真的發生這種情況,那同步回到主記憶體時以誰的快取資料為準呢?為瞭解決一致性的問題,需要各個處理器訪問快取時都遵循一些協議,在讀寫時要根據協議來進行操作,這類協議有MSI、MESI(IllinoisProtocol)、MOSI、Synapse、Firefly及DragonProtocol,等等。

匯流排加鎖(奔騰處理器)這個是很早之前的一個CPU的實現方法,這個的原理就是每次CPU要把資料回寫到記憶體中的時候,都需要去匯流排中獲取一個鎖,獲取鎖之後才可能把資料寫入到記憶體中。而沒有獲取到鎖的CPU就需要等待,直到獲取鎖為止。

MESI協議

Cache line : Cache中最小快取單位

  • M

    • 狀態:修改
    • 描述 : 該Cache line有效,資料被修改了,和記憶體中的資料不一致,資料只存在於本Cache中。
    • 監聽任務: Cache line 必須時刻監聽所有試圖讀該Cache line 相對就記憶體的操作,這種操作必須在快取將Cache line寫回主記憶體並將狀態變成S(共享)狀態之前被延遲執行。
  • E

    • 狀態:共享(Shared)
    • 描述 : 該Cache line有效,資料和記憶體中的資料一致,資料存在於很多的Cache中
    • 監聽任務: Cache line 必須監聽其他快取使該Cache line無效或者獨享該Cache line的請求,並將該Cache line更換狀態為無效(Invaild)。
  • I

    • 狀態:無效(Invaild)
    • 描述 : 該Cache line無效,無法做任何操作,不能回寫資料到主記憶體中。
    • 監聽任務: 無

CPU Cache line會時時刻刻的去嗅探 BUS(快取一致性協議),監聽是否有新的狀態改變,是否有新的指令(#LOCK等),以此來改變自身的Cache Line的狀態,以便後續可以做相應的操作。

問題舉例2-解決

  1. T1會從主記憶體load到指令到CPU1中,然後會把對應的Cache line狀態變成S(獨佔)狀態;
  2. T2也執行同樣操作,因為有2個CPU獲取了同一個主記憶體的資料,所以T2的Cache line 會變成一個S(Shared)狀態,T1中的Cache line會從 S(獨佔) --> E(共享)進行轉變。
  3. T1,T2會以此的把指令從L3到L2到L1再到暫存器,進行計算,然後回寫到L3中,因為對資料進行修改,T1中的CPU需要對Cache line 進行 鎖定操作,鎖定完成之後把狀態更改為M(修改)狀態,此時(i = 2 );當然T2也可以同時進行修改狀態為 M(狀態),具體看誰快,CPU彼此之間也是有時間延遲存在。
  4. 當T1更改完成狀態之後,以此同時會傳送一個訊息到BUS(快取一致性協議)中,通知其他的監聽該記憶體的Cache line。
  5. 此時,CPU會有一個指令週期,去進行裁決Cache line的狀態。T2的Cache line監聽到了資料變化(i = 2 ),會把自身的狀態更新為 I(Invaild)狀態,無法再更新資料(T2 i = 2)到主記憶體中。
  6. 假如T2還想再更新資料到主記憶體中,需要重新的從主記憶體中load資料(i = 2)到CPU中,再重新計算ALU,然後回寫到主記憶體中(i = 2 + 1)

Cache line 狀態失效場景

  1. i儲存長度大於一個Cache line的時候,這個時候需要儲存到多個Cache line,這個時候是無法做到MESI快取一致性協議的,只能用匯流排鎖。
  2. 當CPU不支援MESI

小結

回顧本章,我們瞭解到了計算機的模型,CPU,記憶體的互動通訊工作流程,以及如何保證快取一致性(MESI)等知識點。