天天操夜夜爱_天天插天天操天天干_黄色av地址_成人黄色在线观看_国内久久久久久_欧美国产日韩一区二区三区_国产精品久久久久久久久晋中

電腦運(yùn)行內(nèi)存可以加換

發(fā)布時(shí)間: 2023-04-15 20:58 閱讀: 文章來(lái)源:轉(zhuǎn)載

如果本文對(duì)你有幫助,歡迎關(guān)注、點(diǎn)贊、收藏、轉(zhuǎn)發(fā)給朋友,讓我有持續(xù)創(chuàng)作的動(dòng)力

CPU性能

響應(yīng)時(shí)間:指的就是,我們執(zhí)行一個(gè)程序,到底需要花多少時(shí)間。花的時(shí)間越少,自然性能就越好。吞吐率:在一定的時(shí)間范圍內(nèi),到底能處理多少事情。這里的“事情”,在計(jì)算機(jī)里就是處理的數(shù)據(jù)或者執(zhí)行的程序指令。

我們一般把性能,定義成響應(yīng)時(shí)間的倒數(shù),也就是: 性能 = 1/響應(yīng)時(shí)間

程序運(yùn)行的時(shí)間

程序運(yùn)行的時(shí)間=程序運(yùn)行結(jié)束的時(shí)間-程序開(kāi)始運(yùn)行的時(shí)間

但是,計(jì)算機(jī)可能同時(shí)運(yùn)行著好多個(gè)程序,CPU實(shí)際上不停地在各個(gè)程序之間進(jìn)行切換。在這些走掉的時(shí)間里面,很可能CPU切換去運(yùn)行別的程序了。所以這個(gè)時(shí)間并不準(zhǔn)。

我們使用time命令統(tǒng)計(jì)運(yùn)行時(shí)間:

$ time seq 1000000 | wc -l1000000real0m0.101suser0m0.031ssys0m0.016s

其中real就是Wall Clock Time,而程序?qū)嶋H花費(fèi)的CPU執(zhí)行時(shí)間,就是user time加上sys time。

我們下面對(duì)程序的CPU執(zhí)行時(shí)間進(jìn)行拆解:

程序的CPU執(zhí)行時(shí)間=CPU時(shí)鐘周期數(shù)×?xí)r鐘周期時(shí)間

時(shí)鐘周期時(shí)間:如果一臺(tái)電腦的主頻是2.8GHz,那么可以簡(jiǎn)單認(rèn)為,CPU在1秒時(shí)間內(nèi),可以執(zhí)行的簡(jiǎn)單指令的數(shù)量是2.8G條。在這個(gè)2.8GHz的CPU上,這個(gè)時(shí)鐘周期時(shí)間,就是1/2.8G。

對(duì)于上面的公式:CPU時(shí)鐘周期數(shù)還可以拆解成指令數(shù)×每條指令的平均時(shí)鐘周期數(shù)Cycles Per Instruction,簡(jiǎn)稱(chēng)CPI)。

程序的CPU執(zhí)行時(shí)間=指令數(shù)×CPI×Clock Cycle Time

并行優(yōu)化

由于通過(guò)提升CPU頻率已經(jīng)達(dá)到瓶頸,所以開(kāi)始推出多核CPU,通過(guò)提升“吞吐率”而不是“響應(yīng)時(shí)間”,來(lái)達(dá)到目的。

但是,并不是所有問(wèn)題,都可以通過(guò)并行提高性能來(lái)解決。如果想要使用這種思想,需要滿(mǎn)足這樣幾個(gè)條件。

  1. 需要進(jìn)行的計(jì)算,本身可以分解成幾個(gè)可以并行的任務(wù)。
  2. 需要能夠分解好問(wèn)題,并確保幾個(gè)人的結(jié)果能夠匯總到一起。
  3. 在“匯總”這個(gè)階段,是沒(méi)有辦法并行進(jìn)行的,還是得順序執(zhí)行,一步一步來(lái)。

所以并行計(jì)算涉及到了一個(gè)阿姆達(dá)爾定律(Amdahl’s Law)。

對(duì)于一個(gè)程序進(jìn)行優(yōu)化之后,處理器并行運(yùn)算之后效率提升的情況。具體可以用這樣一個(gè)公式來(lái)表示:

優(yōu)化后的執(zhí)行時(shí)間 = 受優(yōu)化影響的執(zhí)行時(shí)間/加速倍數(shù)+不受影響的執(zhí)行時(shí)間

比如做一段數(shù)據(jù)的計(jì)算, 本來(lái)如果整個(gè)計(jì)算單核完成需要120ns,但是我們可以將這個(gè)任務(wù)拆分成4個(gè),最后再匯總加起來(lái)。如果每個(gè)任務(wù)單獨(dú)計(jì)算需要25ns,加起來(lái)匯總需要20ns,那么4個(gè)任務(wù)并行計(jì)算需要100/4+20=25ns。

即使我們?cè)黾痈嗟牟⑿卸葋?lái)提供加速倍數(shù),比如有100個(gè)CPU,整個(gè)時(shí)間也需要100/100+20=21ns。

從編譯到匯編,代碼怎么變成機(jī)器碼?

如下C語(yǔ)言程序例子:

// test.cint main(){int a = 1; int b = 2;a = a + b;}

我們給兩個(gè)變量 a、b分別賦值1、2,然后再將a、b兩個(gè)變量中的值加在一起,重新賦值給了a整個(gè)變量。

要讓這段程序在一個(gè)Linux操作系統(tǒng)上跑起來(lái),我們需要把整個(gè)程序翻譯成一個(gè)匯編語(yǔ)言(ASM,Assembly Language)的程序,這個(gè)過(guò)程我們一般叫編譯(Compile)成匯編代碼。

針對(duì)匯編代碼,我們可以再用匯編器(Assembler)翻譯成機(jī)器碼(Machine Code)。這些機(jī)器碼由“0”和“1”組成的機(jī)器語(yǔ)言表示。這一條條機(jī)器碼,就是一條條的計(jì)算機(jī)指令。這樣一串串的16進(jìn)制數(shù)字,就是我們CPU能夠真正認(rèn)識(shí)的計(jì)算機(jī)指令。

匯編代碼其實(shí)就是“給程序員看的機(jī)器碼”,也正因?yàn)檫@樣,機(jī)器碼和匯編代碼是一一對(duì)應(yīng)的。我們?nèi)祟?lèi)很容易記住add、mov這些用英文表示的指令,而8b 45 f8這樣的指令,由于很難一下子看明白是在干什么,所以會(huì)非常難以記憶。所以我們需要匯編代碼。

程序指令

指令是如何被執(zhí)行的

一個(gè)CPU里面會(huì)有很多種不同功能的寄存器。我這里給你介紹三種比較特殊的。

一個(gè)是PC寄存器(Program Counter Register),也叫指令地址寄存器(Instruction Address Register)。它就是用來(lái)存放下一條需要執(zhí)行的計(jì)算機(jī)指令的內(nèi)存地址。

第二個(gè)是指令寄存器(Instruction Register),用來(lái)存放當(dāng)前正在執(zhí)行的指令。

第三個(gè)是條件碼寄存器(Status Register),用里面的一個(gè)一個(gè)標(biāo)記位(Flag),存放CPU進(jìn)行算術(shù)或者邏輯計(jì)算的結(jié)果。

實(shí)際上,一個(gè)程序執(zhí)行的時(shí)候,CPU會(huì)根據(jù)PC寄存器里的地址,從內(nèi)存里面把需要執(zhí)行的指令讀取到指令寄存器里面執(zhí)行,然后根據(jù)指令長(zhǎng)度自增,開(kāi)始順序讀取下一條指令。可以看到,一個(gè)程序的一條條指令,在內(nèi)存里面是連續(xù)保存的,也會(huì)一條條順序加載。

程序的執(zhí)行和跳轉(zhuǎn)

現(xiàn)在就來(lái)看一個(gè)包含if…else的簡(jiǎn)單程序。

// test.c#include #include int main(){srand(time(NULL));int r = rand() % 2;int a = 10;if (r == 0){a = 1;} else {a = 2;} 

把這個(gè)程序編譯成匯編代碼。

if (r == 0)3b:83 7d fc 00 cmpDWORD PTR [rbp-0x4],0x03f:75 09jne4a {a = 1;41:c7 45 f8 01 00 00 00movDWORD PTR [rbp-0x8],0x148:eb 07jmp51 }else{a = 2;4a:c7 45 f8 02 00 00 00movDWORD PTR [rbp-0x8],0x251:b8 00 00 00 00moveax,0x0}

可以看到,這里對(duì)于r == 0的條件判斷,被編譯成了cmp和jne這兩條指令。對(duì)于:

cmpDWORD PTR [rbp-0x4],0x0

cmp指令比較了前后兩個(gè)操作數(shù)的值,這里的DWORD PTR代表操作的數(shù)據(jù)類(lèi)型是32位的整數(shù),而[rbp-0x4]則是一個(gè)寄存器的地址。所以,第一個(gè)操作數(shù)就是從寄存器里拿到的變量r的值。第二個(gè)操作數(shù)0x0就是我們?cè)O(shè)定的常量0的16進(jìn)制表示。cmp指令的比較結(jié)果,會(huì)存入到條件碼寄存器當(dāng)中去。

在這里,如果比較的結(jié)果是False,也就是0,就把零標(biāo)志條件碼(對(duì)應(yīng)的條件碼是ZF,Zero Flag)設(shè)置為1。

cmp指令執(zhí)行完成之后,PC寄存器會(huì)自動(dòng)自增,開(kāi)始執(zhí)行下一條jne的指令。

對(duì)于:

jne4a 

jne指令,是jump if not equal的意思,它會(huì)查看對(duì)應(yīng)的零標(biāo)志位。如果為0,會(huì)跳轉(zhuǎn)到后面跟著的操作數(shù)4a的位置。這個(gè)4a,對(duì)應(yīng)這里匯編代碼的行號(hào),也就是上面設(shè)置的else條件里的第一條指令。

當(dāng)跳轉(zhuǎn)發(fā)生的時(shí)候,PC寄存器就不再是自增變成下一條指令的地址,而是被直接設(shè)置成這里的4a這個(gè)地址。這個(gè)時(shí)候,CPU再把4a地址里的指令加載到指令寄存器中來(lái)執(zhí)行。

4a:c7 45 f8 02 00 00 00movDWORD PTR [rbp-0x8],0x251:b8 00 00 00 00moveax,0x0

4a的指令,實(shí)際是一條mov指令,第一個(gè)操作數(shù)和前面的cmp指令一樣,是另一個(gè)32位整型的寄存器地址,以及對(duì)應(yīng)的2的16進(jìn)制值0x2。mov指令把2設(shè)置到對(duì)應(yīng)的寄存器里去,相當(dāng)于一個(gè)賦值操作。然后,PC寄存器里的值繼續(xù)自增,執(zhí)行下一條mov指令。

下一條指令也是mov,第一個(gè)操作數(shù)eax,代表累加寄存器,第二個(gè)操作數(shù)0x0則是16進(jìn)制的0的表示。這條指令其實(shí)沒(méi)有實(shí)際的作用,它的作用是一個(gè)占位符。

函數(shù)調(diào)用

我們先來(lái)看個(gè)例子:

// function_example.c#include int static add(int a, int b){return a+b;}int main(){int x = 5;int y = 10;int u = add(x, y);}

我們把這個(gè)程序編譯之后:

int static add(int a, int b){0:55pushrbp1:48 89 e5movrbp,rsp4:89 7d fcmovDWORD PTR [rbp-0x4],edi7:89 75 f8movDWORD PTR [rbp-0x8],esireturn a+b;a:8b 55 fcmovedx,DWORD PTR [rbp-0x4]d:8b 45 f8moveax,DWORD PTR [rbp-0x8]10:01 d0addeax,edx}12:5dpoprbp13:c3ret0000000000000014 
:int main(){14:55pushrbp15:48 89 e5movrbp,rsp18:48 83 ec 10 subrsp,0x10int x = 5;1c:c7 45 fc 05 00 00 00movDWORD PTR [rbp-0x4],0x5int y = 10;23:c7 45 f8 0a 00 00 00movDWORD PTR [rbp-0x8],0xaint u = add(x, y);2a:8b 55 f8movedx,DWORD PTR [rbp-0x8]2d:8b 45 fcmoveax,DWORD PTR [rbp-0x4]30:89 d6movesi,edx32:89 c7movedi,eax34:e8 c7 ff ff ffcall0 39:89 45 f4movDWORD PTR [rbp-0xc],eax3c:b8 00 00 00 00moveax,0x0}41:c9leave42:c3ret

在add函數(shù)編譯之后,代碼先執(zhí)行了一條push指令和一條mov指令;在函數(shù)執(zhí)行結(jié)束的時(shí)候,又執(zhí)行了一條pop和一條ret指令。

add函數(shù)的第0行,push rbp這個(gè)指令,就是在進(jìn)行壓棧。這里的rbp又叫棧幀指針(Frame Pointer),是一個(gè)存放了當(dāng)前棧幀位置的寄存器。push rbp就把之前調(diào)用函數(shù)的返回地址,壓到棧頂。

接著,第1行的一條命令mov rbp, rsp里,則是把rsp這個(gè)棧指針(Stack Pointer)的值復(fù)制到rbp里,而rsp始終會(huì)指向棧頂。這個(gè)命令意味著,rbp這個(gè)棧幀指針指向的返回地址,變成當(dāng)前最新的棧頂,也就是add函數(shù)的返回地址了。

而在函數(shù)add執(zhí)行完成之后,又會(huì)分別調(diào)用第12行的pop rbp來(lái)將當(dāng)前的棧頂出棧,然后調(diào)用第13行的ret指令,將程序的控制權(quán)返回到出棧后的棧頂,也就是main函數(shù)的返回地址。

拆解程序執(zhí)行

實(shí)際上,“C語(yǔ)言代碼-匯編代碼-機(jī)器碼” 這個(gè)過(guò)程,在我們的計(jì)算機(jī)上進(jìn)行的時(shí)候是由兩部分組成的。

第一個(gè)部分由編譯(Compile)、匯編(Assemble)以及鏈接(Link)三個(gè)階段組成。在這三個(gè)階段完成之后,我們就生成了一個(gè)可執(zhí)行文件。

第二部分,我們通過(guò)裝載器(Loader)把可執(zhí)行文件裝載(Load)到內(nèi)存中。CPU從內(nèi)存中讀取指令和數(shù)據(jù),來(lái)開(kāi)始真正執(zhí)行程序。

鏈接

靜態(tài)鏈接

程序的鏈接,是把對(duì)應(yīng)的不同文件內(nèi)的代碼段,合并到一起,成為最后的可執(zhí)行文件。

在可執(zhí)行文件里,我們可以看到,對(duì)應(yīng)的函數(shù)名稱(chēng),像add、main等等,乃至你自己定義的全局可以訪問(wèn)的變量名稱(chēng)對(duì)應(yīng)的地址,存儲(chǔ)在一個(gè)叫作符號(hào)表(Symbols Table)的位置里。符號(hào)表相當(dāng)于一個(gè)地址簿,把名字和地址關(guān)聯(lián)了起來(lái)。

經(jīng)過(guò)程序的鏈接之后,main函數(shù)里調(diào)用add的跳轉(zhuǎn)地址,不再是下一條指令的地址了,而是add函數(shù)的入口地址了。

鏈接器會(huì)掃描所有輸入的目標(biāo)文件,然后把所有符號(hào)表里的信息收集起來(lái),構(gòu)成一個(gè)全局的符號(hào)表。然后再根據(jù)重定位表,把所有不確定要跳轉(zhuǎn)地址的代碼,根據(jù)符號(hào)表里面存儲(chǔ)的地址,進(jìn)行一次修正。最后,把所有的目標(biāo)文件的對(duì)應(yīng)段進(jìn)行一次合并,變成了最終的可執(zhí)行代碼。

這個(gè)合并代碼段的方法,是叫靜態(tài)鏈接

動(dòng)態(tài)鏈接

在動(dòng)態(tài)鏈接的過(guò)程中,我們想要“鏈接”的,不是存儲(chǔ)在硬盤(pán)上的目標(biāo)文件代碼,而是加載到內(nèi)存中的共享庫(kù)(Shared Libraries)。

要想要在程序運(yùn)行的時(shí)候共享代碼,也有一定的要求,就是這些機(jī)器碼必須是“地址無(wú)關(guān)”的。換句話說(shuō)就是,這段代碼,無(wú)論加載在哪個(gè)內(nèi)存地址,都能夠正常執(zhí)行。

動(dòng)態(tài)代碼庫(kù)內(nèi)部的變量和函數(shù)調(diào)用都是使用相對(duì)地址。因?yàn)檎麄€(gè)共享庫(kù)是放在一段連續(xù)的虛擬內(nèi)存地址中的,無(wú)論裝載到哪一段地址,不同指令之間的相對(duì)地址都是不變的。

裝載程序

在運(yùn)行這些可執(zhí)行文件的時(shí)候,我們其實(shí)是通過(guò)一個(gè)裝載器,解析ELF或者PE格式的可執(zhí)行文件。裝載器會(huì)把對(duì)應(yīng)的指令和數(shù)據(jù)加載到內(nèi)存里面來(lái),讓CPU去執(zhí)行。

裝載器需要滿(mǎn)足兩個(gè)要求:

  1. 可執(zhí)行程序加載后占用的內(nèi)存空間應(yīng)該是連續(xù)的。因?yàn)镃PU在執(zhí)行指令的時(shí)候,程序計(jì)數(shù)器是順序地一條一條指令執(zhí)行下去。
  2. 我們需要同時(shí)加載很多個(gè)程序,并且不能讓程序自己規(guī)定在內(nèi)存中加載的位置。因?yàn)槲覀儸F(xiàn)在的計(jì)算機(jī)通常會(huì)同時(shí)運(yùn)行很多個(gè)程序,可能你想要的內(nèi)存地址已經(jīng)被其他加載了的程序占用了。

基于上面,我們需要在內(nèi)存空間地址和整個(gè)程序指令指定的內(nèi)存地址做一個(gè)映射。

把指令里用到的內(nèi)存地址叫作虛擬內(nèi)存地址(Virtual Memory Address),實(shí)際在內(nèi)存硬件里面的空間地址,我們叫物理內(nèi)存地址(Physical Memory Address)。

內(nèi)存分頁(yè)

分頁(yè)是把整個(gè)物理內(nèi)存空間切成一段段固定尺寸的大小。而對(duì)應(yīng)的程序所需要占用的虛擬內(nèi)存空間,也會(huì)同樣切成一段段固定尺寸的大小。這樣一個(gè)連續(xù)并且尺寸固定的內(nèi)存空間,我們叫頁(yè)(Page)。

從虛擬內(nèi)存到物理內(nèi)存的映射,不再是拿整段連續(xù)的內(nèi)存的物理地址,而是按照一個(gè)一個(gè)頁(yè)來(lái)的。

分頁(yè)之后避免了整個(gè)程序和硬盤(pán)進(jìn)行交換而產(chǎn)生性能瓶頸。即使內(nèi)存空間不夠,需要讓現(xiàn)有的、正在運(yùn)行的其他程序,通過(guò)內(nèi)存交換釋放出一些內(nèi)存的頁(yè)出來(lái),一次性寫(xiě)入磁盤(pán)的也只有少數(shù)的一個(gè)頁(yè)或者幾個(gè)頁(yè),不會(huì)花太多時(shí)間,讓整個(gè)機(jī)器被內(nèi)存交換的過(guò)程給卡住。

???展開(kāi)全文
相關(guān)文章
主站蜘蛛池模板: 久久久性视频 | 黄色毛片在线 | 逼中极品一线天 | 九九九九精品视频在线播放 | 久久精品a国产一级 | 99热这里只有精品国产99 | 免费美剧在线观看 | 免费视频不卡一区二区三区 | 一本久道热中字伊人 | 日本高清www色 | jizz18日本人在线播放 | 午夜资源网 | 国产网曝手机视频在线观看 | 欧美网站在线 | 日本免费性 | 国产美女一级视频 | 99久久国产亚洲综合精品 | 国产福利在线观看永久视频 | 国产一区日韩二区欧美三 | 老子影院午夜伦手机不卡无 | 欧美日韩一区二区综合在线视频 | 亚洲欧洲日产国码一级毛片 | 天天爱天天爽 | 免费一级a毛片在线播放 | 2022年国产精品久久久久 | 欧美专区一区 | 99在线观看视频免费精品9 | 秋霞一级黄色片 | 欧美97久久人人模人人爽人人喊 | 久久免费视频1 | 毛片毛片毛片毛片毛片 | 免费国产成高清人在线视频 | 日本韩经典三级在线播放 | 免费特级片| 麻豆免费版 | 人人玩人人添人人澡欧美 | 欧美日本视频在线观看 | 免费在线欧美 | 青青草视频在线免费观看 | 最近最新中文字幕免费的一页 | 亚洲欧美日韩不卡 |