在上一篇文章中,小编为您详细介绍了关于《高配版夏普S2咋样?mb860 软件检测 CPU型号: armv7 processor rev 0》相关知识。本篇中小编将再为您讲解标题如何理解 struct 的内存对齐?有多少CPU指令极大的提高了处理器效率。

padding = (align - (offset mod align)) mod alignnew offset = offset + padding = offset + (align - (offset mod align)) mod alignthe total size of the structure should be a multiple of the largest alignment of any structure member ------ wikipedia
③ 个因素导致现在的地址对齐约定:
生活很艰难世界多姿多彩,世上有各种不同的人存在但我们还是要在①起呀在①起
以下以最简,理想的模型进行讨论。
①个最小存储单位为 ⑧ 字节的内存来说。访问地址① · 大小为 ④ 字节的数据。只需读取地址 ⓪ 的 ⑧ 字节的数据。然后在出口处移位下就行。因为只需在出口处做①次处理,所以可以不计成本进行移位优化,但这种优化只能在部件内部或者部件组内部进行。不同组件的交互部分对“对齐”还是有不同的看法的。又因为 RISC CPU 的设计,大多精简指令集的指令长就是字长。而指令还需区分取立即数和各种 action, ①字长的指令无法全部用来表示地址空间。综上,大多 RISC CPU 强制地址对齐,地址的低位脑补成 ⓪。顺便也减少了地址线的宽度。
访问内存的速度是非常非常非常……慢的。再加上 CPU 及其指令设计的限制。在这种艰苦条件下,我们必须无所不用其极地减少随机内存访问次数:
在①个访问周期里读写最多,但不更多的数据。也就是①字长大小的数据。对于 CPU, 内存的最小存储单元的大小为最大,但不更大的 CPU 字长。
基于此,①般的 RISC CPU 的地址线宽度为 . 比如①个 ③② 位 CPU 的字长是 ③②bit, 字节大小为 ⑧bit, 那么地址线宽度为 可选择 个地址单位, 每个单位的大小是 ④ 字节 于是总共可管理 字节的内存。这也是逻辑地址最低 位总是为 ⓪ 的来历了。对于 ③②bit 字长的 CPU, 就是最低 ② 位为 ⓪ 了。注意,此段所述只是基于 de-facto 习惯(① byte = ⑧ bit, mem 空间大小为
byte), 为了便于讨论而做的假设。并无特别的意义和强行规定。
以①个字长为 ④ 字节的 RISC CPU 来进行讨论。单位为字节。此时,如果我们需要访问地址为 ② 的大小为 ① 字长也就是 ④ 字节的数据,也就是 ②-⑤ 的数据. 地址 ② mod 大小 ④ = ② 不为 ⓪. 这是未对齐的访问。(地址线以②进制表示。最后两位空置,所以始终为逻辑 ⓪)我们需要将地址线设为 ⓪(⓪⓪) 读出 ⓪-③ · 取 ②-③ · 然后将地址线设为 ①(⓪⓪) 读出 ④-⑦ 取 ④-⑤ · 然后再合成所需数据。共两次访问。如果要访问地址为 ⓪(⓪⓪) 或者 ①(⓪⓪) 的大小为 ① 字长的数据。则①次访问即可。这就是地址不对齐导致访问变慢的来历了。
此时若要访问 地址 ② 的半个字长大小的数据。也就是 ②-③ 的数据。我们可以将地址设为 ⓪(⓪⓪) 读出 ⓪-③ 的数据,然后将其在寄存器中右移 ② 字节即可。
那么,问题来了。既然如此,我也可以访问地址 ① · 大小为 ② 字节的数据啊。也就是 ①-② 的数据。将地址线设成 ⓪(⓪⓪) 读出数据,然后在寄存器内左移 ① 字节,再右移 ② 字节就行了啊。这时候 ① mod ②= ① · 不为 ⓪ · 但需要访问内存的次数还是①次。但世界上有许多地方,那儿的 CPU 字长只有 ② 字节。当数据到那些地方去旅行时。那儿的 CPU 访问地址为 ① · 大小为 ② 字节的数据的时候还是需要两次的。
那么,问题又来了。字长为 ④ 字节的 CPU, 访问 ⑧ 字节长度的数据,这个数据反正始终都要读两次,那么它不对齐也是可以的呀。只要他的地址 mod ④ 为 ⓪ 就可以了。但世界上还有些地方,那儿的 CPU 字长是 ⑧ 字节的,当数据到那些地方去旅行时。那儿的 CPU 访问大小为 ⑧ 字节的数据,若其地址 mod ⑧ 为 ⓪ 时,只要读①次就够了。此时读两次就是①种浪费了。
我们的世界是个艰难但又多姿多彩的世界。为了大家的数据都有①个兼容且①致的模型,方便交换,分析。我们郑重做出约定:
大小为 size 的字段,他的结构内偏移 offset 需符合 offset mod size 为 ⓪.引用的 wikipedia 的第①段就是对这句话的精确表述。
最后,问题又来了。
struct hi { let: ④ // padding ④ us: ⑧ // padding ⓪ play: ① // padding ① together: ② // padding ? }
together 字段的 padding 是要多少?是的 padding ⓪ 就行了。所以大小是 ⑧ + ⑧ + ② + ②= ②⓪那为什么 gcc 告诉我们应该是 ②④ 呢。
因为我们的世界不是孤单的世界。
数据们可以欢乐地组成团队。
hi group[②];
如果我们不能相互体谅,自私地将最后的 padding 设为 ⓪ 的话。
假设第①个 hi 位于地址 ⓪ · 那么第②个 hi 就得从地址 ②⓪ 开始了。此时 us 的地址是
, 而 ②⑧ mod ⑧= ④ 不为 ⓪.如果 hi 的大小为其中最大单元的整数倍也就是 ⑧ * ③= ②④ 的话。那么 第②个 hi 的 us 字段的地址是 ②④+⑧= ③②. 而 ③② mod ⑧= ⓪ · 对齐了。所以,最后我们还需要 padding ④ 字节。在这个不孤单的世界里,为了同①类数据能和谐相处。所以我们郑重做出约定:
整个结构的大小必须是其中最大字段大小的整数倍。
于是,不管是在①个数组里没羞没臊地在①起。还是在这个如此多姿多彩各不相同的世界里到处旅行。数据们的美好的生活都可以快速,和谐,①致地进行啦。
最后,若题主有闲,推荐看①下哈佛的 CS①⓪① · From NAND to Tetris 课程。从最简单的逻辑门开始,自己动手打造①遍 latch, flip-flop, register,RAM, ALU, CPU, assembler, compiler. 相信到时候你会有更深的体会。
针对@朱涵俊 的回答,提出反对。
cmov系列除非你arch=i⑤⑧⑥以前,不然只要是⑥⑧⑥及以后,都会生成,对应msvc则需要开启sse。很多linux发行版提供的③②位binary要求i⑥⑧⑥ · 其实就是因为用到了cmov。
bsr/bsf和bt家族,gcc不清楚,msvc都有intrinsic。
rep mov,你自己写memcpy甚至for循环复制,编译器也有机会生成的
cmpxchg,这个intrinsic也有,不然信号量和锁你以为怎么实现的?而且多核上这个指令还需要lock前缀。
不要自己①知半解了就总想搞个大新闻。
--------------------------------昏割线-------------------------------
gcc ④.⑨有个大改动,你们不关心,我很关心
x⑧⑥(IA③②)上,函数调用时候的参数传递是使用类似
push arg①
push arg⓪
call foo
这样的指令来做的
在老式的cpu上,gcc为了优化,将其改成
sub esp,⑧
mov [esp+④], arg①
mov [esp], arg⓪
call foo
在老式cpu上,这样做减少了对于esp这个指针的假依赖(因为需要esp的值,所以是依赖,然而push的行为是确定的,所以esp可以预测而非不可预测,因此是假依赖)。此外这样做还有个好处是如果调用多个函数,esp可以①次性减下来。
然而在近代cpu上,push更快。
后面的写法还有个问题,就是mov [esp+x], xxx占用的字节数远大于push xxx,所以如果函数调用较多,对于cache恐怕是足以以量变引起质变的。
Win③②API 函数调用链很常见,WINAPI的stdcall GCC(mingw-gcc)又支持的不好,配合上这点,说实话,在gcc④.⑨以前我真的在win③②上基本不会考虑用gcc链。
gcc④.⑨以前可以通过-mno-accumulate-outgoing-args来用回push,但是带来的代价是损失很多和函数调用有关的优化,比如栈每次都要调完函数的时候来回归指针(还有其他的,懒得再找code去编译看结果了)。
对应的release note如下。
-mno-accumulate-outgoing-args is now honored when unwind information is output. Argument accumulation is also now turned off for portions of programs optimized for size.
但实际上不仅仅是for size, for speed你也落后了。gcc④.⑨最大的价值并不是默认开启这个,而是能够在不损失其他优化的前提下开这个。
--------------------------------昏割线-------------------------------
啰嗦这么多,其实就是想说明
①. 新旧指令选择方面,编译器生成啥,①般人没必要关注。
②. 新指令真的有实质作用,请使用instrinsic或者应该有人做出库。
③. x⑧⑥还有很多黑科技的指令,比如我曾经写过①个追求极端optimize for size的小东西,里面连loop这种指令都被我强行用作jmp if ecx!=⓪来使用了(x⑧⑥ natively 有 jmp if ecx==⓪ 的指令 jecxz),之所以说“强行”,是因为loop实际做的是 if (ecx!=⓪) { ecx--; jmp ..... ;} 而在我的使用情况下,ecx的值可以摧毁,所以可以直接使用。这些黑科技会给你程序带来什么样的影响(变快或是变慢),请以Intel Software Developer\'s Manual以及实测为准。
编后语:关于《如何理解 struct 的内存对齐?有多少CPU指令极大的提高了处理器效率》关于知识就介绍到这里,希望本站内容能让您有所收获,如有疑问可跟帖留言,值班小编第一时间回复。 下一篇内容是有关《安卓手机到底应不应该关注内存占用问题?能增加手机的ram么》,感兴趣的同学可以点击进去看看。
小鹿湾阅读 惠尔仕健康伙伴 阿淘券 南湖人大 铛铛赚 惠加油卡 oppo通 萤石互联 588qp棋牌官网版 兔牙棋牌3最新版 领跑娱乐棋牌官方版 A6娱乐 唯一棋牌官方版 679棋牌 588qp棋牌旧版本 燕晋麻将 蓝月娱乐棋牌官方版 889棋牌官方版 口袋棋牌2933 虎牙棋牌官网版 太阳棋牌旧版 291娱乐棋牌官网版 济南震东棋牌最新版 盛世棋牌娱乐棋牌 虎牙棋牌手机版 889棋牌4.0版本 88棋牌最新官网版 88棋牌2021最新版 291娱乐棋牌最新版 济南震东棋牌 济南震东棋牌正版官方版 济南震东棋牌旧版本 291娱乐棋牌官方版 口袋棋牌8399 口袋棋牌2020官网版 迷鹿棋牌老版本 东晓小学教师端 大悦盆底 CN酵素网 雀雀计步器 好工网劳务版 AR指南针 布朗新风系统 乐百家工具 moru相机 走考网校 天天省钱喵 体育指导员 易工店铺 影文艺 语音文字转换器