Chinaunix首页 | 论坛 | 博客
  • 博客访问: 351311
  • 博文数量: 18
  • 博客积分: 2049
  • 博客等级: 大尉
  • 技术积分: 664
  • 用 户 组: 普通用户
  • 注册时间: 2007-07-06 17:38
文章分类

全部博文(18)

文章存档

2012年(1)

2011年(8)

2010年(4)

2009年(2)

2008年(3)

分类: C/C++

2010-06-20 19:28:30

    在复习Linux文件系统的代码,看到一段代码,想起来好像与“代码优化”一书中介绍的原理颇为契合,于是操刀编译试了试,最后焦点集中在了memcpy身上。试验的结果晒一下:

kernel:

static inline char *std_memcpy(char *to, char *from, int n)
{
int d0, d1, d2;
asm volatile ("rep ; movsl\n\t"
"movl %4,%%ecx\n\t"
"andl $3,%%ecx\n\t"
"jz 1f\n\t"
"rep ; movsb\n\t"
"1:":"=&c" (d0), "=&D"(d1), "=&S"(d2):"0"(n / 4),
"g"(n), "1"((long) to), "2"((long) from):"memory");
return to;
}



unroll:

static inline char* std_memcpy(char *dst, char *src, unsigned int sz)
{
   int n;
   unsigned long long *srcp, *dstp;
   char *r = dst;

  n = sz & (sizeof(unsigned long long) - 1);
  while (n-- && (*dst++ = *src++))

    ;

  n = sz & ~(sizeof(unsigned long long) - 1);
  srcp = (unsigned long long *) src;
  dstp = (unsigned long long *) dst;

  while (n && (*dstp++ = *srcp++))
     n -= sizeof(unsigned long long);
  return r;
}


working(more unroll):

static inline char* std_memcpy(char *dst, char *src, unsigned int sz)
{
int n;
unsigned long long *srcp, *dstp;
char *r = dst;

n = sz & ((sizeof(unsigned long long)<<1) - 1);
while (n--)
   *dst++ = *src++;

  n = sz & ~((sizeof(unsigned long long)<<1) - 1);
  srcp = (unsigned long long *) src;
  dstp = (unsigned long long *) dst;

while (n) {
  *dstp++ = *srcp++;
  *dstp++ = *srcp++;
  n -= sizeof(unsigned long long)<<1;
}
return r;


结果:

测试平台1

  • CPUIntel(R) Pentium(R) M processor 1.60GHz

  • 内存:1GB

  • gcc选项:-fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -fno-delete-null-pointer-checks -O2 -march=nocona -mno-red-zone -funit-at-a-time -maccumulate-outgoing-args -fstack-protector -fstack-protector-all -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mno-mmx -mno-3dnow -fomit-frame-pointer -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -mno-sse2 -mno-sse -m32 -Wall (为内核编译所用)


有图有真相,分别以复制512Bytes, 4KB, 512KB, 1MB, 2MB的数据展示结果:(横轴是用例编号,时间差是用rdtsc读出来的;std即为C库代码)




测试平台2


  • AMD Athlon(tm) 64 X2 Dual Core Processor 5000+

  • 内存:2GB

  • GCC选项同上。

同样示出类似AMD平台的结果:



结论

  • 2.6.31内核的memcpyNB

  • 较新的x86机器上,混合内存读写事务对性能影响不大

  • 从性能表现上看,AMDIntel机器的行为很接近。

  • AMD机器的表现似乎不太稳定,相同测试中的跳跃比较大,但我测试IntelAMD的软件环境不同,这点结论可能不足为凭。

  • C库中的memcpy()得试完再用,性能不是想像中的那么high:

    • 在复制少量数据时(<4KB),显然标准的memcpy并不占任何优势。

    • 在复制大量数据时( >1MB),所有方法似乎都差不多,但kernel和标准的stdcpy的性能表现更稳定一些。

上面的优化方法很简单,就是循环展开,我们可以用ANSI C写出更快的memcpy么?下面我试验的完整代码,绘制图片要求使用gnuplot 4.2 patchlevel 5


文件:strcpy.tar.gz
大小:2KB
:下载
阅读(6542) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

bg2bkk2014-08-02 22:37:20

loop unroll和more unroll的思想都对,在细节上可能有一点瑕疵。
在第二个代码unroll中
 n = sz & (sizeof(unsigned long long) - 1);
  while (n-- && (*dst++ = *src++))
这个while循环可能在完全复制n字节前,因为*src为0而结束,我想这里应该是有些问题的。

hengyunabc2011-12-11 00:28:38

貌似std_memcpy算法有问题,没有考虑到字节对齐的问题。
比如要复制99个字节算,按上面的unroll算法,是复制3个字节,再整8个地复制。
考虑到malloc分配到的内存一般是4字节对齐的。在long long赋值给另一个long long时要进行两次内存写操作。
??