Chinaunix首页 | 论坛 | 博客
  • 博客访问: 33331
  • 博文数量: 34
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-15 22:44
文章分类
文章存档

2014年(34)

我的朋友

分类: C/C++

2014-05-15 23:40:50

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
 

上一篇博文,发现glibc中的strcpy的效率居然比我写出的例子要差,这实在让我感到惊奇。下面看看为什么glibc中的实现,效率会低呢?让我们反汇编两个实现的代码
第一个strcpy,是我写的例子。
  1. char* my_strcpy1(char *dest, const char *src)
  2. {
  3.     char *= dest;
  4.     register char c;
  5.     
  6.     do {
  7.         c = *src++;
  8.         *d++ = c;
  9.     } while ('\0' != c);

  10.     return dest;
  11. }
  12. 它对应的汇编代码如下:
  1. Dump of assembler code for function my_strcpy1:
  2. 0x08048394 <+0>: push %ebp
  3. 0x08048395 <+1>: mov %esp,%ebp
  4. 0x08048397 <+3>: push %ebx
  5. 0x08048398 <+4>: sub $0x10,%esp
  6. 0x0804839b <+7>: mov 0x8(%ebp),%eax
  7. 0x0804839e <+10>: mov %eax,-0x8(%ebp)
  8. 0x080483a1 <+13>: mov 0xc(%ebp),%eax
  9. 0x080483a4 <+16>: movzbl (%eax),%ebx
  10. 0x080483a7 <+19>: addl $0x1,0xc(%ebp)
  11. 0x080483ab <+23>: mov -0x8(%ebp),%eax
  12. 0x080483ae <+26>: mov %bl,(%eax)
  13. 0x080483b0 <+28>: addl $0x1,-0x8(%ebp)
  14. 0x080483b4 <+32>: test %bl,%bl
  15. 0x080483b6 <+34>: jne 0x80483a1
  16. 0x080483b8 <+36>: mov 0x8(%ebp),%eax
  17. 0x080483bb <+39>: add $0x10,%esp
  18. 0x080483be <+42>: pop %ebx
  19. 0x080483bf <+43>: pop %ebp
  20. 0x080483c0 <+44>: ret
  21. End of assembler dump.
红色部分的汇编代码为do {} while循环代码。

glibc中的strcpy的代码如下:
  1. /* Copy SRC to DEST. */
  2. char *
  3. my_strcpy2 (dest, src)
  4.      char *dest;
  5.      const char *src;
  6. {
  7.   register char c;
  8.   char * s = (char *)src;
  9.   const int off = dest - s - 1;

  10.   do
  11.     {
  12.       c = *s++;
  13.       s[off] = c;
  14.     }
  15.   while (!= '\0');

  16.   return dest;
  17. }
对应的汇编代码如下:
  1. Dump of assembler code for function my_strcpy2:
  2. 0x080483c1 <+0>: push %ebp
  3. 0x080483c2 <+1>: mov %esp,%ebp
  4. 0x080483c4 <+3>: push %ebx
  5. 0x080483c5 <+4>: sub $0x10,%esp
  6. 0x080483c8 <+7>: mov 0xc(%ebp),%eax
  7. 0x080483cb <+10>: mov %eax,-0xc(%ebp)
  8. 0x080483ce <+13>: mov 0x8(%ebp),%edx
  9. 0x080483d1 <+16>: mov -0xc(%ebp),%eax
  10. 0x080483d4 <+19>: mov %edx,%ecx
  11. 0x080483d6 <+21>: sub %eax,%ecx
  12. 0x080483d8 <+23>: mov %ecx,%eax
  13. 0x080483da <+25>: sub $0x1,%eax
  14. 0x080483dd <+28>: mov %eax,-0x8(%ebp)
  15. 0x080483e0 <+31>: mov -0xc(%ebp),%eax
  16. 0x080483e3 <+34>: movzbl (%eax),%ebx
  17. 0x080483e6 <+37>: addl $0x1,-0xc(%ebp)
  18. 0x080483ea <+41>: mov -0x8(%ebp),%eax
  19. 0x080483ed <+44>: add -0xc(%ebp),%eax
  20. 0x080483f0 <+47>: mov %bl,(%eax)
  21. 0x080483f2 <+49>: test %bl,%bl
  22. 0x080483f4 <+51>: jne 0x80483e0
  23. 0x080483f6 <+53>: mov 0x8(%ebp),%eax
  24. 0x080483f9 <+56>: add $0x10,%esp
  25. 0x080483fc <+59>: pop %ebx
  26. 0x080483fd <+60>: pop %ebp
  27. 0x080483fe <+61>: ret
  28. End of assembler dump.
这里的红色部分同样是对应的do{}while循环代码。

两个实现对应的汇编代码基本相似,那么是否由循环前面的代码引起的呢。my_strcpy2使用了offset,所以多了一些mov和sub操作。我再次更改了代码,在my_strcpy2中不再计算offset。
  1. #include <stdio.h>
  2. #include <stdlib.h>


  3. char* my_strcpy1(char *dest, const char *src)
  4. {
  5.     char *d = dest;
  6.     register char c;

  7.     do {
  8.         c = *src++;
  9.         *d++ = c;
  10.     } while ('\0' != c);

  11.     return dest;
  12. }


  13. int off;

  14. /* Copy SRC to DEST. */
  15. char *
  16. my_strcpy2 (dest, src)
  17.      char *dest;
  18.      const char *src;
  19. {
  20.   register char c;
  21.   char * s = (char *)src;

  22.   do
  23.     {
  24.       c = *s++;
  25.       s[off] = c;
  26.     }
  27.   while (c != '\0');

  28.   return dest;
  29. }

  30. int main()
  31. {
  32.     const char *str1 = "test1";
  33.     char buf[100];

  34.     off = buf-str1-1;

  35.     int i;
  36.     for (i = 0; i < 10000000; ++i) {
  37.         my_strcpy1(buf, str1);
  38.     }

  39.     return 0;
  40. }
通过使用一个off的全局变量,来省得my_strcpy2的offset的计算。但是结果仍然是my_strcpy1效率跟高。
my_strcpy1的时间约为0.147s,而my_strcpy2的时间为0.220s。再次查看汇编
  1. (gdb) disassemble my_strcpy1
  2. Dump of assembler code for function my_strcpy1:
  3. 0x08048394 <+0>: push %ebp
  4. 0x08048395 <+1>: mov %esp,%ebp
  5. 0x08048397 <+3>: push %ebx
  6. 0x08048398 <+4>: sub $0x10,%esp
  7. 0x0804839b <+7>: mov 0x8(%ebp),%eax
  8. 0x0804839e <+10>: mov %eax,-0x8(%ebp)
  9. 0x080483a1 <+13>: mov 0xc(%ebp),%eax
  10. 0x080483a4 <+16>: movzbl (%eax),%ebx
  11. 0x080483a7 <+19>: addl $0x1,0xc(%ebp)
  12. 0x080483ab <+23>: mov -0x8(%ebp),%eax
  13. 0x080483ae <+26>: mov %bl,(%eax)
  14. 0x080483b0 <+28>: addl $0x1,-0x8(%ebp)
  15. 0x080483b4 <+32>: test %bl,%bl
  16. 0x080483b6 <+34>: jne 0x80483a1
  17. 0x080483b8 <+36>: mov 0x8(%ebp),%eax
  18. 0x080483bb <+39>: add $0x10,%esp
  19. 0x080483be <+42>: pop %ebx
  20. 0x080483bf <+43>: pop %ebp
  21. 0x080483c0 <+44>: ret
  22. End of assembler dump.
  23. (gdb) disassemble my_strcpy2
  24. Dump of assembler code for function my_strcpy2:
  25. 0x080483c1 <+0>: push %ebp
  26. 0x080483c2 <+1>: mov %esp,%ebp
  27. 0x080483c4 <+3>: push %ebx
  28. 0x080483c5 <+4>: sub $0x10,%esp
  29. 0x080483c8 <+7>: mov 0xc(%ebp),%eax
  30. 0x080483cb <+10>: mov %eax,-0x8(%ebp)
  31. 0x080483ce <+13>: mov -0x8(%ebp),%eax
  32. 0x080483d1 <+16>: movzbl (%eax),%ebx
  33. 0x080483d4 <+19>: addl $0x1,-0x8(%ebp)
  34. 0x080483d8 <+23>: mov 0x80496bc,%eax
  35. 0x080483dd <+28>: add -0x8(%ebp),%eax
  36. 0x080483e0 <+31>: mov %bl,(%eax)
  37. 0x080483e2 <+33>: test %bl,%bl
  38. 0x080483e4 <+35>: jne 0x80483ce
  39. 0x080483e6 <+37>: mov 0x8(%ebp),%eax
  40. 0x080483e9 <+40>: add $0x10,%esp
  41. 0x080483ec <+43>: pop %ebx
  42. 0x080483ed <+44>: pop %ebp
  43. 0x080483ee <+45>: ret
  44. End of assembler dump.
现在效率仍然有区别,那么看来还是循环处出的问题。时间又晚了,下次再继续研究。

阅读(159) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~