ITPub博客

首页 > > > 【向经典致敬】 关于 memcpy 效率问题

【向经典致敬】 关于 memcpy 效率问题

作者:u_453e1a12781dea 时间:2016-06-17 17:01:17 0 删除 编辑

先交代一下背景:

由于最近代码里面经常性。。或者说是必然性+反复性的做内存拷贝,使用的是memcpy(dst, src, size),

大概情况如下:

1, i5 core, 64bit, 4GB mem, laptop;
2,每秒钟大概都需要做100次数据拷贝;
3,一次拷贝的时候,需要拷贝一共约为 1024Byte*循环1000次;
4,src的内存空间是连续的,即src的数据全部在1M的空间里面(假设总数据量是3所述的1024Byte*1000);
5,但是,存放到dst的空间,却不是连续的;

4,5两点的意思大概是:
然后,src以一行(假设以1024Byte)为单位,
src的0,1,2,.....,n....行,
会存放到dst的 0,3,6,....,3n,....行里面;

这里应该意味着,不可以直接“块拷贝”之类的(?)。
伪代码如下:
char* src = (char*) malloc(sizeof(char) * 1024 * 1024);
char* dst = (char*) malloc(sizeof(char) * 1024 * 1024 * 3);
int j;
for(int i = 0; i < 1000; i ++) {
        j = 1024*i;       
        memcpy(&dst[3*j], &src[j], 1024);
}

根据1描述的环境,测试的时候大致留意了一下,每次拷贝数据,大约需要2ms(目前还不是release版本)

今天突然想起:

有没有比memcpy() 更高效的做法呢?最好当然是有API了。。。
(针对某一些硬件平台做的优化不在讨论之列)
------------------------

搜索了一下,发现有一个帖子:
http://bbs.chinaunix.net/thread-2320072-1-1.html
7楼:
我是memcpy的作者,如果你发现效率很低麻烦您告诉我,谢谢。

哈哈,好吧,第一反应我以为是 @linguranus 在调侃...
---------

然后在知乎再次看到这位大牛的回帖:
https://www.zhihu.com/question/35172305/answer/61584927

哇靠,才发现真的是大牛而不是在调侃的。 
敬礼!

回到上面关于memcpy()的问题,
各位有什么建议吗?

“
网友hanxin83:
必然性+反复性的做内存拷贝

我觉得没办法, 有好办法记得在这里写一记.
-----------------------------------------------------------------

PS, 努力自写一份, 绝大多数也达不到标准库的水平. 考虑上业务的特殊性做特别的优化.
性能提升也不会超过20%.


不过提问题, 直接限定"必然+反复"的条件, 似乎限定了讨论的范畴, 如果讲清楚使用场景
可能大家会想到更好的办法.

楼主robin10回复:
 rb>>的确,一般人写的,不太可能到达/超越libc,glibc里面的API,

除非,有某些 “其他” 方式,或者做这件事情,有其他更好的API,

比如说memmove()--这里只是举一个例子说明对内存、数据操作,除了memcpy()还有memmove()之类的,并非表达memmove()比memcpy()高效。

或者说,能直接调用类似于 内核态的API  或者 比较通用的DMA什么的。。。

“
楼主robin10分享:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

int main(void)
{
        int i;
        int j;
        int k;
        int m;
        struct timeval start_t;
        struct timeval end_t;


        char* src = (char*) malloc(sizeof(char) * 1024 * 1024);
        char* dst = (char*) malloc(sizeof(char) * 1024 * 1024 * 3);
        memset(src, 0x56, 1024*1024);
        memset(src, 0x00, 1024*1024);

        gettimeofday(&start_t, NULL);
        printf("#start#%ld.%ld#\n", start_t.tv_sec, start_t.tv_usec);

        for(k = 0; k < 1000; k ++) {
        //        memset(src, k, 1024*1024);
                m =k%3;
                for(i = 0; i < 1000; i ++) {
                        j = 1024*i;       
                        memcpy(&dst[m*j], &src[j], 1024);
                }
        }

        gettimeofday(&end_t, NULL);
        printf("#end#%ld.%ld#\n\n", end_t.tv_sec, end_t.tv_usec);
        printf("#dlt=%02ld Sec %06ld uSec#\n", end_t.tv_sec - start_t.tv_sec, end_t.tv_usec - start_t.tv_usec );

        return 0;
}
测试了一下,遇到一个有意思的现象:
i5, 4G memory, ubuntu12.04,64bit.
如果只是拷贝 一次 ,即没有for(k = 0; k < 1000; k ++) {...} 循环,
需要大概3.3ms
但是,
如果循环1000次,却只需要70ms左右,
如果每次使用 memset(src, k, 1024*1024);
大概90ms左右。

“
网友hanxin83:
既然不方便说架构和场景的话, 那我就只能提供一个关键字你看有用没有用了.
remap_file_pages

楼主robin10回复:
兄弟误会了,倒不是不方便,只是为了避免无用信息干扰,
尽可能的把无关信息过滤掉了而已,

并且这只是其中的非常小的一部分。。。却是必须用到的部分(也就是为什么说 每秒都要执行100次左右的原因)。

坦白说,其实对这个的优化,并没有抱着太大的希望,
因为相信能经得起99%的人使用、检验的API,当然不是吹的(起码里面99%左右应该是比较 极限 的了吧)。
反而比较好奇,

为什么连续做1000次,10000次循环memcpy(),和我做一次,消耗的时间完全不是线性的。

为了避免“每次都是拷贝一样的数据到一样的地址段,(拷贝之前对比了)内容一样,所以直接跳过” 这这可能性,
我也尽可能的每次都往src中放入不同的数据,并且拷贝到dst的不同位置(k%3),但是效果几乎是一样的。

补一张循环10K次的截图,耗时约为600ms。
1次 3ms;
1K次 70ms;
10K次 600ms;

如果数据不一样的话,cache的作用应该也不大了吧。。。

最后,

感谢提示~!

另外,提供一下科普资料:
http://www.man7.org/linux/man-pages/man2/remap_file_pages.2.html

编辑:王楠
注:本文版权由ChinaUnix技术社区所有,如需转载请注明出处。


原文作者:;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2019-07-17

  • 博文量
    1982
  • 访问量
    17930