原創(chuàng)|行業(yè)資訊|編輯:龔雪|2014-12-08 09:32:00.000|閱讀 343 次
概述:本文收集了為實(shí)現(xiàn)最佳性能而在結(jié)構(gòu)內(nèi)對(duì)齊數(shù)據(jù)的常識(shí)和一些最佳設(shè)計(jì)方案
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷售中 >>
每種數(shù)據(jù)類型都有一個(gè)與之相關(guān)的隊(duì)列,這個(gè)隊(duì)列是由處理器架構(gòu)而非這個(gè)語(yǔ)言本身授權(quán)的。校準(zhǔn)數(shù)據(jù)元素允許處理器以高效的方式從內(nèi)存中抓取數(shù)據(jù),并由此提高性能。為了提供最佳的性能,編譯器試圖保持這種數(shù)據(jù)元素的隊(duì)列。在32位和64位的Linux系統(tǒng)上,英特爾®C++編譯器上使用著的數(shù)據(jù)類型典型對(duì)齊要求如下:
Data Type |
32-bit(bytes) |
64-bit(bytes) |
char |
1 |
1 |
short |
2 |
2 |
int |
4 |
4 |
long |
8 |
8 |
float |
4 |
4 |
double |
8 |
8 |
long long |
8 |
8 |
long double |
4 |
16 |
Any pointer |
4 |
8 |
一般情況下,編譯器會(huì)在任何可能的時(shí)候都滿足這些數(shù)據(jù)元素的對(duì)齊要求。在使用英特爾®C++和Fortran編譯器的情況下,可以使用-align(C/C++,F(xiàn)ortran語(yǔ)言)編譯器開(kāi)關(guān)來(lái)強(qiáng)制或禁止自然對(duì)齊規(guī)則。對(duì)于通常含有不同類型的數(shù)據(jù)元素的結(jié)構(gòu),編譯器試圖通過(guò)在元素之間插入未使用的存儲(chǔ)來(lái)保持的數(shù)據(jù)元素實(shí)現(xiàn)正確對(duì)齊。這種技術(shù)被稱為“填充”。此外,編譯器還會(huì)以它的最嚴(yán)格的對(duì)準(zhǔn)成員為基準(zhǔn)來(lái)對(duì)齊整個(gè)結(jié)構(gòu)。編譯器也可能會(huì)增加結(jié)構(gòu)的空間大小,必要的時(shí)候,編譯器會(huì)通過(guò)在結(jié)構(gòu)端部添加填充的方式來(lái)使其實(shí)現(xiàn)成倍的對(duì)齊。這就是所謂的“尾填充”。如此一來(lái),填充就醫(yī)浪費(fèi)存儲(chǔ)空間的代價(jià)提升了性能。如果是英特爾®至強(qiáng)融核™協(xié)處理器,提供給應(yīng)用程序可用存儲(chǔ)的數(shù)量本身是有限的,這會(huì)帶來(lái)一個(gè)嚴(yán)重的問(wèn)題。
開(kāi)發(fā)者可以通過(guò)給結(jié)構(gòu)元素排序來(lái)最小化這種內(nèi)存浪費(fèi),這樣最大/最寬的元素會(huì)排在前面,接著是第二寬的,依次排開(kāi)。下面的這個(gè)例子能為你闡明用結(jié)構(gòu)的空間大小給數(shù)據(jù)元素排序影響:
結(jié)構(gòu)s1有11個(gè)填充字節(jié),如下表所示:
看看下面的結(jié)構(gòu)s2:
這個(gè)結(jié)構(gòu)只包含了3個(gè)尾填充的字節(jié),如下圖所示:
這樣就節(jié)省了內(nèi)存。因此,僅僅在結(jié)構(gòu)定義中重排數(shù)據(jù)元素就有可能避免內(nèi)存浪費(fèi)。
這種給元素排序的一種例外是,如果你的結(jié)構(gòu)比你的高速緩存線(在因特爾至強(qiáng)融核協(xié)處理器上是64個(gè)字節(jié))更大的話,一些循環(huán)或內(nèi)核就只能接觸到結(jié)構(gòu)的一部分。在這種情況下,保持結(jié)構(gòu)的各部分能在內(nèi)存中一起被接觸到可能是有益的,這可能會(huì)改善高速緩存局部性。
如果你的結(jié)構(gòu)比高速緩存線更大,并且一些循環(huán)和內(nèi)核只能接觸到結(jié)構(gòu)的一部分的話,你可以考慮下通過(guò)把大結(jié)構(gòu)分解為多個(gè)以單獨(dú)的排列存儲(chǔ)的更小的結(jié)構(gòu)。這就潛在地提升了可接觸數(shù)據(jù)的密度,incident提升了高速緩存的局部性。
你也可以使用_decipsec(align)屬性來(lái)指導(dǎo)編譯器比用其他方式更嚴(yán)格地對(duì)齊數(shù)據(jù),這個(gè)擴(kuò)展屬性的語(yǔ)法如下:
C/C++:
_decipsec(align(n))<數(shù)據(jù)類型聲明>
Fortran:
cDEC$ATRIBUTES ALIGN:n::<數(shù)據(jù)類型聲明>
這里的n是要求的隊(duì)列,是2的乘冪,在英特爾C++編譯器里最大為4096,在英特爾Fortran編譯器里最大是16384.你可以使用這個(gè)屬性為單個(gè)變量,靜態(tài)結(jié)構(gòu)或自動(dòng)存儲(chǔ)持續(xù)期間內(nèi)請(qǐng)求對(duì)齊。然而,這就表示,盡管你提高結(jié)構(gòu)的一致性,但這個(gè)屬性并不能調(diào)整結(jié)構(gòu)內(nèi)元素的對(duì)齊。通過(guò)把_declpsec(align)放在關(guān)鍵字struct前面,你就為僅僅這種類型的對(duì)象請(qǐng)求適當(dāng)?shù)膶?duì)齊。讓我用下面這個(gè)例子來(lái)說(shuō)明我的觀點(diǎn):
在上述示例中,對(duì)字符a2和整數(shù)b2的對(duì)齊仍然各自保持為1個(gè)字節(jié)和4個(gè)字節(jié),這是默認(rèn)的。然而,結(jié)構(gòu)s2的每個(gè)實(shí)例都被對(duì)齊到32個(gè)字節(jié)的邊界,正如_declspsec聲明里描述的那樣。因此,結(jié)構(gòu)s1內(nèi)部的結(jié)構(gòu)s2目前的每個(gè)實(shí)例都將對(duì)齊到32個(gè)字節(jié)的邊界。
我們還可以通過(guò)動(dòng)態(tài)分配結(jié)構(gòu)s2的排列來(lái)進(jìn)一步擴(kuò)展這個(gè)例子:
這種情況下,你仍然需要使用_mm_malloc或一個(gè)相當(dāng)于為指針?lè)峙鋵?duì)齊內(nèi)存的可移植性操作系統(tǒng)接口(POSIX),但通過(guò)使用_declspec(align(32)),你就是要為排列arr1中的每一個(gè)元素都強(qiáng)制對(duì)齊到32個(gè)字節(jié)。
你也可以使用這個(gè)數(shù)據(jù)對(duì)齊支持來(lái)為高速緩存線使用最優(yōu)化提供優(yōu)勢(shì)。通過(guò)把平常經(jīng)常在一起使用的小對(duì)象聚集到一個(gè)結(jié)構(gòu)里,并強(qiáng)制這個(gè)結(jié)構(gòu)從高速緩存線的起始端分配內(nèi)存,你就能有效地保證每一個(gè)對(duì)象在需要的時(shí)候都能及時(shí)地被裝載進(jìn)高速緩存里,這樣會(huì)有很明顯的性能提升。例如,考慮i和j這兩個(gè)被頻繁調(diào)用的變量,他們可能會(huì)被分配到不同的告訴緩存線上。你可以像下面這么來(lái)聲明它們:
通過(guò)使用這種方式聲明變量,編譯器就能確保這些變量被分配在同一個(gè)高速緩存線上。
慧都提供【正版IDE聯(lián)合推廣計(jì)劃】,各種IDE低至半價(jià)出售(截止日期2014/12/31)。另外還有5折限時(shí)搶購(gòu)和免費(fèi)領(lǐng)iPhone 6、iPad air等好禮!
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn