memmove与复制单个数组元素

时间:2013-07-09 15:37:39

标签: c sorting optimization insertion-sort memmove

在CLRS第2章中,有一个练习,询问是否将插入排序的最坏情况运行时间改进为O(n lg n)。我看到this question并发现它无法完成。

最坏情况下的复杂性无法改善,但与单独移动数组元素相比,使用memmove实际运行时间会更好吗?

单独移动元素的代码

void insertion_sort(int arr[], int length)
{
    /*
    Sorts into increasing order
    For decreasing order change the comparison in for-loop
    */
    for (int j = 1; j < length; j++)
    {
        int temp = arr[j];
        int k;
        for (k = j - 1; k >= 0 && arr[k] > temp; k--){
            arr[k + 1] = arr[k];
        }
        arr[k + 1] = temp;
    }
}

使用 memmove

移动元素的代码
void insertion_sort(int arr[], int length)
{
    for (int j = 1; j < length; j++)
    {
        int temp = arr[j];
        int k;
        for (k = j - 1; k >= 0 && arr[k] > temp; k--){
                ;
        }
        if (k != j - 1){
            memmove(&arr[k + 2], &arr[k + 1], sizeof(int) *(j - k - 2));
        }
        arr[k + 1] = temp;
    }
}

我无法让第二个完美地运行,但这是我想要做的一个例子。

使用memmove

会有明显的速度提升吗?

4 个答案:

答案 0 :(得分:6)

{C}后面的实现可能会在您的C库中进行更优化。一些架构具有非常有效地一次移动整个存储器块的指令。理论运行时间复杂度不会得到改善,但它在现实生活中可能仍会运行得更快。

答案 1 :(得分:3)

memmove将完美地调整以最大限度地利用可用的系统资源(当然,每个实现都是唯一的)。

以下是来自专家C编程 - 深度C秘密的一个引用,介绍了使用循环和使用memcpy之间的区别(前面是两个代码片段,一个将源复制到一个目的地使用for循环和另一个memcpy):

  

在这种特殊情况下,源和目标都使用相同的   缓存行,导致每个内存引用错过缓存和   在处理器等待常规内存传送时停止处理器。   库memcpy()例程特别针对高性能进行了调整。   它将循环展开以读取一个高速缓存行然后写入   避免这个问题。使用智能副本,我们得到了巨大的成功   性能改进。这也显示了绘画的愚蠢   来自简单的基准程序的结论。

这可以追溯到1994年,但它仍然说明了标准库函数与您自己推出的任何内容相比有多优化。循环案例运行大约需要7秒,而memcpy运行大约需要1秒。

虽然memmove只会略微慢于memcpy,但由于需要对源和目标做出假设(memcpy中它们不能重叠),它仍应远远优于#include <string.h> #define DUMBCOPY for (i = 0; i < 65536; i++) destination[i] = source[i] #define SMARTCOPY memcpy(destination, source, 65536) int main() { char source[65536], destination[65536]; int i, j; for (j = 0; j < 100; j++) DUMBCOPY; /* or put SMARTCOPY here instead */ return 0; } 到任何标准循环。

请注意,这不会影响复杂性(正如另一张海报所指出的那样)。复杂性不依赖于更大的缓存或展开的循环:)

这里要求的是代码片段(略有变化):

$ time ./a.out 
real    0m0.002s
user    0m0.000s
sys     0m0.000s

在我的机器上(32位,Linux Mint,GCC 4.6.3)我得到以下时间:

使用SMARTCOPY:

$ time ./a.out 
real    0m0.050s
user    0m0.036s
sys     0m0.000s

使用DUMBCOPY:

{{1}}

答案 2 :(得分:2)

这完全取决于您的编译器和其他实现细节。确实memmove可以用一些棘手的超优化方式实现。但与此同时,智能编译器可能能够找出每个元素复制代码正在做什么,并以相同(或非常类似)的方式对其进行优化。试一试,亲眼看看。

答案 3 :(得分:0)

你不能用C实现击败memcpy。因为它是用asm编写的,并且具有良好的算法。

如果你为特定的cpu编写asm代码,并开发一个考虑缓存的好算法,你可能有机会。

标准库函数经过优化,使用它们总是更好。