realloc和memcpy如何工作?

时间:2008-12-12 13:43:13

标签: c memory-management realloc

我有两个问题。

  1. realloc()memcpy()是否以比在每个元素O(N)上迭代更快的方式将数组中的条目复制到另一个中?如果答案是肯定的,那么您认为它的复杂性是什么?

  2. 如果分配的大小小于原始大小,realloc()是否会将条目复制到其他位置,或者只是将它们留下来,因为它们会减小数组的大小?

8 个答案:

答案 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)

  1. 绝对没有办法比O(N)更快地复制N个项目。但是,它可能能够一次复制多个项目,或者使用特殊的处理器指令 - 因此它仍然可能比您自己做的更快。
  2. 我不确定,但我认为内存已完全重新分配。这是最安全的假设,无论如何它可能都依赖于实现。

答案 3 :(得分:5)

  1. memcpy的性能实际上并不比O(N)好,但可以进行优化,使其优于手动复制;例如,它可能能够在复制1个字节的时间内复制4个字节。许多memcpy实现是使用优化指令在汇编中编写的,这些指令可以一次复制多个元素,这通常比一次复制数据的速度快。

  2. 我不太明白这个问题,如果你使用realloc减少内存大小并且成功(返回非NULL),新位置将包含与旧版本相同的数据位置最大为新请求的大小。如果由于调用realloc而改变了内存位置(减小大小时通常不会),则会复制内容,否则不需要进行复制,因为内存未移动。

答案 4 :(得分:2)

  1. 可以推测,memcpy可以被写入,以便它可以移动大量的位。例如如果有利的话,完全可以使用SSE指令复制数据。
  2. 正如其他人所说,它不会比O(n)更快,但是内存系统通常具有首选的块大小,并且还可以,例如,一次写入缓存行的大小。

答案 5 :(得分:0)

假设你在谈论glibc,并且因为你的问题是依赖于实现的,所以最好只检查来源:

malloc.c

memcpy.c

我读它的方式,答案是:

  1. O(N)---无法以比线性时间更好的方式复制项目。
  2. 当使用realloc()缩小项目时,偶尔会复制大项目。

答案 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);

  1. realloc()函数应将ptr指向的内存对象的大小更改为size指定的大小。

  2. 对象的内容应保持不变,直至新旧尺寸中的较小者。

  3. 如果新尺寸较大,则未指定新分配的对象部分的内容。

  4. 如果size为0且ptr不是空指针,则释放指向的对象。

  5. 如果ptr是空指针,则realloc()应等于指定大小的malloc()。

  6. 如果ptr与之前由calloc(),malloc()或realloc()返回的指针不匹配,或者如果先前已通过调用free()或realloc()释放空间,则行为未定义。