找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 4383|回复: 4

【C】使用realloc()与使用free() -> malloc()的差异

[复制链接]

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24237 个
贡献
46222 次
宅之契约
0 份
在线时间
2297 小时
注册时间
2014-1-26
发表于 2017-6-12 13:20:29 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
翻译自https://stackoverflow.com/questi ... ee-malloc-functions
其中我把自己的一些吐槽用斜体字标注了。看的时候无视即可。如果觉得啰嗦的话。
要注意,我这次的翻译是脑补译,不是直译。

提问:为什么要使用realloc()函数来动态分配数组,而不是先malloc()一块新的内存然后复制数据,再free()掉旧的内存,或是先free()掉旧的内存,再重新malloc()一块新的内存?各自的优势与劣势是啥?

一楼的回答:使用realloc()能保留旧的数据,而是用malloc()和free()的话,数据会丢失,你必须自己手动复制数据。(废话)

二楼瞎扯淡我就不翻译了。

三楼的回答:我知道这个问题非常老(2009年就有人回答了)但我希望我的回答能帮助到一些对此感兴趣的人(包括我)。(也包括我)

首先性能测试并不能得到决定性的结论,因为不同的平台和系统的内存管理机制不同。时至今日这些玩意儿变得越来越标准,所以这个测试结果应该也可以被安全用作一些参考点(但如果这些结果和你的实际测试有所差异,请告知)(要告知的话告知我就行,我会帮你转告原回答者的)。我用的是Windows 7,CPU是2.10Hz 四核 i3 2310M,内存是4GB,这是我能找到的最好的机器了(真穷)

我这个测评的方式是先分配一定数量的内存,然后不断地一步步增加分配的大小,直到分配到足够大小的内存。为此,我试了六种情况:

1、不保留原数据,先free()再malloc(),逐步增大分配的内存。
2、不保留原数据,先free()再malloc(),逐步减小分配的内存。
3、先malloc(),然后复制内容,再free()旧的缓冲区,逐步增大分配的内存。
4、先malloc(),然后复制内容,再free()旧的缓冲区,逐步减小分配的内存。
5、使用realloc(),逐步增大分配的内存。
6、使用realloc(),逐步减小分配的内存。

所以,第一次测试:从2MB内存开始,逐步增大到3MB或者减小到1MB,每次增大1KB或者减小1KB:
  1. 不保留原数据增大缓冲区的malloc()方式消耗了3毫秒的时间;
  2. 不保留原数据减小缓冲区的malloc()方式消耗了5毫秒的时间;
  3. 先malloc()再复制内容,最后free()旧缓冲区,逐步增大空间,消耗了1265毫秒;
  4. 先malloc()再复制内容,最后free()旧缓冲区,逐步减小空间,消耗了744毫秒;
  5. 用realloc()逐步增大空间,消耗了316毫秒;
  6. 用realloc()逐步减小空间,0毫秒。
复制代码
如我们所见,自己手动用memcpy()复制内容的方式,永远比realloc()慢,因为在这种场合下,malloc()保证每次申请的都是一块新的内存,然后你不得不从旧的内存复制数据到新的内存。而realloc()则可以重复利用旧的内存因为它的运作方式是调整缓冲区的大小,所以有时候可以省去复制内容的过程。所以,如果你想要在保持你的数据不变的前提下动态分配内存,realloc()大概是你应该使用的玩意儿。结果已经一目了然,我已不再打算测试这种无损malloc()方案。

让我们继续测试:初始内存32MB,扩大到48MB或者减小到16MB,每次增加或减少16KB:
  1. 不保留原数据增大缓冲区的malloc()方式消耗了4毫秒的时间;
  2. 不保留原数据减小缓冲区的malloc()方式消耗了4毫秒的时间;
  3. 用realloc()逐步增大空间,消耗了21453毫秒;
  4. 用realloc()逐步减小空间,0毫秒。
复制代码
此时我们可以看到用realloc()逐步增大空间的过程消耗了大量的时间。而用realloc()逐步减少空间的过程消耗的时间却几乎没满1毫秒。这表明如果你不打算保留原数据的话你应该先free()再malloc()。
确实是这样吗?我们看一下另外的一组测试结果:(目测作者忘了说测试前提了)
  1. 不保留原数据增大缓冲区的malloc()方式消耗了777毫秒的时间;
  2. 不保留原数据减小缓冲区的malloc()方式消耗了729毫秒的时间;
  3. 用realloc()逐步减小空间,消耗了19毫秒。
复制代码
(因为这些测试结果太过于相近,所以我做了多次测试然后取了平均值)
可以看出,逐步减小空间的过程,realloc()更快,这大概是因为它不用去搜寻新的可用内存块,它只需要继续使用旧的内存块然后收缩它。
从性能上来看,如果你需要频繁分配内存的话,可以看出这里存在巨大的性能差异。

与此同时,我们也能看出使用malloc()逐步增大空间,比使用malloc()逐步减小空间,要稍微慢一些。即使操作都一样:找一块内存块然后分配它。这样的差异,似乎是因为查找更大的内存块的时候,平均来说malloc()需要消耗更多的时间,而找更小的内存块的时候malloc()则更快一些。比如,我有个30MB的内存块,用malloc()从中分配16MB的话,会用到它;而用malloc()分配32MB的话,它就得跳过这个内存块然后继续找到足够大小的内存块。这大概是为啥我的测试结果存在各种各样的差异的原因了。

总而言之(前面太长懒得看的话看这里)
1、如果你要保留旧的数据,用realloc()。当你要增大缓冲区大小的时候,它比malloc()->memcpy()->free()快4倍。当你要缩小缓冲区大小的时候,它快100000倍。永远别自己复制数据。
2、如果你不要旧的数据了,你应该用free()->malloc()的方式来增大缓冲区,然后用realloc()的方式来缩小缓冲区。
3、如果你不要旧的数据了并且你也不知道旧的缓冲区的大小的话(你不知道你是要扩大还是缩小缓冲区的话),用free()->malloc()的方式。减小空间的话,realloc()快40倍,而增大空间的话,realloc()慢7600倍。除非你的程序要一下子分配很大一块内存,但却要频繁地少量缩减内存(差不多是一次分配内存后有200多次缩小内存,这是有可能的),不然,你应该用free()->malloc()的方式。

测试代码:(卧槽居然是cpp)
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <math.h>
  5. #include <chrono>

  6. #define TimePoint std::chrono::time_point<std::chrono::high_resolution_clock>
  7. #define Duration std::chrono::duration<double>
  8. #define getTime() std::chrono::high_resolution_clock::now()

  9. #define GB *(1<<30)
  10. #define MB *(1<<20)
  11. #define KB *(1<<10)
  12. #define  B *(1<<00)

  13. #define INITIAL_MEMORY (64 MB)                                        //How much memory to start with
  14. #define ALLOCATE_MEMORY (32 MB)                                        //How much memory to add/subtract
  15. #define BYTE_STEP (4 KB)                                                //How many bytes to allocate/deallocate per iteration
  16. #define ITERATIONS ALLOCATE_MEMORY/BYTE_STEP        //Times to repeat allocation

  17. inline void measure(const char* name,void* (*func)(int*,void*)) {
  18.         TimePoint start,end;
  19.         int size=INITIAL_MEMORY;        //16MB of memory
  20.         void* memory=malloc(size);
  21.         start=getTime();
  22.         for(int i=0;i<ITERATIONS;i++) {
  23.                 memory=func(&size,memory);
  24.         }
  25.         end=getTime();
  26.         Duration time=end-start;
  27.         int ms=(int)(time.count()*1000);
  28.         printf("%s took ",name);
  29.         if (ms/1000!=0) {
  30.                 printf("%d ",ms/1000);
  31.         }
  32.         printf("%d ms\n",ms%1000);
  33. }

  34. //free->malloc (data is lost)
  35. inline void* testFreeMallocIncrease(int* size,void* memory) {
  36.         free(memory);
  37.         *size+=BYTE_STEP;
  38.         return malloc(*size);
  39. }
  40. inline void* testFreeMallocDecrease(int* size,void* memory) {
  41.         free(memory);
  42.         *size-=BYTE_STEP;
  43.         return malloc(*size);
  44. }
  45. //malloc->memcpy->free (data is conserved)
  46. inline void* testMallocCopyFreeIncrease(int* size,void* memory) {
  47.         int oldSize=*size;
  48.         *size+=BYTE_STEP;
  49.         void* newMemory=malloc(*size);
  50.         memcpy(newMemory,memory,oldSize);
  51.         free(memory);
  52.         return newMemory;
  53. }
  54. inline void* testMallocCopyFreeDecrease(int* size,void* memory) {
  55.         *size-=BYTE_STEP;
  56.         void* newMemory=malloc(*size);
  57.         memcpy(newMemory,memory,*size);
  58.         free(memory);
  59.         return newMemory;
  60. }
  61. //realloc (data is conserved)
  62. inline void* testReallocIncrease(int* size,void* memory) {
  63.         *size+=BYTE_STEP;
  64.         return realloc(memory,*size);
  65. }
  66. inline void* testReallocDecrease(int* size,void* memory) {
  67.         *size-=BYTE_STEP;
  68.         return realloc(memory,*size);
  69. }

  70. int main(int argc,char* argv[]) {
  71.         measure("Increasing Lossful Malloc",testFreeMallocIncrease);
  72.         measure("Decreasing Lossful Malloc",testFreeMallocDecrease);
  73.         //measure("Increasing Malloc",testMallocCopyFreeIncrease);
  74.         //measure("Decreasing Malloc",testMallocCopyFreeDecrease);
  75.         measure("Increasing Realloc",testReallocIncrease);
  76.         measure("Decreasing Realloc",testReallocDecrease);
  77.         return 0;
  78. }
复制代码

本帖被以下淘专辑推荐:

回复

使用道具 举报

30

主题

210

回帖

2778

积分

用户组: 版主

UID
1821
精华
7
威望
69 点
宅币
2159 个
贡献
206 次
宅之契约
0 份
在线时间
480 小时
注册时间
2016-7-12
发表于 2017-6-12 14:29:40 | 显示全部楼层
如果realloc时候的空间不足效率就和malloc memcpy free效率差不多了
回复 赞! 靠!

使用道具 举报

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24237 个
贡献
46222 次
宅之契约
0 份
在线时间
2297 小时
注册时间
2014-1-26
 楼主| 发表于 2017-6-12 14:41:12 | 显示全部楼层
Ayala 发表于 2017-6-12 14:29
如果realloc时候的空间不足效率就和malloc memcpy free效率差不多了

一些实现得不够完美的realloc()会直接返回NULL,而不是调整内存块。
回复 赞! 靠!

使用道具 举报

4

主题

16

回帖

50

积分

用户组: 小·技术宅

UID
4127
精华
0
威望
0 点
宅币
30 个
贡献
0 次
宅之契约
0 份
在线时间
1 小时
注册时间
2018-8-6
发表于 2018-8-6 09:13:51 | 显示全部楼层
emmm我什么也不懂凑个热闹
回复 赞! 靠!

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
发表于 2018-9-1 10:39:09 | 显示全部楼层
啊,好帖子顶
Passion Coding!
回复 赞! 靠!

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-20 15:30 , Processed in 0.041635 second(s), 34 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表