最近和同事談到了ARM平臺(tái)下數(shù)據(jù)總線寬度及對(duì)齊方式對(duì)程序效率的影響問(wèn)題,在定義結(jié)構(gòu)數(shù)據(jù)類(lèi)型時(shí),為了提高系統(tǒng)效率,要注意字長(zhǎng)對(duì)齊原則。正好有點(diǎn)感觸和大家一起談?wù)劇?/p>
這里主要給大家解釋下所謂的對(duì)齊到底是什么?怎么對(duì)齊?為什么會(huì)對(duì)齊或者說(shuō)對(duì)齊帶來(lái)什么樣的效率差異?
1.先看下面的例子:
#include
#pragma pack(4)
struct A
{
char a;
int b;
};
#pragma pack()
#pragma pack(1)
struct B
{
char a;
int b;
};
#pragma pack()
int main()
{
A a;
cout<
B b;
cout<
}
默認(rèn)的vc我記得是4字節(jié)對(duì)齊ADS下是一字節(jié)對(duì)齊,因?yàn)槭莄/c++社區(qū)大家對(duì)PC比較熟悉 我就談PC下的對(duì)齊。
PC下設(shè)計(jì)放的太長(zhǎng)時(shí)間的有錯(cuò)誤就別客氣直接說(shuō),大家可以看到在ms的vc下按4字節(jié)對(duì)齊和1字節(jié)對(duì)齊的結(jié)果是截然不同的分別為8和5為什么會(huì)有這樣的結(jié)果呢?這就是x86上字節(jié)對(duì)齊的作用。
為了加快程序執(zhí)行的速度,一些體系結(jié)構(gòu)以對(duì)齊的方式設(shè)計(jì),通常以字長(zhǎng)作為對(duì)齊邊界。對(duì)于一些結(jié)構(gòu)體變量,整個(gè)結(jié)構(gòu)要對(duì)齊在內(nèi)部成員變量最大的對(duì)齊邊界,如A,整個(gè)結(jié)構(gòu)以4為對(duì)齊邊界,所以sizeof(a)為8,而不是5。
如果是原始我們概念下的的A中的成員將會(huì)一個(gè)挨一個(gè)存儲(chǔ),應(yīng)該只有char+int只有5個(gè)字節(jié)。這個(gè)差異就是由于對(duì)齊導(dǎo)致的。,然我們可以看到A的對(duì)齊要比B浪費(fèi)3個(gè)字節(jié)的存儲(chǔ)空間。
那為什么還要采取對(duì)齊呢?
那是因?yàn)轶w系結(jié)構(gòu)的對(duì)齊和不對(duì)齊,是在時(shí)間和空間上的一個(gè)權(quán)衡。
字節(jié)對(duì)齊節(jié)省了時(shí)間。應(yīng)該是設(shè)計(jì)者考慮用空間換取時(shí)間。
為什么說(shuō)對(duì)齊會(huì)提高效率呢節(jié)省時(shí)間?我想大家要理解的重點(diǎn)之重點(diǎn)就在這里了。
在我們常用的PC下總線寬度是32位
1.如果是總線寬度對(duì)齊的話
那么所有讀寫(xiě)操作都是獲取一個(gè)<=32位數(shù)據(jù)可以一次保證在數(shù)據(jù)總線傳輸完畢
沒(méi)有任何的額外消耗
|1|2|3|4|5|6|7|8|
從1開(kāi)始這里是a的起始位置,5起始為b的位置 訪問(wèn)的時(shí)候
如果訪問(wèn)a一次在總線傳輸8位其他24位無(wú)效的
訪問(wèn)b時(shí)則一次在總線上傳輸32完成
讀寫(xiě)均是一次完整
插敘一下 讀操作先要將讀地址放到地址總線上然后下個(gè)時(shí)鐘周期再?gòu)耐獠?/p>
存儲(chǔ)器接口上讀回?cái)?shù)據(jù)通過(guò)數(shù)據(jù)總線返回需要兩個(gè)周期
而寫(xiě)操作一次將地址及數(shù)據(jù)寫(xiě)入相應(yīng)總線就完成了
讀操作要比寫(xiě)操作慢一半
2.我們看訪問(wèn)數(shù)據(jù)時(shí)如果不對(duì)齊地址的情況
|1|2|3|4|5|6|7|8|
此時(shí)a的地址沒(méi)變還在1而因?yàn)槭遣粚?duì)齊則b的位置就在2處
這時(shí)訪問(wèn)就帶來(lái)效率上問(wèn)題 訪問(wèn)a時(shí)沒(méi)問(wèn)題還是讀會(huì)一個(gè)字節(jié)
但是2處地址因?yàn)椴皇强偩€寬度對(duì)齊一般的CPU在此地址操作將產(chǎn)生error
如sparc,MIPS。它們?cè)谟布脑O(shè)計(jì)上就強(qiáng)制性的要求對(duì)齊。在不對(duì)齊的地址上肯定發(fā)生錯(cuò)誤
但是x86是支持非對(duì)齊訪問(wèn)的
它通過(guò)多次訪問(wèn)來(lái)拼接得到的結(jié)果,具體做法就是從1地址處先讀回后三字節(jié)234 暫存起來(lái)
然后再由5地址處讀回一個(gè)字節(jié)5 與234進(jìn)行拼接組成一個(gè)完整的int也就是b返回
大家看看如此的操作帶來(lái)的消耗多了不止三倍很明顯在字長(zhǎng)對(duì)齊時(shí)效率要高許多
淡然這種效率僅僅是訪問(wèn)多字節(jié)帶來(lái)的 如果還是進(jìn)行的byte操作那效率差不了多少
目前的開(kāi)發(fā)普遍比較重視性能,所以對(duì)齊的問(wèn)題,有2種不同的處理方法:
1) 有一種使用空間換時(shí)間做法是顯式的插入reserved成員:
struct A{
char a;
char reserved1[3]; //使用空間換時(shí)間
int b;
}a;
2) 隨便怎么寫(xiě),一切交給編譯器自動(dòng)對(duì)齊。
還有一種將邏輯相關(guān)的數(shù)據(jù)放在一起定義
代碼中關(guān)于對(duì)齊的隱患,很多是隱式的。比如在強(qiáng)制類(lèi)型轉(zhuǎn)換的時(shí)候。下面舉個(gè)例子:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最后兩句代碼,從奇數(shù)邊界去訪問(wèn)unsignedshort型變量,顯然不符合對(duì)齊的規(guī)定。
在x86上,類(lèi)似的操作只會(huì)影響效率,但是在MIPS或者sparc上,可能就是一個(gè)error
-
ARM
+關(guān)注
關(guān)注
135文章
9450瀏覽量
385684 -
PC
+關(guān)注
關(guān)注
9文章
2161瀏覽量
158015 -
數(shù)據(jù)總線
+關(guān)注
關(guān)注
2文章
62瀏覽量
18097
原文標(biāo)題:嵌入式er日常!和同事交流ARM平臺(tái)字節(jié)對(duì)齊帶來(lái)的效率差異
文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Allegro Skill布局功能--器件絲印過(guò)孔對(duì)齊介紹與演示

數(shù)據(jù)總線寬度可以配置為24位嗎?
包絡(luò)對(duì)齊改進(jìn)算法

什么是數(shù)據(jù)總線寬度/地址總線寬度?
詳解C語(yǔ)言字節(jié)對(duì)齊
ARM內(nèi)存邊界對(duì)齊以及sizeof問(wèn)題
基于ARM程序的字節(jié)對(duì)齊的分析
一個(gè)跟地址對(duì)齊有關(guān)的應(yīng)用異常案例

固態(tài)硬盤(pán)4K對(duì)齊操作對(duì)齊的到底是什么?為什么它如此重要?
SSD固態(tài)硬盤(pán)對(duì)齊的方法

STM32 終極字節(jié)對(duì)齊解析

單片機(jī)字節(jié)對(duì)齊

C語(yǔ)言 | 內(nèi)存對(duì)齊01 - 什么是內(nèi)存對(duì)齊

評(píng)論