0xAA55 发表于 2017-6-12 13:20:29

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

翻译自https://stackoverflow.com/questions/1401234/differences-between-using-realloc-vs-free-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)
#defineB *(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;
}

Ayala 发表于 2017-6-12 14:29:40

如果realloc时候的空间不足效率就和malloc memcpy free效率差不多了

0xAA55 发表于 2017-6-12 14:41:12

Ayala 发表于 2017-6-12 14:29
如果realloc时候的空间不足效率就和malloc memcpy free效率差不多了

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

3239066163 发表于 2018-8-6 09:13:51

emmm我什么也不懂凑个热闹

watermelon 发表于 2018-9-1 10:39:09

啊,好帖子顶
页: [1]
查看完整版本: 【C】使用realloc()与使用free() -> malloc()的差异