當(dāng)前位置:首頁 > 公眾號精選 > CPP開發(fā)者
[導(dǎo)讀]↓推薦關(guān)注↓無論你寫什么樣的代碼都會交給CPU來執(zhí)行,所以,如果你想寫出性能比較高的代碼,這篇文章中提到的技術(shù)還是值得認真學(xué)習(xí)的。另外,千萬別覺得這些東西沒用,這些東西非常有用,十多年前就是這些知識在性能調(diào)優(yōu)上幫了我的很多大忙,從而跟很多人拉開了差距……基礎(chǔ)知識首先,我們都知道現(xiàn)...


無論你寫什么樣的代碼都會交給 CPU 來執(zhí)行,所以,如果你想寫出性能比較高的代碼,這篇文章中提到的技術(shù)還是值得認真學(xué)習(xí)的。另外,千萬別覺得這些東西沒用,這些東西非常有用,十多年前就是這些知識在性能調(diào)優(yōu)上幫了我的很多大忙,從而跟很多人拉開了差距……


基礎(chǔ)知識

首先,我們都知道現(xiàn)在的 CPU 多核技術(shù),都會有幾級緩存,老的 CPU 會有兩級內(nèi)存(L1 和 L2),新的CPU會有三級內(nèi)存(L1,L2,L3 ),如下圖所示:


其中:


  • L1 緩存分成兩種,一種是指令緩存,一種是數(shù)據(jù)緩存。L2 緩存和 L3 緩存不分指令和數(shù)據(jù)。
  • L1 和 L2 緩存在每一個 CPU 核中,L3 則是所有 CPU 核心共享的內(nèi)存。
  • L1、L2、L3 的越離CPU近就越小,速度也越快,越離 CPU 遠,速度也越慢。
再往后面就是內(nèi)存,內(nèi)存的后面就是硬盤。我們來看一些他們的速度:


  • L1 的存取速度:4 個CPU時鐘周期
  • L2 的存取速度:11 個CPU時鐘周期
  • L3 的存取速度:39 個CPU時鐘周期
  • RAM內(nèi)存的存取速度 :107 個CPU時鐘周期
我們可以看到,L1 的速度是 RAM 的 27 倍,但是 L1/L2 的大小基本上也就是 KB 級別的,L3 會是 MB 級別的。例如:Intel Core i7-8700K ,是一個 6 核的 CPU,每核上的 L1 是 64KB(數(shù)據(jù)和指令各 32KB),L2 是 256K,L3 有 2MB(我的蘋果電腦是 Intel Core i9-8950HK,和Core i7-8700K 的Cache大小一樣)。


我們的數(shù)據(jù)就從內(nèi)存向上,先到 L3,再到 L2,再到 L1,最后到寄存器進行 CPU 計算。為什么會設(shè)計成三層?這里有下面幾個方面的考慮:


  • 一個方面是物理速度,如果要更大的容量就需要更多的晶體管,除了芯片的體積會變大,更重要的是大量的晶體管會導(dǎo)致速度下降,因為訪問速度和要訪問的晶體管所在的位置成反比,也就是當(dāng)信號路徑變長時,通信速度會變慢。這部分是物理問題。
  • 另外一個問題是,多核技術(shù)中,數(shù)據(jù)的狀態(tài)需要在多個CPU中進行同步,并且,我們可以看到,cache 和RAM 的速度差距太大,所以,多級不同尺寸的緩存有利于提高整體的性能。
這個世界永遠是平衡的,一面變得有多光鮮,另一面也會變得有多黑暗。建立這么多級的緩存,一定就會引入其它的問題,這里有兩個比較重要的問題,


  • 一個是比較簡單的緩存的命中率的問題。
  • 另一個是比較復(fù)雜的緩存更新的一致性問題。
尤其是第二個問題,在多核技術(shù)下,這就很像分布式的系統(tǒng)了,要對多個地方進行更新。


緩存的命中

在說明這兩個問題之前。我們需要要解一個術(shù)語 Cache Line。緩存基本上來說就是把后面的數(shù)據(jù)加載到離自己近的地方,對于 CPU 來說,它是不會一個字節(jié)一個字節(jié)的加載的,因為這非常沒有效率,一般來說都是要一塊一塊的加載的,對于這樣的一塊一塊的數(shù)據(jù)單位,術(shù)語叫 Cache Line,


一般來說,一個主流的 CPU 的 Cache Line 是 64 Bytes(也有的CPU用32Bytes和128Bytes),64 Bytes也就是 16 個 32 位的整型,這就是 CPU 從內(nèi)存中撈數(shù)據(jù)上來的最小數(shù)據(jù)單位。


比如:Cache Line是最小單位(64Bytes),所以先把 Cache 分布多個 Cache Line,比如:L1 有 32KB,那么,32KB/64B = 512 個 Cache Line。


一方面,緩存需要把內(nèi)存里的數(shù)據(jù)放到放進來,英文叫 CPU Associativity。Cache 的數(shù)據(jù)放置的策略決定了內(nèi)存中的數(shù)據(jù)塊會拷貝到 CPU Cache 中的哪個位置上,因為 Cache 的大小遠遠小于內(nèi)存,所以,需要有一種地址關(guān)聯(lián)的算法,能夠讓內(nèi)存中的數(shù)據(jù)可以被映射到 Cache 中來。這個有點像內(nèi)存地址從邏輯地址向物理地址映射的方法,但不完全一樣。


基本上來說,我們會有如下的一些方法。


  • 一種方法是,任何一個內(nèi)存地址的數(shù)據(jù)可以被緩存在任何一個 Cache Line 里,這種方法是最靈活的,但是,如果我們要知道一個內(nèi)存是否存在于 Cache 中,我們就需要進行 O(n) 復(fù)雜度的 Cache 遍歷,這是很沒有效率的。
  • 另一種方法,為了降低緩存搜索算法,我們需要使用像Hash Table這樣的數(shù)據(jù)結(jié)構(gòu),最簡單的hash table就是做求模運算,比如:我們的 L1 Cache 有 512 個 Cache Line,那么,公式:(內(nèi)存地址 mod 512)* 64就可以直接找到所在的Cache地址的偏移了。但是,這樣的方式需要我們的程序?qū)?nèi)存地址的訪問要非常地平均,不然沖突就會非常嚴重。這成了一種非常理想的情況了。
  • 為了避免上述的兩種方案的問題,于是就要容忍一定的hash沖突,也就出現(xiàn)了 N-Way 關(guān)聯(lián)。也就是把連續(xù)的N 個 Cache Line 綁成一組,然后,先把找到相關(guān)的組,然后再在這個組內(nèi)找到相關(guān)的 Cache Line。這叫 Set Associativity。如下圖所示。
對于 N-Way 組關(guān)聯(lián),可能有點不好理解,這里個例子,并多說一些細節(jié)(不然后面的代碼你會不能理解),Intel 大多數(shù)處理器的 L1 Cache 都是 32KB,8-Way 組相聯(lián),Cache Line 是 64 Bytes。這意味著,


  • 32KB的可以分成,32KB / 64 = 512 條 Cache Line。
  • 因為有8 Way,于是會每一Way 有 512 / 8 = 64 條 Cache Line。
  • 于是每一路就有 64 x 64 = 4096 Byts 的內(nèi)存。
為了方便索引內(nèi)存地址,


  • Tag:每條 Cache Line 前都會有一個獨立分配的 24 bits來存的 tag,其就是內(nèi)存地址的前24bits
  • Index:內(nèi)存地址后續(xù)的 6 個 bits 則是在這一 Way 的是Cache Line 索引,2^6 = 64 剛好可以索引64條Cache Line
  • Offset:再往后的 6bits 用于表示在 Cache Line 里的偏移量
如下圖所示:(圖片來自《Cache: a place for concealment and safekeeping》)


當(dāng)拿到一個內(nèi)存地址的時候,先拿出中間的 6bits 來,找到是哪組。


然后,在這一個 8 組的 cache line 中,再進行 O(n) n=8 的遍歷,主是要匹配前 24bits 的 tag。如果匹配中了,就算命中,如果沒有匹配到,那就是 cache miss,如果是讀操作,就需要進向后面的緩存進行訪問了。


L2/L3 同樣是這樣的算法。而淘汰算法有兩種,一種是隨機一種是 LRU。現(xiàn)在一般都是以 LRU 的算法(通過增加一個訪問計數(shù)器來實現(xiàn))


這也意味著:


  • L1 Cache 可映射 36bits 的內(nèi)存地址,一共 2^36 = 64GB 的內(nèi)存
  • 當(dāng) CPU 要訪問一個內(nèi)存的時候,通過這個內(nèi)存中間的 6bits 定位是哪個 set,通過前 24bits 定位相應(yīng)的Cache Line。
  • 就像一個 hash Table 的數(shù)據(jù)結(jié)構(gòu)一樣,先是 O(1)的索引,然后進入沖突搜索。
  • 因為中間的 6bits 決定了一個同一個 set,所以,對于一段連續(xù)的內(nèi)存來說,每隔 4096 的內(nèi)存會被放在同一個組內(nèi),導(dǎo)致緩存沖突。
此外,當(dāng)有數(shù)據(jù)沒有命中緩存的時候,CPU 就會以最小為 Cache Line 的單元向內(nèi)存更新數(shù)據(jù)。當(dāng)然,CPU 并不一定只是更新 64Bytes,因為訪問主存實在是太慢了,所以,一般都會多更新一些。好的 CPU 會有一些預(yù)測的技術(shù),如果找到一種 pattern 的話,就會預(yù)先加載更多的內(nèi)存,包括指令也可以預(yù)加載。


這叫 Prefetching 技術(shù) (參看,Wikipedia 的 Cache Prefetching 和 紐約州立大學(xué)的 Memory Prefetching)。比如,你在for-loop訪問一個連續(xù)的數(shù)組,你的步長是一個固定的數(shù),內(nèi)存就可以做到prefetching。(注:指令也是以預(yù)加載的方式執(zhí)行)


了解這些細節(jié),會有利于我們知道在什么情況下有可以導(dǎo)致緩存的失效。


緩存的一致性

對于主流的 CPU 來說,緩存的寫操作基本上是兩種策略,


  • 一種是 Write Back,寫操作只要在 cache 上,然后再 flush 到內(nèi)存上。
  • 一種是 Write Through,寫操作同時寫到 cache 和內(nèi)存上。
為了提高寫的性能,一般來說,主流的 CPU(如:Intel Core i7/i9)采用的是 Write Back 的策略,因為直接寫內(nèi)存實在是太慢了。


好了,現(xiàn)在問題來了,如果有一個數(shù)據(jù) x 在 CPU 第 0 核的緩存上被更新了,那么其它 CPU 核上對于這個數(shù)據(jù) x 的值也要被更新,這就是緩存一致性的問題。(當(dāng)然,對于我們上層的程序我們不用關(guān)心 CPU 多個核的緩存是怎么同步的,這對上層的代碼來說都是透明的)


一般來說,在 CPU 硬件上,會有兩種方法來解決這個問題。


  • Directory 協(xié)議。這種方法的典型實現(xiàn)是要設(shè)計一個集中式控制器,它是主存儲器控制器的一部分。其中有一個目錄存儲在主存儲器中,其中包含有關(guān)各種本地緩存內(nèi)容的全局狀態(tài)信息。當(dāng)單個 CPU Cache 發(fā)出讀寫請求時,這個集中式控制器會檢查并發(fā)出必要的命令,以在主存和 CPU Cache之間或在 CPU Cache自身之間進行數(shù)據(jù)同步和傳輸。
  • Snoopy 協(xié)議。這種協(xié)議更像是一種數(shù)據(jù)通知的總線型的技術(shù)。CPU Cache 通過這個協(xié)議可以識別其它Cache上的數(shù)據(jù)狀態(tài)。如果有數(shù)據(jù)共享的話,可以通過廣播機制將共享數(shù)據(jù)的狀態(tài)通知給其它 CPU Cache。這個協(xié)議要求每個 CPU Cache 都可以窺探數(shù)據(jù)事件的通知并做出相應(yīng)的反應(yīng)。如下圖所示,有一個 Snoopy Bus 的總線。


因為 Directory 協(xié)議是一個中心式的,會有性能瓶頸,而且會增加整體設(shè)計的復(fù)雜度。而 Snoopy 協(xié)議更像是微服務(wù) 消息通訊,所以,現(xiàn)在基本都是使用 Snoopy 的總線的設(shè)計。


這里,我想多寫一些細節(jié),因為這種微觀的東西,讓人不自然地就會跟分布式系統(tǒng)關(guān)聯(lián)起來,在分布式系統(tǒng)中我們一般用 Paxos/Raft 這樣的分布式一致性的算法。


而在 CPU 的微觀世界里,則不必使用這樣的算法,原因是因為 CPU 的多個核的硬件不必考慮網(wǎng)絡(luò)會斷會延遲的問題。所以,CPU 的多核心緩存間的同步的核心就是要管理好數(shù)據(jù)的狀態(tài)就好了。


這里介紹幾個狀態(tài)協(xié)議,先從最簡單的開始,MESI 協(xié)議,這個協(xié)議跟那個著名的足球運動員梅西沒什么關(guān)系,其主要表示緩存數(shù)據(jù)有四個狀態(tài):Modified(已修改), Exclusive(獨占的),Shared(共享的),Invalid(無效的)。


這些狀態(tài)的狀態(tài)機如下所示(有點復(fù)雜,你可以先不看,這個圖就是想告訴你狀態(tài)控制有多復(fù)雜):


下面是個示例(如果你想看一下動畫演示的話,這里有一個網(wǎng)頁(MESI Interactive Animations),你可以進行交互操作,這個動畫演示中使用的 Write Through 算法):


當(dāng)前操作 CPU0 CPU1 Memory 說明
1) CPU0 read(x) x=1 (E)
x=1 只有一個CPU有 x 變量, 所以,狀態(tài)是 Exclusive
2) CPU1 read(x) x=1 (S) x=1(S) x=1 有兩個CPU都讀取 x 變量, 所以狀態(tài)變成 Shared
3) CPU0 write(x,9) x=9 (M) x=1(I) x=1 變量改變,在CPU0中狀態(tài) 變成 Modified,在CPU1中 狀態(tài)變成 Invalid
4) 變量 x 寫回內(nèi)存 x=9 (M) X=1(I) x=9 目前的狀態(tài)不變
5) CPU1 read(x) x=9 (S) x=9(S) x=9 變量同步到所有的Cache中, 狀態(tài)回到Shared
MESI 這種協(xié)議在數(shù)據(jù)更新后,會標記其它共享的 CPU 緩存的數(shù)據(jù)拷貝為 Invalid 狀態(tài),然后當(dāng)其它 CPU 再次read 的時候,就會出現(xiàn) cache miss 的問題,此時再從內(nèi)存中更新數(shù)據(jù)。從內(nèi)存中更新數(shù)據(jù)意味著 20 倍速度的降低。


我們能不能直接從我隔壁的 CPU 緩存中更新?是的,這就可以增加很多速度了,但是狀態(tài)控制也就變麻煩了。還需要多來一個狀態(tài):Owner(宿主),用于標記,我是更新數(shù)據(jù)的源。于是,出現(xiàn)了 MOESI 協(xié)議


MOESI 協(xié)議的狀態(tài)機和演示示例我就不貼了(有興趣可以上Berkeley上看看相關(guān)的課件),我們只需要理解MOESI協(xié)議允許 CPU Cache 間同步數(shù)據(jù),于是也降低了對內(nèi)存的操作,性能是非常大的提升,但是控制邏輯也非常復(fù)雜。


順便說一下,與 MOESI 協(xié)議類似的一個協(xié)議是 MESIF,其中的 F 是 Forward,同樣是把更新過的數(shù)據(jù)轉(zhuǎn)發(fā)給別的 CPU Cache 但是,MOESI 中的 Owner 狀態(tài) 和MESIF 中的 Forward 狀態(tài)有一個非常大的不一樣—— Owner 狀態(tài)下的數(shù)據(jù)是 dirty 的,還沒有寫回內(nèi)存,F(xiàn)orward 狀態(tài)下的數(shù)據(jù)是 clean的,可以丟棄而不用另行通知。


需要說明的是,AMD 用 MOESI,Intel 用 MESIF。所以,F(xiàn) 狀態(tài)主要是針對 CPU L3 Cache 設(shè)計的(前面我們說過,L3 是所有 CPU 核心共享的)。(相關(guān)的比較可以參看StackOverlow上這個問題的答案)


程序性能

了解了我們上面的這些東西后,我們來看一下對于程序的影響。


示例一

首先,假設(shè)我們有一個64M長的數(shù)組,設(shè)想一下下面的兩個循環(huán):


const int LEN = 64*1024*1024;
int *arr = new int[LEN];

for (int i = 0; i < LEN; i = 2) arr[i] *= i;

for (int i = 0; i < LEN; i = 8) arr[i] *= i;
按我們的想法來看,第二個循環(huán)要比第一個循環(huán)少4倍的計算量,其應(yīng)該也是要快4倍的。但實際跑下來并不是,在我的機器上,第一個循環(huán)需要 127 毫秒,第二個循環(huán)則需要 121 毫秒,相差無幾。


這里最主要的原因就是 Cache Line,因為 CPU 會以一個 Cache Line 64Bytes 最小時單位加載,也就是 16 個 32bits 的整型,所以,無論你步長是 2 還是 8,都差不多。而后面的乘法其實是不耗 CPU 時間的。


示例二

我們再來看一個與緩存命中率有關(guān)的代碼,我們以一定的步長increment來訪問一個連續(xù)的數(shù)組。


for (int i = 0; i < 10000000; i ) {
for (int j = 0; j < size; j = increment) {
memory[j]  = j;
}
}
我們測試一下,在下表中, 表頭是步長,也就是每次跳多少個整數(shù),而縱向是這個數(shù)組可以跳幾次(你可以理解為要幾條 Cache Line),于是表中的任何一項代表了這個數(shù)組有多少,而且步長是多少。


比如:橫軸是 512,縱軸是4,意思是,這個數(shù)組有 4*512 = 2048個長度,訪問時按512步長訪問,也就是訪問其中的這幾項:[0, 512, 1024, 1536]這四項。


表中同的項是,是循環(huán) 1000 萬次的時間,單位是“微秒”(除以1000后是毫秒)


| count |   1    |   16  |  512  | 1024  |
------------------------------------------
|     1 |  17539 | 16726 | 15143 | 14477 |
|     2 |  15420 | 14648 | 13552 | 13343 |
|     3 |  14716 | 14463 | 15086 | 17509 |
|     4 |  18976 | 18829 | 18961 | 21645 |
|     5 |  23693 | 23436 | 74349 | 29796 |
|     6 |  23264 | 23707 | 27005 | 44103 |
|     7 |  28574 | 28979 | 33169 | 58759 |
|     8 |  33155 | 34405 | 39339 | 65182 |
|     9 |  37088 | 37788 | 49863 |156745 |
|    10 |  41543 | 42103 | 58533 |215278 |
|    11 |  47638 | 50329 | 66620 |335603 |
|    12 |  49759 | 51228 | 75087 |305075 |
|    13 |  53938 | 53924 | 77790 |366879 |
|    14 |  58422 | 59565 | 90501 |466368 |
|    15 |  62161 | 64129 | 90814 |525780 |
|    16 |  67061 | 66663 | 98734 |440558 |
|    17 |  71132 | 69753 |171203 |506631 |
|    18 |  74102 | 73130 |293947 |550920 |
我們可以看到,從 [9,1024]以后,時間顯著上升。包括 [17,512]和 [18,512]也顯著上升。這是因為,我機器的 L1 Cache 是 32KB, 8 Way 的,前面說過,8 Way 的有 64 組,每組 8 個 Cache Line,當(dāng) for-loop步長超過 1024 個整型,也就是正好 4096 Bytes 時,也就是導(dǎo)致內(nèi)存地址的變化是變化在高位的 24bits 上,


而低位的1 2bits 變化不大,尤其是中間6bits沒有變化,導(dǎo)致全部命中同一組 set,導(dǎo)致大量的 cache 沖突,導(dǎo)致性能下降,時間上升。而 [16, 512]也是一樣的,其中的幾步開始導(dǎo)致L1 Cache開始沖突失效。


示例三

接下來,我們再來看個示例。下面是一個二維數(shù)組的兩種遍歷方式,一個逐行遍歷,一個是逐列遍歷,這兩種方式在理論上來說,尋址和計算量都是一樣的,執(zhí)行時間應(yīng)該也是一樣的。


const int row = 1024;
const int col = 512
int matrix[row][col];

//逐行遍歷
int sum_row=0;
for(int _r=0; _r for(int _c=0; _c sum_row  = matrix[_r][_c];
}
}

//逐列遍歷
int sum_col=0;
for(int _c=0; _c for(int _r=0; _r sum_col  = matrix[_r][_c];
}
}
然而,并不是,在我的機器上,得到下面的結(jié)果。


  • 逐行遍歷:0.081ms
  • 逐列遍歷:1.069ms
執(zhí)行時間有十幾倍的差距。其中的原因,就是逐列遍歷對于 CPU Cache 的運作方式并不友好,所以,付出巨大的代價。


示例四

接下來,我們來看一下多核下的性能問題,參看如下的代碼。兩個線程在操作一個數(shù)組的兩個不同的元素(無需加鎖),線程循環(huán)1000萬次,做加法操作。在下面的代碼中,我高亮了一行,就是p2指針,要么是p[1],或是 p[30],理論上來說,無論訪問哪兩個數(shù)組元素,都應(yīng)該是一樣的執(zhí)行時間。


void fn (int* data) {
for(int i = 0; i < 10*1024*1024; i)
*data  = rand();
}

int p[32];

int *p1 = 
本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

在這篇文章中,小編將對CPU中央處理器的相關(guān)內(nèi)容和情況加以介紹以幫助大家增進對CPU中央處理器的了解程度,和小編一起來閱讀以下內(nèi)容吧。

關(guān)鍵字: CPU 中央處理器 晶圓

在桌面級處理器上,AMD多年來一直在多核上有優(yōu)勢,不過12代酷睿開始,Intel通過P、E核異構(gòu)實現(xiàn)了反超,13代酷睿做到了24核32線程,核心數(shù)已經(jīng)超過了銳龍7000的最大16核。在服務(wù)器處理器上,AMD優(yōu)勢更大,64...

關(guān)鍵字: AMD CPU Intel EUV

華為麒麟芯片(HUAWEI Kirin)是華為技術(shù)有限公司于2019年9月6日在德國柏林和北京同時發(fā)布的一款新一代旗艦芯片。華為麒麟在3G芯片大戰(zhàn)中,扮演了“黑馬”的角色。

關(guān)鍵字: 麒麟 CPU 華為Mate 50

據(jù)業(yè)內(nèi)信息,近日ADM的一份內(nèi)部報告顯示,ADM正在計劃降低其銳龍 7000 CPU的生產(chǎn)計劃?,F(xiàn)階段全球市場PC的低迷和銷量下滑,再加上AM5平臺整體反響不佳等等一系列原因?qū)е翧DM采取這一行動計劃。

關(guān)鍵字: PC ADM 銳龍 7000 CPU

北京2022年10月17日 /美通社/ --  "天下武功、唯快不破",數(shù)字經(jīng)濟時代尤甚。 數(shù)據(jù)極富價值,堪比新時代的石油。數(shù)字經(jīng)濟時代,數(shù)據(jù)價值如何快速、高效地釋放顯得尤為重要。自20...

關(guān)鍵字: 軟件 IO SSD CPU

Linux內(nèi)核是從V2.6開始引入設(shè)備樹的概念,其起源于OF:OpenFirmware, 用于描述一個硬件平臺的硬件資源信息,這些信息包括:CPU的數(shù)量和類別、內(nèi)存基地址和大小、總線和橋、外設(shè)連接、中斷控制器和中斷使用情...

關(guān)鍵字: Linux內(nèi)核 硬件 CPU

(微控制單元 MCU(Microcontroller Unit),又稱單片機,是把中央處理器(CentralProcess Unit; CPU)的頻率與規(guī)格做適當(dāng)縮減,并將內(nèi)存(memory)、計數(shù)器(Timer)、US...

關(guān)鍵字: 單片機 芯片 CPU

日前,瀾起科技宣布其第三代津逮?CPU系列產(chǎn)品通過了VMware公司的產(chǎn)品兼容性認證,達到VMware ESXi 7.0 U3虛擬化平臺的通用兼容性及性能、可靠性要求,滿足用戶的關(guān)鍵應(yīng)用需要。

關(guān)鍵字: 瀾起科技 CPU VMware兼容性

CPU內(nèi)部自帶的定時器模塊,通過初始化、配置可以實現(xiàn)定時,定時時間到以后就會執(zhí)行相應(yīng)的定時器中斷處理函數(shù)。硬件定時器一般都帶有其它功能,比如PWM輸出、輸入捕獲等等功能。但是缺點是硬件定時器數(shù)量少??!

關(guān)鍵字: 軟件定時器 硬件定時器 CPU

為了提高代碼密度,處理器選擇支持16位的壓縮指令集,因此程序會出現(xiàn)32bit和16bit同時出現(xiàn)的場景,32bit指令可能存在與32位地址邊界不對齊的情況,E203采用剩余緩存技術(shù)(Leftover Buffer)。IT...

關(guān)鍵字: E203 CPU SMIC的64bit SRAM

CPP開發(fā)者

234 篇文章

關(guān)注

發(fā)布文章

編輯精選

技術(shù)子站

關(guān)閉