写代码有时候和笃信宗教一样,一旦信仰崩溃,是最难受的事情。早年我读过云风的一篇《VC 对 memcpy 的优化》,以及《Efficiency geek 2: copying data in C/C++, optimisation》,所以我是坚信很难能写出比C运行时库更快的memcpy的。但最近有两个事情,让我对这个坚信产生了怀疑。

第一个个是最近在看lz4的代码,lz4可能是目前最快的内存压缩算法,部分评测他比snappy还要快点(lz4的实现后面专文剖析)。研究他的代码,发现他其中有个重要的和其他代码不同地方就是他的内存拷贝采用的是一个宏,而不是使用memcpy。其内部直接使用uint64_t转换指针进行拷贝赋值,个人估计这是他加快处理速度的一个地方。其拷贝代码大意如下,其实现不在乎字长溢出的部分。

  1. //你要保证dst有足够的空间,其要求空间很可能比sz大,都是8字节补齐的。
  2. #define ZEN_TEST_FAST_COPY(dst,src,sz) {\
  3. char *_cpy_dst = dst; \
  4. const char *_cpy_src = src; \
  5. size_t _cpy_size = sz;\
  6. do \
  7. { \
  8. ZBYTE_TO_UINT64(_cpy_dst) = ZBYTE_TO_UINT64(_cpy_src); \
  9. _cpy_dst += sizeof(uint64_t); \
  10. _cpy_src += sizeof(uint64_t); \
  11. }while( _cpy_size > sizeof(uint64_t) && (_cpy_size -= sizeof(uint64_t))); \
  12. }

第二个是看了一篇文章《哪个memcpy更快?》。发现里面说Linux标准库的memcpy比较不堪。这个有点颠覆了。原文代码有个问题是,其在最开始对非取整的部分进行了操作。但如果memcpy的dst,src参数地址是对齐的,这样明显不利于加快速度。我改进的代码如下:

  1. void *ZEN_OS::fast_memcpy(void *dst, const void *src, size_t sz)
  2. {
  3. void *r = dst;
  4.  
  5. //先进行uint64_t长度的拷贝,一般而言,内存地址都是对齐的,
  6. size_t n = sz & ~(sizeof(uint64_t) - );
  7. uint64_t *src_u64 = (uint64_t *) src;
  8. uint64_t *dst_u64 = (uint64_t *) dst;
  9.  
  10. while (n)
  11. {
  12. *dst_u64++ = *src_u64++;
  13. n -= sizeof(uint64_t);
  14. }
  15.  
  16. //将没有非8字节字长取整的部分copy
  17. n = sz & (sizeof(uint64_t) - );
  18. uint8_t *src_u8 = (uint8_t *) src;
  19. uint8_t *dst_u8 = (uint8_t *) dst;
  20. while (n-- )
  21. {
  22. (*dst_u8++ = *src_u8++);
  23. }
  24.  
  25. return r;
  26. }

文章代码里面还有个一个类似函数,区别在于,其在每次循环拷贝了2次uint64_t字长的数据。为了行文方便我们称之为fast_memcpy[2]把。

  1. while (n)
  2. {
  3. *dst_u64++ = *src_u64++;
  4. *dst_u64++ = *src_u64++;
  5. n -= sizeof(uint64_t)*;
  6. }

为了搞明白到底如何,只有自己测试一下。测试拷贝了8,16……64K,1M,4M字节的数据。在LINUX 64 (GCC 4.3 O3优化),Windows7 X64(Visual 2010 realse),Windwos7 win32(realse)的环境进行了测试,测试采用高精度定时器采集数据。。

第一组测试以字节对齐数据进行,无聊的数据就不贴了,直接上图。如果把最慢的速度比作100%,其他人作为和他的相对比率绘制图片,线条一直在下面的肯定更好一些:

Linux 64位下(GCC 4.3 O3优化),字节对齐情况下拷贝速度对比,如下图,

Windows7 X64,Visaul C++ 2010 Realse, 字节对齐情况下拷贝速度,如下图

看上面的比较图片就会知道memcpy在对齐清下,memcpy在任何时候都是不错的选择。

但内存拷贝还有另外一种常见情况,就是字节无法对齐的情况,而lz4作为一个压缩算法,可能恰恰要经常面对无法对齐的情况,所以我针对这种情况也做了一下测试。

Linux 64位下(GCC 4.3 O3优化),字节非对齐情况下拷贝速度,如下图,

Windows7 X64,Visaul C++ 2010 Realse, 字节非对齐情况下拷贝速度,如下图,宏的拷贝

在字节不对齐的情况下,并且拷贝的内存长度小于256字节,用8字节的赋值方式速度会稍微优于memcpy,这可能也是lz4采用这个方法的原因。如果拷贝的尺寸更大的不对齐时,memcpy还是更好的选择。而考虑到lz4大部分情况面对的拷贝字节,应该小于256字节。所以他采用宏拷贝的方式理论上是可以获得一些优势。

结论:

在字节对齐的情况下,memcpy在任何时候几乎都是最优的选择

确实有些方法在特定条件下比memcpy快一点。但如果你不知道如何选择,默认还是选择memcpy为好。同理也适应memset函数

Windows 平台上,如果数据长度达到1M或者4M,Windows的下的表现要好于Linxu平台,特别是在64位的平台上。(当然Linux测试环境是虚拟机,是否有一定影响?),可能的原因为止,和frostburn掰扯估计Windows下指令优化也许有过GCC之处。

写出比运行时库memcpy更快函数,这本身就是一个比较扯淡的事情,你的对手有编译器优化,代码运行优化,指令优化等多种手段。《哪个memcpy更快?》一文应该是有缪误的。当然他可能有他的背景(没有开优化?),作者可能并没有说清。

参考文档以及背景阅读:

VC 对 memcpy 的优化》 云风解释的VC对于各种拷贝长度,编译器做的优化。

Efficiency geek 2: copying data in C/C++, optimisation》作者对一些Geek的方法做了评估,当然第一篇memset部分写的更为详细一些。有些Geek作法大家可以看看。《各种版本的memcpy(底层优化)》一问解释一些数据拷贝的优化方法。《Optimizing Memcpy improves speed》这个里面提到的memcpy也不是我们所说的C运行时库的memcpy,但此文也解释了一些的提速数据拷贝方法。

哪个memcpy更快?》误导我的文章,我认为他所述的东东应该有个特殊场景。或者他说的C库的memcpy和我理解就不是一个东东?

C/C++ tip: How to copy memory quickly》文章也说明了这个问题,他的结论和我们一样。

【本文作者是雁渡寒潭,本着自由的精神,你可以在无盈利的情况完整转载此文 档,转载时请附上BLOG链接:http://www.cnblogs.com/fullsail/,否则每字一元,每图一百不讲价。对Baidu文库和360doc加价一倍】

扯扯淡,写个更快的memcpy的更多相关文章

  1. 更快的memcpy

    更快的memcpy 写代码有时候和笃信宗教一样,一旦信仰崩溃,是最难受的事情.早年我读过云风的一篇<VC 对 memcpy 的优化>,以及<Efficiency geek 2: co ...

  2. 面试官:如何写出让 CPU 跑得更快的代码?

    前言 代码都是由 CPU 跑起来的,我们代码写的好与坏就决定了 CPU 的执行效率,特别是在编写计算密集型的程序,更要注重 CPU 的执行效率,否则将会大大影响系统性能. CPU 内部嵌入了 CPU ...

  3. 【面向对象】用大白话扯扯那"神奇"的面向对象编程思维(二)

    前言: 上一章我们用大白话讲解了一下面向对象的编程思维,那么这一张我们来讲讲如何用面向对象来书写代码.终于到了激动人心的时刻了..... 传送门:https://www.cnblogs.com/sy1 ...

  4. php提供更快的文件下载

    在微博上偶然看到一篇介绍php更快下载文件的方法,其实就是利用web服务器的xsendfile特性,鸟哥的博客中只说了apache的实现方式,我找到了介绍nginx实现方式的文章,整理一下! let' ...

  5. ClownFish:比手写代码还快的通用数据访问层

    http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...

  6. CSS VS JS动画,哪个更快[译]

    英文原文:https://davidwalsh.name/css-js-animation 原作者Julian Shapiro是Velocity.js的作者,Velocity.js是一个高效易用的js ...

  7. 更快学习 JavaScript 的 6 个思维技巧

    更快学习 JavaScript 的 6 个思维技巧 我们在学习JavaScript,或其他任何编码技能的时候,往往是因为这些拦路虎而裹足不前: 有些概念可能会造成混淆,尤其当你是从其他语言转过来的时候 ...

  8. 新型序列化类库MessagePack,比JSON更快、更小的格式

    MessagePack is an efficient binary serialization format. It lets you exchange data among multiple la ...

  9. mysql DB server端,如何让读写更快

    其实,我不是专业的DB管理同学,甚至算不上会了解.只是在最近的工作中,遇到了DB server端优化的契机,所以把这些手段记录下来: 通过调整这个参数的值,可以让DB更给力: 这两个参数的含义: 1. ...

随机推荐

  1. 想让安卓 APP 如丝般顺滑?

    随着安卓手机市场占有率的节节攀升,随便在大街上找几个人估计 80% 用的都是安卓手机吧!用安卓手机的人这么多,不知道大家是否曾经感觉到过 APP 卡顿.死机?是否遇到应用程序无响应.闪退?本文就为大家 ...

  2. Spring/Hibernate 应用性能优化的7种方法

    对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能.此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法.本文系 O ...

  3. 几款国产开源的Windows界面库

    上次介绍的几款图形界面库http://blog.okbase.net/vchelp/archive/23.html都是国外的开源项目,今天介绍的几款都是国人的开源项目,大部分是采用DirectUI设计 ...

  4. WAF SSI

    http://www.2cto.com/Article/201405/299154.html

  5. C++中 模板Template的使用

    1.在c++Template中很多地方都用到了typename与class这两个关键字,而且好像可以替换,是不是这两个关键字完全一样呢?答:class用于定义类,在模板引入c++后,最初定义模板的方法 ...

  6. ANDROID_MARS学习笔记_S01_002View、监听器初步

    一.View.监听器介绍 二.在Activity中获取view和设置属性,设置button的监听器 1.activity_main.xml <LinearLayout xmlns:android ...

  7. P154、面试题28:字符串的排列

    题目:输入一个字符串,打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a.b.c所能排列出来的所有字符串abc.acb.bac.bca.cab.cba. 测试用例: 1)功能测试( ...

  8. pmf,cpmf,pdf,cdf,iid的解释

  9. 【HDOJ】4366 Successor

    基本思路是将树形结构转换为线性结构.然后,所求即为一个区间内大于abi的最大的loy指向的ID.将结点按照abi降序排序,注意abi可能相等.然后,使用线段树单点更新,区间查询可解. /* 4366 ...

  10. Mac修改用户名

    Mac 修改用户是一件很悲剧的事,因为牵涉到很多地方的修改,当然,如果只是需要满足登陆用户名的修改的话,就比较简单.而如果需要将某个用户在每一个地方显示的名字都改掉的话,就要修改不是地方了,下面就来讲 ...