Подтвердить что ты не робот

Очень быстро memcpy для обработки изображений?

Я занимаюсь обработкой изображений на C, которая требует копирования больших фрагментов данных вокруг памяти - источник и место назначения никогда не перекрываются.

Каков самый быстрый способ сделать это на платформе x86 с помощью GCC (где SSE, SSE2, но не SSE3)?

Я ожидаю, что решение будет либо в сборе, либо с использованием свойств GCC?

Я нашел следующую ссылку, но понятия не имею, лучше ли это сделать (автор также говорит, что у нее несколько ошибок): http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html

EDIT: обратите внимание, что необходима копия, я не могу обойтись, чтобы скопировать данные (я мог бы объяснить, почему, но я пощажу вам объяснение:))

4b9b3361

Ответ 1

Предоставлено William Chan и Google. На 30-70% быстрее, чем memcpy в Microsoft Visual Studio 2005.

void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size)
{

  __asm
  {
    mov esi, src;    //src pointer
    mov edi, dest;   //dest pointer

    mov ebx, size;   //ebx is our counter 
    shr ebx, 7;      //divide by 128 (8 * 128bit registers)


    loop_copy:
      prefetchnta 128[ESI]; //SSE2 prefetch
      prefetchnta 160[ESI];
      prefetchnta 192[ESI];
      prefetchnta 224[ESI];

      movdqa xmm0, 0[ESI]; //move data from src to registers
      movdqa xmm1, 16[ESI];
      movdqa xmm2, 32[ESI];
      movdqa xmm3, 48[ESI];
      movdqa xmm4, 64[ESI];
      movdqa xmm5, 80[ESI];
      movdqa xmm6, 96[ESI];
      movdqa xmm7, 112[ESI];

      movntdq 0[EDI], xmm0; //move data from registers to dest
      movntdq 16[EDI], xmm1;
      movntdq 32[EDI], xmm2;
      movntdq 48[EDI], xmm3;
      movntdq 64[EDI], xmm4;
      movntdq 80[EDI], xmm5;
      movntdq 96[EDI], xmm6;
      movntdq 112[EDI], xmm7;

      add esi, 128;
      add edi, 128;
      dec ebx;

      jnz loop_copy; //loop please
    loop_copy_end:
  }
}

Возможно, вы сможете оптимизировать его в зависимости от вашей конкретной ситуации и любых допущений, которые вы можете сделать.

Вы также можете проверить источник memcpy (memcpy.asm) и удалить его специальную обработку. Возможно дальнейшая оптимизация!

Ответ 2

На любом уровне оптимизации -O1 или выше GCC будет использовать встроенные определения для таких функций, как memcpy - с правильным параметром -march (-march=pentium4 для набора указанных вами функций), он должен генерировать довольно оптимальные встроенный в архитектуру встроенный код.

Я бы сравнил его и посмотрел, что получится.

Ответ 3

SSE-код, опубликованный hapalibashi, - это путь.

Если вам нужна еще большая производительность и не уклоняйтесь от долгой и извилистой дороги написания драйвера устройства: на всех важных платформах в настоящее время есть DMA-контроллер, способный выполнять копирование быстрее и параллельно к коду ЦП.

Это связано с написанием драйвера. Никакая большая ОС, о которой я знаю, предоставляет эту функциональность пользователю из-за рисков безопасности.

Однако, это может быть полезно (если вам нужна производительность), поскольку никакой код на земле не может опередить часть аппаратного обеспечения, предназначенное для выполнения такой работы.

Ответ 4

Этот вопрос сейчас четыре года, и я немного удивлен, что никто не упоминает пропускную способность памяти. CPU-Z сообщает, что у моей машины есть ОЗУ PC3-10700. То, что ОЗУ имеет максимальную пропускную способность (скорость передачи данных, пропускная способность и т.д.), Составляет 10700 Мбайт/с. Процессор на моей машине - i5-2430M, с максимальной частотой турбонаддува 3 ГГц.

Теоретически, с бесконечно быстрым процессором и моей оперативной памятью memcpy может работать со скоростью 5300 Мбайт/с, то есть с половиной 10700, потому что memcpy должен читать, а затем записывать в ОЗУ. (edit: Как указывал v.oddou, это упрощенное приближение).

С другой стороны, представьте, что у нас была бесконечно быстрая оперативная память и реалистичный процессор, чего мы могли достичь? В качестве примера можно использовать процессор с частотой 3 ГГц. Если бы он мог делать 32-битное чтение и 32-битную запись каждого цикла, тогда он мог бы передавать 3e9 * 4 = 12000 Мбайт/сек. Это кажется легко доступным для современного процессора. Уже сейчас мы видим, что код, запущенный на CPU, на самом деле не является узким местом. Это одна из причин того, что современные машины имеют кэши данных.

Мы можем измерить то, что CPU действительно может сделать, сравнивая memcpy, когда мы знаем, что данные кэшируются. Делать это аккуратно неудобно. Я сделал простое приложение, которое записывало случайные числа в массив, memcpy'd их в другой массив, затем проверило скопированные данные. Я прошел через код в отладчике, чтобы убедиться, что умный компилятор не удалил копию. Изменение размера массива изменяет производительность кэша - небольшие массивы вписываются в кеш, а большие - меньше. Я получил следующие результаты:

  • 40 Кбайт массивов: 16000 Мбайт/с
  • 400 Кбайт массивов: 11000 Мбайт/с
  • 4000 Кбайт массивов: 3100 Мбайт/с

Очевидно, что мой процессор может читать и записывать более 32 бит за цикл, поскольку 16000 - это больше, чем 12000, которые я вычислил теоретически выше. Это означает, что процессор даже меньше узкого места, чем я уже думал. Я использовал Visual Studio 2005 и вступил в стандартную реализацию memcpy, я вижу, что он использует инструкцию movqda на моей машине. Я думаю, это может читать и писать 64 бит за цикл.

Хороший код hapalibashi опубликовал на моей машине 4200 Мбайт/с - примерно на 40% быстрее, чем реализация VS 2005. Я думаю, что это быстрее, потому что для повышения производительности кэша используется команда prefetch.

Таким образом, код, выполняющийся на процессоре, не является узким местом, и настройка этого кода будет только незначительными улучшениями.

Ответ 5

Если вы работаете в Windows, используйте API DirectX, который имеет конкретный GPU - оптимизированные подпрограммы для обработки графики (насколько быстро это возможно: ваш CPU не загружен. Сделайте что-то еще, пока GPU его обманывает).

Если вы хотите быть агностиком от ОС, попробуйте OpenGL.

Не играйте с ассемблером, потому что слишком вероятно, что вы потерпите неудачу, чтобы превзойти 10-летнего опытного разработчика программного обеспечения для библиотек.

Ответ 6

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