我有两个问题。
realloc()
和memcpy()
是否以比在每个元素O(N)
上迭代更快的方式将数组中的条目复制到另一个中?如果答案是肯定的,那么您认为它的复杂性是什么?
如果分配的大小小于原始大小,realloc()
是否会将条目复制到其他位置,或者只是将它们留下来,因为它们会减小数组的大小?
答案 0 :(得分:19)
1 - 否。他们一次复制一个块。请参阅http://www.embedded.com/design/configurable-systems/4024961/Optimizing-Memcpy-improves-speed进行非常好的分析。
2 - 这取决于实现。有关glibc的详细信息,请参阅http://www.gnu.org/software/libtool/manual/libc/Changing-Block-Size.html。 “在几个分配实现中,将块缩小有时需要复制它”
答案 1 :(得分:18)
让我们仔细看看memcpy
,当我们看到它时,请注意“大O”或Landau符号。
首先,大O.正如我在其他地方谈到的那样,值得记住big O的定义,即某些函数 g(n)被认为是 O(f(n))当存在 g(n)≤ kf(n)的常数 k 时。常量的作用是让你忽略重要部分的小细节。众所周知,在大多数普通架构中, n 字节的memcpy
将是 O(n),因为无论你需要移动那些 em > n 字节,一次一个块。因此,可以编写C中memcpy
的第一个天真实现
unsigned char *
memcpy(unsigned char * s1, unsigned char * s2, long size){
long ix;
for(ix=0; ix < size; ix++)
s1[ix] = s2[ix];
return s1;
}
实际上这是 O(n),可能会让你想知道为什么我们甚至不愿意使用库例程。然而,关于 libc 函数的事情是它们是特定于平台的实用程序被编写的地方;如果你想优化架构,这是你可以做到的地方之一。因此,取决于架构,可能有更高效的实现选项;例如,在IBM 360架构中,有一条MOVL
指令使用非常高度优化的微码来移动数据。因此,代替那个循环,memcpy的360实现可能看起来像
LR 3,S1 LOAD S1 ADDR in Register 3
LR 4,S2
MOVL 3,4,SIZE
(顺便说一句,不能保证完全正确的360代码,但它可以用于说明。)这个实现看起来像而不是做 n 步骤循环作为C代码,它只执行3条指令。
真正发生的事情是,它正在执行 O(n)micro 指令。两者之间的不同是常量 k ;因为微码更快,并且由于指令上只有三个解码步骤,它比天真版本显着更快,但它仍然 O(n) - 只是常数更小。
这就是为什么你可以充分利用memcpy
- 它不是渐近更快,但实现的速度和某人在特定架构上的一样快。
答案 2 :(得分:5)
答案 3 :(得分:5)
memcpy
的性能实际上并不比O(N)好,但可以进行优化,使其优于手动复制;例如,它可能能够在复制1个字节的时间内复制4个字节。许多memcpy
实现是使用优化指令在汇编中编写的,这些指令可以一次复制多个元素,这通常比一次复制数据的速度快。
我不太明白这个问题,如果你使用realloc
减少内存大小并且成功(返回非NULL),新位置将包含与旧版本相同的数据位置最大为新请求的大小。如果由于调用realloc
而改变了内存位置(减小大小时通常不会),则会复制内容,否则不需要进行复制,因为内存未移动。
答案 4 :(得分:2)
正如其他人所说,它不会比O(n)更快,但是内存系统通常具有首选的块大小,并且还可以,例如,一次写入缓存行的大小。
答案 5 :(得分:0)
假设你在谈论glibc,并且因为你的问题是依赖于实现的,所以最好只检查来源:
我读它的方式,答案是:
答案 6 :(得分:0)
x86具有扫描和匹配内存块中的字节/字的特殊指令,以及可用于复制内存块的指令(毕竟它是一个CISC cpu)。许多实现内联汇编语言的C编译器和一个用于内联整个函数的pragma多年来都在它们的库函数中利用了它。
用于mem copy的那些是movsb / movsw与rep指令的组合。
CMPS/MOVS/SCAS/STOS
REP, REPE, REPNE, REPNZ, REPZ
设置寄存器使用src / trg地址和int计数,然后离开。
答案 7 :(得分:0)
与realloc相关的一些要点(检查dev c ++): void * realloc(void * ptr,size_t size);
realloc()函数应将ptr指向的内存对象的大小更改为size指定的大小。
对象的内容应保持不变,直至新旧尺寸中的较小者。
如果新尺寸较大,则未指定新分配的对象部分的内容。
如果size为0且ptr不是空指针,则释放指向的对象。
如果ptr是空指针,则realloc()应等于指定大小的malloc()。
如果ptr与之前由calloc(),malloc()或realloc()返回的指针不匹配,或者如果先前已通过调用free()或realloc()释放空间,则行为未定义。