- UID
- 1
- 精华
- 积分
- 76365
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
翻译自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:- 不保留原数据增大缓冲区的malloc()方式消耗了3毫秒的时间;
- 不保留原数据减小缓冲区的malloc()方式消耗了5毫秒的时间;
- 先malloc()再复制内容,最后free()旧缓冲区,逐步增大空间,消耗了1265毫秒;
- 先malloc()再复制内容,最后free()旧缓冲区,逐步减小空间,消耗了744毫秒;
- 用realloc()逐步增大空间,消耗了316毫秒;
- 用realloc()逐步减小空间,0毫秒。
复制代码 如我们所见,自己手动用memcpy()复制内容的方式,永远比realloc()慢,因为在这种场合下,malloc()保证每次申请的都是一块新的内存,然后你不得不从旧的内存复制数据到新的内存。而realloc()则可以重复利用旧的内存因为它的运作方式是调整缓冲区的大小,所以有时候可以省去复制内容的过程。所以,如果你想要在保持你的数据不变的前提下动态分配内存,realloc()大概是你应该使用的玩意儿。结果已经一目了然,我已不再打算测试这种无损malloc()方案。
让我们继续测试:初始内存32MB,扩大到48MB或者减小到16MB,每次增加或减少16KB:- 不保留原数据增大缓冲区的malloc()方式消耗了4毫秒的时间;
- 不保留原数据减小缓冲区的malloc()方式消耗了4毫秒的时间;
- 用realloc()逐步增大空间,消耗了21453毫秒;
- 用realloc()逐步减小空间,0毫秒。
复制代码 此时我们可以看到用realloc()逐步增大空间的过程消耗了大量的时间。而用realloc()逐步减少空间的过程消耗的时间却几乎没满1毫秒。这表明如果你不打算保留原数据的话你应该先free()再malloc()。
确实是这样吗?我们看一下另外的一组测试结果:(目测作者忘了说测试前提了)- 不保留原数据增大缓冲区的malloc()方式消耗了777毫秒的时间;
- 不保留原数据减小缓冲区的malloc()方式消耗了729毫秒的时间;
- 用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)- #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;
- }
复制代码
|
|