内存转换的灵活运用

网络开发中,经常会碰到一些内存与转换,如下面的场景:

 

[cpp] 

  1. #define PACKAGE_PARSE_ERROR -1
  2. #define PACKAGE_PARSE_OK 0
  3. int parse_package( int* a, int* b, int* c, int* d, char* buf, int buf_len )
  4. {
  5.         if( !buf || buf_len < 16 ){
  6.                 return PACKAGE_PARSE_ERROR;
  7.         }
  8.         memcpy( a, buf, 4 );
  9.         memcpy( b, buf + 4, 4 );
  10.         memcpy( c, buf + 8, 4 );
  11.         memcpy( d, buf + 12, 4 );
  12.         return PACKAGE_PARSE_OK;
  13. }

 

 

这是网络解包的过程,封包的过程则是逆过程。

像这样的应用其实完全可以用整型强制转换来代替,而且效率会至少提高一倍。

为了说明问题,我们举个简单的例子:

 

[cpp] 

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <memory.h>
  4. int main()
  5. {
  6.         int s;
  7.         char buffer[4];
  8.         memcpy(&s, buffer, 4 );
  9.         s = *(int*)(buffer);
  10.         return 0;
  11. }

第10行和第11行的效果是一样的,10行采用的是内存复制,11行采用的是强制转换,为了方便比较,我们看一下汇编代码:

 

 

[cpp] 

  1. pushq   %rbp
  2. .cfi_def_cfa_offset 16
  3. .cfi_offset 6, -16
  4. movq    %rsp, %rbp
  5. .cfi_def_cfa_register 6
  6. subq    $16, %rsp
  7. leaq    -16(%rbp), %rcx
  8. leaq    -4(%rbp), %rax
  9. movl    $4, %edx
  10. movq    %rcx, %rsi
  11. movq    %rax, %rdi
  12. call    memcpy
  13. leaq    -16(%rbp), %rax
  14. movl    (%rax), %eax
  15. movl    %eax, -4(%rbp)
  16. movl    $0, %eax
  17. leave

代码中可以看出,内存复制方法占用了7-12行,共6行,强制转换占用了13-15行,共3行,指令上少了一半,其实还不止,因为第12行其实是一个行数调用,

 

必然会有栈的迁移,所以强制转换的效率比内存复制起码高一倍。

再看看glibc 的memcpy函数实现:

 

[cpp] 

  1. void *memcpy (void *dstpp, const void *srcpp, size_t len )
  2. {
  3.   unsigned long int dstp = (long int) dstpp;
  4.   unsigned long int srcp = (long int) srcpp;
  5.   if (len >= OP_T_THRES)
  6.     {
  7.       len -= (-dstp) % OPSIZ;
  8.       BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);
  9.       PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);
  10.       WORD_COPY_FWD (dstp, srcp, len, len);
  11.     }
  12.   BYTE_COPY_FWD (dstp, srcp, len);
  13.   return dstpp;
  14. }

9-11行分别是三种处理方法,取决于 len 与 OP_T_THRES的比较,一般 OP_T_THRES 是8或16,对于len 小于它的内存复制,采用的是字节方式转换,即遍历每个字节,第个字节都要经过 “内存–寄存器–内存” 这个过程。

 

从上面的分析可以看出,强制转换是节省了很大的运算时间,效率上至少提高一倍。不要小看这样的提升,在每秒几万并发的情况下,尤其每个并发都存在解包,封包的过程,这样的处理可以给我们带来相当大的性能提升。

标签