在我们的概念中,数组成员访问的时间复杂度是O(1),每个成员都可以定位一次,所以访问时间应该是一样的。那如果我说有一个1000万个元素的数组,访问第一个元素的时间和访问最后一个元素的时间一样吗?这个时候你会犹豫吗?实际动手验证实践是检验真理的唯一手段。做梦也没用,实际测试一下吧。我们实现如下所示的代码,其中我们创建了一个大小为1000万个元素的全局数组。然后分别给第一个元素和最后一个元素赋值,记录赋值前后的时间。#defineBUF_SIZE10000000//千万个元素inttest_array[BUF_SIZE]={};intmain(intargc,char*argv[]){longtime1;longtime2;time1=get_time();//获取时间,单位为纳秒test_array[0]=1;//访问第一个元素time2=get_time();printf("accessfirstitemtime:%ld\n",time2-time1);time1=get_time();test_array[BUF_SIZE-1]=1;//访问最后一个elementtime2=get_time();printf("accesslastitemtime:%ld\n",time2-time1);return(0);}完成代码后,我们来编译运行一下。为了得到稳定可靠的结果,我们多次运行。得到的结果如下所示。从测试结果可以看出,访问最后一个元素的性能明显比访问第一个元素慢很多,性能相差几十倍!原因分析要找出上述问题的原因,需要对计算机有更深入的了解。原理,包括可执行程序的内存布局,操作系统进程的原理,以及一些硬件层面的知识。接下来,我们将逐步介绍相关内容,弄清楚为什么会出现如此明显的性能差异。我们知道,用户态的程序都是在虚拟空间中运行的,每个程序都有自己的4GB虚拟空间。这个虚拟空间也称为虚拟内存。程序的虚拟内存不是立即分配的,而是按需分配的。也就是说,只有当用户访问这部分内存中的数据时,操作系统才会分配相应的物理内存,然后将数据加载到内存中。显然,从硬盘读取数据的速度肯定比直接访问内存慢很多。现代CPU使用多级缓存和流水线技术来改进系统。CPU会根据程序指令的运行状态,将部分数据或指令预加载到缓存中,这样就可以直接从缓存中读取数据。根据存储性能金字塔的数据,从缓存中读取数据的性能是从内存中读取数据的10倍以上。因此,如果代码不规则,CPU不能预取数据和指令,程序的运行效率肯定会很低。说到这里,让我们回到主题本身。由于这里的数组比较大,所以在访问第一个元素的时候,第1000万个元素一定不能预读,后面访问数据的时候很有可能会出现pagefault。因此,访问第一个元素和访问最后一个元素在性能上存在差异。进一步验证有了上面的分析,我们可以做进一步的验证。例如,我们可以连续访问最后一个元素两次,看看两者之间的区别。intmain(intargc,char*argv[]){longtime1;longtime2;time1=get_time();test_array[0]=1;time2=get_time();printf("accessfirstitemtime:%ld\n",time2-time1);time1=get_time();test_array[BUF_SIZE-1]=1;//第一次访问最后一个元素time2=get_time();printf("accesslastitemtime:%ld\n",time2-time1);time1=get_time();test_array[BUF_SIZE-1]=1;//第二次访问最后一个元素time2=get_time();printf("accesslastitemtime:%ld\n",time2-time1);return(0);}修改后代码,执行程序。同样,我们多次运行该过程以确保一致的结果。从下面的执行结果可以看出,第二次执行的时间和访问第一个元素的时间是一样的,没有明显的区别。通过以上的学习,你感受到深入学习计算机原理的重要性了吗?关于更深入的电脑内容,我们稍后会继续与大家分享。
