请问一个数组下标效率的问题

alldying 2020-05-10 08:00:19
代码片段如下,linux,malloc200W和2W,耗时有约15倍的差距,请问是什么原因及如何优化啊?

char **cc;
cc = malloc(sizeof(char *)*2000000);
gettimeofday(&tv1, 0);
char ccc = 'a';
*(cc + 0) = 'a';
gettimeofday(&tv2, 0);
fprintf(stdout, "2.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec);
...全文
208 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
ForestDB 2020-05-11
  • 打赏
  • 举报
回复
我拿Linux来解释,不完全准确,可以近似这样理解。 当你malloc了200W字节的时候,系统实际上给你开了个空头支票。你虽然拿到了分配的内存的地址,但底层的物理内存压根就没有分配,只是做了个备案。当你开始往内存中写东西的时候,系统才开始给你分配真正的物理内存,而且容易想象,你写的地址的offset越大,系统需要真正准备的物理内存越多,花的时间越长。 想要准确的解释,可以去看虚拟内存这块。
gouyanfen 2020-05-11
  • 打赏
  • 举报
回复
引用 7 楼 alldying 的回复:
我觉得你可以把cc转为unsigned int 然后再运算+偏移,不要用他本身的类型 *(cc+150000)='a' 这个运算是 cc+sizeof(char)*150000 *(char*)((int)cc+150000)='a'; 这样看会不会快些
alldying 2020-05-11
  • 打赏
  • 举报
回复
修改成
*(char*)((int*)cc+150000)='a';
后,运行2次,第一次有大幅改善,第二次结果跟之前差不多,结果如下

2.1. 1589128544,452039,1589128544,452045
2.2. 1589128544,452047,1589128544,453108


2.1. 1589128846,724661,1589128846,724667
2.2. 1589128846,724669,1589128846,724929

后来再多次运行,结果类似第二次。

猜测,第一次刚好如你上面所说,cc分配在同一物理页了,所以快了很多,因为单从数组来说,操作各个位置的复杂度应该是O(1),但是到寻址这一步,如果跨了分页,那么离起点越远就步骤越多,越耗时。
alldying 2020-05-10
  • 打赏
  • 举报
回复
谢谢gouyanfen的回复。


1、改成偏移10,结果差不多,这个测试是想知道对一个大数组的不同位置,由其是距离起点不同距离的位置赋值是否耗时一致;

char **cc;
cc = malloc(sizeof(char *)*2000000);
gettimeofday(&tv1, 0);
*(cc + 10) = 'a';
gettimeofday(&tv2, 0);
fprintf(stdout, "2.1.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec);

gettimeofday(&tv1, 0);
*(cc + 1500000) = 'a';
gettimeofday(&tv2, 0);
fprintf(stdout, "2.2.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec);

测试结果

2.1. 1589123428,159323,1589123428,159328
2.2. 1589123428,159329,1589123428,159613

2、变量是char类型是因为实际项目中这里是个自定义的结构,结构大小没法保持和总线位数一样;

3、虽然这个测试的目的不是内存分配效率,不过,怎样才能分配在一个物理内存页呢,那样的话赋值效率应该也会高些;
gouyanfen 2020-05-10
  • 打赏
  • 举报
回复
引用 5 楼 alldying 的回复:
不好意思,刚刚描述有点错,是200W时对不同位置赋值,耗时不一样,下面是代码和结果 char **cc; cc = malloc(sizeof(char *)*2000000); gettimeofday(&tv1, 0); *(cc + 0) = 'a'; gettimeofday(&tv2, 0); fprintf(stdout, "2.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec); gettimeofday(&tv1, 0); *(cc + 1500000) = 'a'; gettimeofday(&tv2, 0); fprintf(stdout, "2.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec); 测试结果 2.1589117027,554010,1589117027,554014 2.1589117027,554016,1589117027,554282
你这个测试不是很合理,*(cc+0)='a';编译器没有那么笨的,会优化的,所以你应该改一下位置不要用偏移为0, 其次你的变量为char类型,那么在偏移寻址,地址操作时就多了几个寻址取数的汇编码的寻址最快的是和总线相同位数的 再有一个你测试分配内存测试效率,应该尽量分配在一个物理内存页中。
alldying 2020-05-10
  • 打赏
  • 举报
回复
不好意思,刚刚描述有点错,是200W时对不同位置赋值,耗时不一样,下面是代码和结果

char **cc;
cc = malloc(sizeof(char *)*2000000);
gettimeofday(&tv1, 0);
*(cc + 0) = 'a';
gettimeofday(&tv2, 0);
fprintf(stdout, "2.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec);

gettimeofday(&tv1, 0);
*(cc + 1500000) = 'a';
gettimeofday(&tv2, 0);
fprintf(stdout, "2.%lu,%lu,%lu,%lu\n", tv1.tv_sec, tv1.tv_usec, tv2.tv_sec, tv2.tv_usec);

测试结果

2.1589117027,554010,1589117027,554014
2.1589117027,554016,1589117027,554282

Simple-Soft 2020-05-10
  • 打赏
  • 举报
回复
不会有差别的,你把测试结果贴出来
alldying 2020-05-10
  • 打赏
  • 举报
回复
赋值是一样,关键是这个数组大小不一样,一个20W,一个2W,是否大小不一样导致寻址过程不一样呢
Simple-Soft 2020-05-10
  • 打赏
  • 举报
回复
这里赋值不都一样吗?
alldying 2020-05-10
  • 打赏
  • 举报
回复
补充,耗时集中在赋值,不在malloc,malloc的差距可以忽略。

69,374

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧