我有两个问题。
realloc()
以比仅迭代每个元素更快的方式将数组中的memcpy()
条目复制到另一个数组中吗O(N)
?如果答案是肯定的,那么您认为它的复杂性是什么?如果分配的大小小于原始大小,是否
realloc()
将条目复制到其他地方,或者在它们减小数组大小时将它们保留?
我有两个问题。
realloc()
以比仅迭代每个元素更快的方式将数组中的memcpy()
条目复制到另一个数组中吗O(N)
?如果答案是肯定的,那么您认为它的复杂性是什么?
如果分配的大小小于原始大小,是否realloc()
将条目复制到其他地方,或者在它们减小数组大小时将它们保留?
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。“在几种分配实现中,有时需要将块变小以复制它”
让我们更仔细地看一下memcpy
“大 O”或朗道符号。
首先,大O。正如我在别处谈到的,值得记住大 O 的定义,即当存在一个常数k时,某个函数g(n)被称为O(f(n)),其中g(n) ≤ kf(n)。常量的作用是让您忽略小细节,转而关注重要部分。正如每个人都注意到的那样,在大多数正常架构中,n字节的 O(n) 将是O(n),因为无论你必须移动这些n字节,一次一个块。因此,可以编写 C 中的第一个简单的实现memcpy
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 代码,但它可以用作说明。)这个实现看起来不像 C 代码那样在循环中执行n步,它只执行 3 条指令。
然而,真正发生的是它在幕后执行O(n) 微指令。两者的不同之处在于常数k;因为微码要快得多,并且因为指令上只有三个解码步骤,所以它比原始版本快得多,但它仍然是O (n) - 它只是常数更小。
这就是为什么您可以充分利用它的原因memcpy
——它并不是渐进地更快,但实现速度与某人在该特定架构上实现的速度一样快。
的性能memcpy
确实不能比 O(N) 好,但可以对其进行优化,使其优于手动复制;例如,它可能能够在您复制 1 个字节的时间内复制 4 个字节。许多memcpy
实现是使用优化指令以汇编形式编写的,这些指令可以一次复制多个元素,这通常比一次复制一个字节的数据要快。
我不太明白这个问题,如果你realloc
用来减少内存大小并且它成功(返回非NULL),新位置将包含与旧位置相同的数据,直到新请求的大小。如果由于调用而更改了内存位置realloc
(在减小大小时不常见),则将复制内容,否则由于内存没有移动,因此不需要进行复制。
正如其他人所说,它不会比 O(n) 快,但内存系统通常有一个首选的块大小,并且还可以一次写入一个高速缓存行的大小。
x86 具有用于扫描和匹配内存块中的字节/字的特殊指令,以及可用于复制内存块的特殊指令(毕竟它是 CISC cpu)。许多实现内联汇编语言和编译整个函数的编译器的 C 编译器多年来一直在其库函数中利用这一点。
用于 mem 复制的是 movsb/movsw 与 rep 指令的组合。
CMPS/MOVS/SCAS/STOS
REP, REPE, REPNE, REPNZ, REPZ
使用 src/trg 地址和 int 计数设置寄存器,然后就可以了。
与 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() 释放,则行为未定义。