技术宅的结界

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

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 813|回复: 4
收起左侧

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

[复制链接]

1007

主题

2232

帖子

5万

积分

用户组: 管理员

一只技术宅

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

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

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

x
翻译自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)
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <chrono>

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

#define GB *(1<<30)
#define MB *(1<<20)
#define KB *(1<<10)
#define  B *(1<<00)

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

inline void measure(const char* name,void* (*func)(int*,void*)) {
	TimePoint start,end;
	int size=INITIAL_MEMORY;	//16MB of memory
	void* memory=malloc(size);
	start=getTime();
	for(int i=0;i<ITERATIONS;i++) {
		memory=func(&size,memory);
	}
	end=getTime();
	Duration time=end-start;
	int ms=(int)(time.count()*1000);
	printf("%s took ",name);
	if (ms/1000!=0) {
		printf("%d ",ms/1000);
	}
	printf("%d ms\n",ms%1000);
}

//free->malloc (data is lost)
inline void* testFreeMallocIncrease(int* size,void* memory) {
	free(memory);
	*size+=BYTE_STEP;
	return malloc(*size);
}
inline void* testFreeMallocDecrease(int* size,void* memory) {
	free(memory);
	*size-=BYTE_STEP;
	return malloc(*size);
}
//malloc->memcpy->free (data is conserved)
inline void* testMallocCopyFreeIncrease(int* size,void* memory) {
	int oldSize=*size;
	*size+=BYTE_STEP;
	void* newMemory=malloc(*size);
	memcpy(newMemory,memory,oldSize);
	free(memory);
	return newMemory;
}
inline void* testMallocCopyFreeDecrease(int* size,void* memory) {
	*size-=BYTE_STEP;
	void* newMemory=malloc(*size);
	memcpy(newMemory,memory,*size);
	free(memory);
	return newMemory;
}
//realloc (data is conserved)
inline void* testReallocIncrease(int* size,void* memory) {
	*size+=BYTE_STEP;
	return realloc(memory,*size);
}
inline void* testReallocDecrease(int* size,void* memory) {
	*size-=BYTE_STEP;
	return realloc(memory,*size);
}

int main(int argc,char* argv[]) {
	measure("Increasing Lossful Malloc",testFreeMallocIncrease);
	measure("Decreasing Lossful Malloc",testFreeMallocDecrease);
	//measure("Increasing Malloc",testMallocCopyFreeIncrease);
	//measure("Decreasing Malloc",testMallocCopyFreeDecrease);
	measure("Increasing Realloc",testReallocIncrease);
	measure("Decreasing Realloc",testReallocDecrease);
	return 0;
}

本帖被以下淘专辑推荐:

25

主题

86

帖子

1157

积分

用户组: 版主

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

1007

主题

2232

帖子

5万

积分

用户组: 管理员

一只技术宅

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

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

4

主题

20

帖子

50

积分

用户组: 小·技术宅

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

11

主题

100

帖子

738

积分

用户组: 大·技术宅

UID
3808
精华
1
威望
16 点
宅币
557 个
贡献
44 次
宅之契约
0 份
在线时间
90 小时
注册时间
2018-5-6
发表于 2018-9-1 10:39:09 | 显示全部楼层
啊,好帖子顶
菜鸟一枚,直接指正,不必留情

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-11-20 06:16 , Processed in 0.080804 second(s), 16 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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