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