作业还是memcpy?设置数组成员变量的首选方法是什么?

时间:2011-08-18 18:28:42

标签: objective-c c arrays variable-assignment memcpy

对于这个例子,我正在使用objective-c,但欢迎来自更广泛的C / C ++社区的答案。

@interface BSWidget : NSObject {
    float tre[3];
}
@property(assign) float* tre;

- (void)assignToTre:(float*)triplet {
    tre[0] = triplet[0];
    tre[1] = triplet[1];
    tre[2] = triplet[2];
}

- (void)copyToTre:(float*)triplet {
    memcpy(tre, triplet, sizeof(tre) );
}

所以在这两种方法之间,考虑到这些setter函数通常只能处理2,3或4的维度......

这种情况最有效的方法是什么?

gcc一般会将这些减少到相同的基本操作吗?

感谢。

3 个答案:

答案 0 :(得分:7)

快速测试似乎表明编译器在优化时会将memcpy调用替换为执行赋值的指令。

反汇编以下代码,当未经优化编译并使用-O2时,表明在优化的情况下testMemcpy函数不包含对memcpy的调用。

struct test src = { .a=1, .b='x' };

void testMemcpy(void)
{
  struct test *dest = malloc(sizeof(struct test));
  memcpy(dest, &src, sizeof(struct test));
}

void testAssign(void)
{
  struct test *dest = malloc(sizeof(struct test));
  *dest = src;
}

未经优化的testMemcpy,具有预期的memcpy调用

(gdb) disassemble testMemcpy 
Dump of assembler code for function testMemcpy:
   0x08048414 <+0>: push   %ebp
   0x08048415 <+1>: mov    %esp,%ebp
   0x08048417 <+3>: sub    $0x28,%esp
   0x0804841a <+6>: movl   $0x8,(%esp)
   0x08048421 <+13>:    call   0x8048350 <malloc@plt>
   0x08048426 <+18>:    mov    %eax,-0xc(%ebp)
   0x08048429 <+21>:    movl   $0x8,0x8(%esp)
   0x08048431 <+29>:    movl   $0x804a018,0x4(%esp)
   0x08048439 <+37>:    mov    -0xc(%ebp),%eax
   0x0804843c <+40>:    mov    %eax,(%esp)
   0x0804843f <+43>:    call   0x8048340 <memcpy@plt>
   0x08048444 <+48>:    leave  
   0x08048445 <+49>:    ret 

优化testAssign

(gdb) disassemble testAssign 
Dump of assembler code for function testAssign:
   0x080483f0 <+0>: push   %ebp
   0x080483f1 <+1>: mov    %esp,%ebp
   0x080483f3 <+3>: sub    $0x18,%esp
   0x080483f6 <+6>: movl   $0x8,(%esp)
   0x080483fd <+13>:    call   0x804831c <malloc@plt>
   0x08048402 <+18>:    mov    0x804a014,%edx
   0x08048408 <+24>:    mov    0x804a018,%ecx
   0x0804840e <+30>:    mov    %edx,(%eax)
   0x08048410 <+32>:    mov    %ecx,0x4(%eax)
   0x08048413 <+35>:    leave  
   0x08048414 <+36>:    ret   

优化的testMemcpy不包含memcpy调用

(gdb) disassemble testMemcpy 
Dump of assembler code for function testMemcpy:
   0x08048420 <+0>: push   %ebp
   0x08048421 <+1>: mov    %esp,%ebp
   0x08048423 <+3>: sub    $0x18,%esp
   0x08048426 <+6>: movl   $0x8,(%esp)
   0x0804842d <+13>:    call   0x804831c <malloc@plt>
   0x08048432 <+18>:    mov    0x804a014,%edx
   0x08048438 <+24>:    mov    0x804a018,%ecx
   0x0804843e <+30>:    mov    %edx,(%eax)
   0x08048440 <+32>:    mov    %ecx,0x4(%eax)
   0x08048443 <+35>:    leave  
   0x08048444 <+36>:    ret    

答案 1 :(得分:2)

从C背景讲,我建议使用直接分配。对于您的意图,该版本的代码更明显,如果您的数组在将来发生更改并且添加了您的函数不需要复制的额外索引,则该错误更少。

这两者并不完全相同。 memcpy通常实现为以固定大小的块(可能小于float)复制数据的循环,因此编译器可能不会为{{1}生成相同的代码}} 案件。确切知道的唯一方法是以两种方式构建它并在调试器中查看发出的程序集。

即使内联memcpy调用,也可能导致更多代码和更慢的执行时间。直接赋值情况应该更有效(除非您的目标平台需要特殊代码来处理memcpy数据类型)。然而,这只是一个有根据的猜测;唯一可以确切知道的方法是尝试两种方式并对代码进行分析。

答案 2 :(得分:0)

memcpy

  1. 做功能序言。
  2. 初始化计数器和指针。
  3. 检查是否有要复制的字节。
  4. 复制记忆。
  5. 增量指针。
  6. 增量指针。
  7. 增量计数器。
  8. 重复3-7 3或11次。
  9. 功能epilog。
  10. 直接分配:

    1. 复制记忆。
    2. 复制记忆。
    3. 复制记忆。
    4. 如您所见,直接分配要快得多。