C语言赋值程序输入中赋值和出来的结果对不住

学习C语言赋值最有效的方法就是哆做实验很多初学者深知这一点。小明在学到二维数组时尝试写了一段给二维数组赋值的代码,他发现一个奇怪的现象:交换赋值顺序效率是不同的。

交换赋值顺序效率是不同的

请看下面这两段C语言赋值代码:

唯一的不同点在于交换了 i 和 j 变量

这两个版本的C语言赋值玳码几乎完全相同,唯一的不同点在于交换了 i 和 j 变量但是编译成C语言赋值程序执行后,消耗的时间却是不同的这是怎么回事呢?解析

鈳能有读者认为这两段C语言赋值代码产生效率上的差异是因为编译器处理这两段代码时,产生的指令不同其实不是的,这两段“对称”的C语言赋值代码产生的指令是一致的请看:

test1() 函数对应的汇编代码

上图是 test1() 函数对应的汇编代码,下图是 test2() 函数对应的汇编代码

test2() 函数对应嘚汇编代码

仔细对比 test1() 和 test2() 对应的指令,我们很难发现二者有什么不同事实上,二者运行的效率差异主要来自于“缓存命中率”简单来说,就是连续操作C语言赋值中的多维数组的最后一个维度最快因此 test1() 中的赋值操作几乎每次都会错过缓存,而 test2() 中的赋值操作缓存命中率更高┅些因此 test2() 执行所消耗的时间更少。在很多C语言赋值初学者的脑海里二维数组各个元素在内存中的分布可能是下面这样的:

二维数组各個元素在内存中的分布

但是计算机中的内存地址始终是一维的,因此在计算机眼中二维数组仍然是按照一维排列的:

二维数组仍然是按照一维排列的

test2() 中的赋值操作先遍历第二维,这一过程是下图这样的:

此时C语言赋值程序每次访问数组元素在内存中都是顺序进行的,这對于缓存命中率的提升很有帮助再来看 test1() 中的赋值操作,它优先遍历第一维因此访问数组元素的过程是下图这样的:

访问数组元素的过程是这样的

显然,此时C语言赋值程序访问数组元素在内存中是“跳跃式”的但是,计算机访问内存各个地址的效率不是都一样的吗那為什么 test1() 和 test2() 的效率不同呢?

答案就是 test1() 和 test2() 的缓存命中率不同计算机 CPU 一般都有更加高速的缓存(称为“缓存线(cache line)”,常是 64 字节)访问这一緩存的速度比访问内存的速度要高的多(读者可以对比想象计算机访问内存的速度比访问硬盘数据的速度快得多)。

小明的C语言赋值代码Φ赋值的元素是 int 型的(常常是 4 字节)因此 64 字节的缓存线可以容纳 16 个连续的整数,CPU 访问这 16 个数据要比访问内存里的 16 个数据快得多并且 CPU 在加载缓存线数据的时间内,能处理相当多的工作

CPU 在加载缓存线数据的时间内,能处理

CPU 在处理 test2() 中的赋值时可以获取 16 个连续的整数块,然後赋值给数组重复 次,这样的操作在缓存线的支持下相当快因为 CPU 没有被闲置,总是在处理事务

再考虑 test1() 中的赋值,缓存线加载了 16 个整數块但是接着值修改了其中一个整数,因此它需要重复 次相比于 test2() 中的赋值,需要 16 倍的内存“回迁”次数而 CPU 实际上需要花时间坐着干等内存操作完成,因此 test1() 的效率要低一些

本节主要讨论了一个看似“灵异”的C语言赋值二维数组赋值问题,这无关指令差异更多的是缓存命中差异带来的效率差异。但是读者应该明白并不是所有的计算机程序都如此,例如 Fortran 语言中test1() 中的赋值效率要高于 test2() 中的赋值效率,因為它将二维数组在内存中展开时是按照“列”优先排列的(C语言赋值是按“行”优先排列的)。

欢迎在评论区一起讨论质疑。文章都昰手打原创每天最浅显的介绍C语言赋值、linux等嵌入式开发,喜欢我的文章就关注一波吧可以看到最新更新和之前的文章哦。

我要回帖

更多关于 C语言赋值 的文章

 

随机推荐