Назначение или memcpy?Каков предпочтительный подход к установке переменной члена массива? - PullRequest
6 голосов
/ 18 августа 2011

В этом примере я работаю с target-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) );
}

Таким образом, между этими двумя подходами и с учетом того факта, что эти функции-установщики, как правило, будут обрабатывать только измерения 2,3 или 4 ...

Какой подход был бы наиболее эффективным для этогоситуация?

Будет ли GCC сводить их к одним и тем же базовым операциям?

Спасибо.

Ответы [ 3 ]

7 голосов
/ 18 августа 2011

Быстрый тест показывает, что при оптимизации компилятор заменяет вызов 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    
2 голосов
/ 18 августа 2011

Говоря на фоне Си, я рекомендую использовать прямое назначение.Эта версия кода более очевидна с точки зрения ваших намерений и менее подвержена ошибкам, если ваш массив изменится в будущем и добавит дополнительные индексы, которые вашей функции не нужно копировать.

Эти два параметра не являются строгоэквивалент.memcpy обычно реализуется как цикл, который копирует данные в виде фрагментов фиксированного размера (которые могут быть меньше, чем float), поэтому компилятор, вероятно, не сгенерирует тот же код для случая memcpy.Единственный способ узнать наверняка - это построить его в обоих направлениях и посмотреть на испущенную сборку в отладчике.

Даже если вызов memcpy является встроенным, это, вероятно, приведет к увеличению объема кода и замедлению времени выполнения.,Случай прямого назначения должен быть более эффективным (если ваша целевая платформа не требует специального кода для обработки float типов данных).Однако это только обоснованное предположение;единственный способ узнать наверняка - это попробовать оба способа и профилировать код.

0 голосов
/ 08 сентября 2012

memcpy:

  1. Пролог функции Do.
  2. Инициализация счетчика и указателей.
  3. Проверка наличия байтов для копирования.
  4. Память копии.
  5. Указатель приращения.
  6. Указатель приращения.
  7. Счетчик приращения.
  8. Повторите 3-7 3 или 11 раз.
  9. Функция эпилог.

Прямое назначение:

  1. Копирование памяти.
  2. Копирование памяти.
  3. Копирование памяти.

Как видите, прямое назначение намного быстрее.

...