Вопрос

Я хотел бы скопировать относительно короткую последовательность памяти (менее 1 КБ, обычно 2–200 байт) в функции, критической по времени.Кажется, лучший код для этого на стороне процессора: rep movsd.Однако я почему-то не могу заставить свой компилятор генерировать этот код.Я надеялся (и я смутно помню, что видел это), что использование memcpy позволит сделать это с использованием встроенных функций компилятора, но на основе дизассемблирования и отладки кажется, что компилятор вместо этого использует вызов реализации библиотеки memcpy/memmove.Я также надеялся, что компилятор окажется достаточно умным, чтобы распознать следующий цикл и использовать rep movsd само по себе, но, похоже, это не так.

char *dst;
const char *src;
// ...
for (int r=size; --r>=0; ) *dst++ = *src++;

Есть ли способ заставить компилятор Visual Studio генерировать rep movsd последовательность, отличная от использования встроенной сборки?

Это было полезно?

Решение 3

Использование memcpy с постоянным размером

Что я нашел за это время:

Компилятор будет использовать встроенный код, если размер скопированного блока известен во время компиляции.Если это не так, вызывается реализация библиотеки.Когда размер известен, сгенерированный код очень хорош и выбирается в зависимости от размера.При необходимости это может быть одиночный mov, movsd или movsd, за которым следует movsb.

Похоже, что если я действительно хочу всегда использовать movsb или movsd, даже с «динамическим» размером, мне придется использовать встроенную ассемблерную систему или специальную встроенную функцию (см. ниже).Я знаю, что размер «довольно короткий», но компилятор этого не знает, и я не могу сообщить ему об этом — я даже пытался использовать __assume(size<16), но этого недостаточно.

Демонстрационный код, скомпилированный с помощью "-Ob1 (расширение только для встроенных):

  #include <memory.h>

  void MemCpyTest(void *tgt, const void *src, size_t size)
  {
    memcpy(tgt,src,size);
  }

  template <int size>
  void MemCpyTestT(void *tgt, const void *src)
  {
    memcpy(tgt,src,size);
  }

  int main ( int argc, char **argv )
  {
    int src;
    int dst;
    MemCpyTest(&dst,&src,sizeof(dst));
    MemCpyTestT<sizeof(dst)>(&dst,&src);
    return 0;
  }

Специализированные встроенные функции

Недавно я обнаружил, что существует очень простой способ копирования символов компилятором Visual Studio с помощью movsd - очень естественный и простой:использование встроенных функций.Следующие встроенные функции могут пригодиться:

Другие советы

На ум приходит несколько вопросов.

Во-первых, откуда вы знаете, что movsd будет быстрее?Вы посмотрели его задержку/пропускную способность?Архитектура x86 полна устаревших инструкций, которые не следует использовать, поскольку они не очень эффективны на современных процессорах.

Во-вторых, что произойдет, если вы используете std::copy вместо memcpy? std::copy потенциально быстрее, так как во время компиляции его можно специализировать для конкретного типа данных.

И в-третьих, включили ли вы встроенные функции в свойствах проекта -> C/C++ -> Оптимизация?

Конечно, я предполагаю, что включены и другие оптимизации.

Вы используете оптимизированную сборку?Он не будет использовать встроенную функцию, если не включена оптимизация.Также стоит отметить, что он, вероятно, будет использовать лучший цикл копирования, чем Rep movsd.Ему следует попытаться использовать MMX, по крайней мере, для выполнения 64-битного копирования за раз.Фактически, 6 или 7 лет назад я написал оптимизированный для MMX цикл копирования для выполнения подобных задач.К сожалению, встроенная функция memcpy компилятора превзошла мою копию MMX примерно на 1%.Это действительно научило меня не делать предположений о том, что делает компилятор.

Вы засекли время memcpy?В последних версиях Visual Studio реализация memcpy использует SSE2...который должен быть быстрее, чем rep movsd.Если размер копируемого блока составляет 1 КБ, то на самом деле это не проблема, если компилятор не использует встроенную функцию, поскольку время вызова функции будет незначительным по сравнению со временем копирования.

Обратите внимание, что для использования movsd, src должен указывать на память, выровненную по 32-битной границе, а ее длина должна быть кратна 4 байтам.

Если да, то почему ваш код использует char * вместо int * или что-то?Если нет, то ваш вопрос неактуален.

Если вы измените char * к int *, ты мощь получить лучший результат от std::copy.

Редактировать: вы измерили, что копирование является узким местом?

Используйте мемкопи.Эта проблема уже решена.

К вашему сведению, Rep movsd не всегда лучший, при некоторых обстоятельствах Rep movsb может быть быстрее, а с SSE и тому подобным лучшим является movntq [edi], xmm0.Вы даже можете дополнительно оптимизировать использование большого объема памяти при использовании локальности страниц, переместив данные в буфер, а затем переместив их в пункт назначения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top