Лучше использовать, memcpy
как показано ниже, или лучше с std::copy()
точки зрения производительности? Зачем?
char *bits = NULL;
...
bits = new (std::nothrow) char[((int *) copyMe->bits)[0]];
if (bits == NULL)
{
cout << "ERROR Not enough memory.\n";
exit(1);
}
memcpy (bits, copyMe->bits, ((int *) copyMe->bits)[0]);
c++
performance
optimization
user576670
источник
источник
char
может быть подписанным или неподписанным, в зависимости от реализации. Если число байтов может быть> = 128, то используйтеunsigned char
для своих байтовых массивов. ((int *)
Актерский состав будет также более безопасным(unsigned int *)
.)std::vector<char>
? Или , так как вы говоритеbits
,std::bitset
?(int*) copyMe->bits[0]
делает?int
определяет его размер, но это похоже на рецепт для катастрофы, определенной реализацией, как и многие другие вещи здесь.(int *)
приведение является просто неопределенным поведением, а не определенным реализацией. Попытка сделать наложение шрифтов с помощью приведения нарушает строгие правила псевдонимов и, следовательно, не определяется Стандартом. (Кроме того, в C ++, хотя и не в C, вы не можете вводить каламбур через aunion
.) Практически единственным исключением является то, что вы конвертируете в вариантchar*
, но допуск не является симметричным.Ответы:
Я собираюсь пойти против общей мудрости здесь, которая
std::copy
будет иметь небольшую, почти незаметную потерю производительности. Я только что сделал тест и обнаружил, что это не соответствует действительности: я заметил разницу в производительности. Однако победителем сталstd::copy
.Я написал реализацию C ++ SHA-2. В моем тесте я хэшировал 5 строк, используя все четыре версии SHA-2 (224, 256, 384, 512), и зацикливался 300 раз. Я измеряю время, используя Boost.timer. Этого счетчика 300 циклов достаточно, чтобы полностью стабилизировать мои результаты. Я запускал тест по 5 раз каждый, чередуя
memcpy
версию иstd::copy
версию. Мой код использует преимущества сбора данных как можно большим количеством фрагментов (многие другие реализации работают сchar
/char *
, тогда как я работаю сT
/T *
(гдеT
самый большой тип в пользовательской реализации, который имеет правильное поведение переполнения), поэтому быстрый доступ к памяти на Наибольшие типы, которые я могу, имеют решающее значение для производительности моего алгоритма. Вот мои результаты:Время (в секундах) для завершения запуска тестов SHA-2
Общее среднее увеличение скорости std :: copy over memcpy: 2,99%
Мой компилятор - gcc 4.6.3 на Fedora 16 x86_64. Мои флаги оптимизации есть
-Ofast -march=native -funsafe-loop-optimizations
.Код для моих реализаций SHA-2.
Я решил провести тест на моей реализации MD5. Результаты были гораздо менее стабильными, поэтому я решил сделать 10 прогонов. Тем не менее, после моих первых нескольких попыток я получил результаты, которые сильно отличались от одного запуска к другому, поэтому я предполагаю, что происходила какая-то активность ОС. Я решил начать все сначала.
Те же настройки компилятора и флаги. Существует только одна версия MD5, и она быстрее, чем SHA-2, поэтому я сделал 3000 циклов на подобном наборе из 5 тестовых строк.
Вот мои последние 10 результатов:
Время (в секундах) до завершения теста MD5
Общее среднее снижение скорости std :: copy over memcpy: 0,11%
Код для моей реализации MD5
Эти результаты показывают, что есть некоторая оптимизация, которую std :: copy использовал в моих тестах SHA-2, которую
std::copy
нельзя было использовать в моих тестах MD5. В тестах SHA-2 оба массива были созданы в той же функции, которая вызвалаstd::copy
/memcpy
. В моих тестах MD5 один из массивов был передан функции в качестве параметра функции.Я провел немного больше тестов, чтобы увидеть, что я могу сделать, чтобы сделать
std::copy
быстрее снова. Ответ оказался простым: включите оптимизацию времени ссылки. Это мои результаты с включенным LTO (опция -flto в gcc):Время (в секундах) завершения теста MD5 с параметром -flto
Общее среднее увеличение скорости std :: copy over memcpy: 0.72%
Таким образом, за использование, похоже, не снижается производительность
std::copy
. На самом деле, похоже, увеличение производительности.Объяснение результатов
Так почему же это может
std::copy
повысить производительность?Во-первых, я не ожидал бы, что это будет медленнее для любой реализации, если включена оптимизация встраивания. Все компиляторы встраиваются агрессивно; это, возможно, самая важная оптимизация, поскольку она позволяет выполнять множество других оптимизаций.
std::copy
может (и я подозреваю, что все реализации реального мира) обнаруживают, что аргументы легко копируются и что память распределяется последовательно. Это означает, что в худшем случае, когдаmemcpy
это законно,std::copy
должно работать не хуже. Тривиальная реализация, отstd::copy
которой зависит,memcpy
должна соответствовать критериям вашего компилятора «всегда вставляйте это при оптимизации для скорости или размера».Тем не менее,
std::copy
также хранит больше своей информации. При вызовеstd::copy
функция сохраняет типы без изменений.memcpy
оперируетvoid *
, что отбрасывает практически всю полезную информацию. Например, если я передам массивstd::uint64_t
, компилятор или разработчик библиотеки могут воспользоваться преимуществами 64-разрядного выравниванияstd::copy
, но это может оказаться более сложнымmemcpy
. Многие реализации алгоритмов, как эта, работают, сначала работая с невыровненной частью в начале диапазона, затем с выровненной частью, затем с невыровненной частью в конце. Если все это гарантированно выровнено, то код становится проще и быстрее, и предиктору ветвления в вашем процессоре становится проще.Преждевременная оптимизация?
std::copy
находится в интересной позиции. Я ожидаю, что это никогда не будет медленнее,memcpy
а иногда и быстрее, с любым современным оптимизирующим компилятором. Более того, все, что вы можетеmemcpy
, вы можетеstd::copy
.memcpy
не допускает никакого перекрытия в буферах, тогда какstd::copy
поддерживает перекрытие в одном направлении (сstd::copy_backward
другим направлением перекрытия).memcpy
работает только на указатели,std::copy
работает на любых итераторы (std::map
,std::vector
,std::deque
, или мой собственный пользовательский тип). Другими словами, вы должны просто использовать,std::copy
когда вам нужно скопировать куски данных вокруг.источник
std::copy
это на 2,99% или 0,72% или -0,11% быстрее, чемmemcpy
это время для выполнения всей программы. Тем не менее, я обычно чувствую, что тесты в реальном коде более полезны, чем тесты в фальшивом коде. Вся моя программа получила это изменение в скорости выполнения. Реальные эффекты только двух схем копирования будут иметь большие различия, чем показано здесь, если рассматривать их отдельно, но это показывает, что они могут иметь измеримые различия в реальном коде.memcpy
иstd::copy
имеет разные реализации, поэтому в некоторых случаях компилятор оптимизирует окружающий код и фактический код копирования памяти как единый фрагмент кода. Другими словами, иногда одно лучше, чем другое, и даже другими словами, решение о том, что использовать, является преждевременной или даже глупой оптимизацией, потому что в каждой ситуации вам приходится проводить новые исследования и, более того, программы обычно разрабатываются, поэтому после некоторые незначительные изменения могут превратиться в преимущество функции над другими.std::copy
это тривиальная встроенная функция, которая вызывается толькоmemcpy
тогда, когда это допустимо. Базовое встраивание устранит любую отрицательную разницу в производительности. Я обновлю пост с небольшим объяснением того, почему std :: copy может быть быстрее.Все известные мне компиляторы заменят простой
std::copy
на,memcpy
когда это уместно, или даже лучше, векторизируют копию, чтобы она была еще быстрее, чем amemcpy
.В любом случае: профиль и узнай сам. Разные компиляторы будут делать разные вещи, и вполне возможно, что они не будут делать именно то, что вы просите.
Смотрите эту презентацию по оптимизации компилятора (pdf).
Вот что делает GCC для простого
std::copy
типа POD.Вот разборка (только с
-O
оптимизацией), показывающая вызовmemmove
:Если вы измените подпись функции на
затем
memmove
становитсяmemcpy
для небольшого улучшения производительности. Обратите внимание, чтоmemcpy
само по себе будет сильно векторизовано.источник
memmove
не должно быть быстрее - скорее, оно должно быть медленнее, потому что оно должно учитывать возможность того, что два диапазона данных перекрываются. Я думаю,std::copy
разрешает дублирование данных, и поэтому он должен вызыватьmemmove
.memcpy
. Это заставляет меня верить, что GCC проверяет, есть ли совпадение памяти.std::copy
позволяет перекрываться в одном направлении, но не в другом. Начало вывода не может находиться в пределах диапазона ввода, но начало ввода может находиться в пределах диапазона вывода. Это немного странно, потому что порядок назначений определен, и вызов может быть UB, даже если эффект этих назначений в этом порядке определен. Но я полагаю, что ограничение позволяет оптимизировать векторизацию.Всегда использовать
std::copy
потому , чтоmemcpy
ограничивается только C-стиле POD структур, и компилятор, вероятно , заменить вызовыstd::copy
с ,memcpy
если цели, на самом деле POD.Кроме того,
std::copy
может использоваться со многими типами итераторов, а не только с указателями.std::copy
является более гибким без потери производительности и явным победителем.источник
std::copy(container.begin(), container.end(), destination);
скопирует содержимоеcontainer
(все междуbegin
иend
) в буфер, указанный какdestination
.std::copy
не требует, как махинации&*container.begin()
или&container.back() + 1
.Теоретически,
memcpy
может иметь небольшое , незаметное , бесконечно малое преимущество в производительности, только потому, что оно не имеет тех же требований, что иstd::copy
. Со страницы руководстваmemcpy
:Другими словами,
memcpy
можно игнорировать возможность перекрытия данных. (Передача перекрывающихся массивовmemcpy
- неопределенное поведение.) Поэтомуmemcpy
нет необходимости явно проверять это условие, в то время какstd::copy
его можно использовать, еслиOutputIterator
параметр не находится в исходном диапазоне. Обратите внимание, что это не то же самое, что сказать, что исходный диапазон и целевой диапазон не могут перекрываться.Так как
std::copy
требования к нему несколько иные, теоретически он должен быть немного (с чрезмерным акцентом на немного ) медленнее, поскольку он, вероятно, будет проверять наличие перекрывающихся C-массивов или делегировать копирование C-массивовmemmove
, что необходимо для выполнения чек. Но на практике вы (и большинство профилировщиков), вероятно, даже не обнаружите никакой разницы.Конечно, если вы не работаете с POD , вы все
memcpy
равно не сможете их использовать .источник
std::copy<char>
. Ноstd::copy<int>
можно предположить, что его входы являются внутренними. Это будет иметь гораздо большее значение, потому что это влияет на каждый элемент. Перекрытие - это разовая проверка.memcpy
которые я видел, проверяют выравнивание и пытаются копировать слова, а не побайтно.memcpy
интерфейс он теряет информацию о выравнивании. Следовательно,memcpy
должен выполнять проверки выравнивания во время выполнения, чтобы обрабатывать невыровненные начала и конца. Эти чеки могут быть дешевыми, но они не бесплатны. Принимая во внимание, чтоstd::copy
можно избежать этих проверок и векторизовать. Кроме того, компилятор может доказать, что массивы источника и назначения не перекрываются и снова векторизируются без необходимости выбора между пользователемmemcpy
иmemmove
.Мое правило простое. Если вы используете C ++, предпочитайте библиотеки C ++, а не C :)
источник
std::end(c_arr)
вместоc_arr + i_hope_this_is_the_right_number_of elements
безопаснее? и, возможно, что еще важнее, понятнее. И в этом конкретном случае я бы хотел подчеркнуть следующее:std::copy()
он более идиоматичен, более понятен, если типы итераторов изменяются позже, приводит к более четкому синтаксису и т. Д.std::copy
безопаснее, потому что он правильно копирует переданные данные, если они не являются POD-типами.memcpy
с удовольствием скопируетstd::string
объект в новое представление побайтно.Небольшое дополнение: разница в скорости между
memcpy()
иstd::copy()
может варьироваться в зависимости от того, включена оптимизация или нет. С g ++ 6.2.0 и без оптимизацийmemcpy()
явно выигрывает:Когда оптимизация включена (
-O3
), все выглядит примерно так же:Чем больше массив, тем менее заметен эффект, но даже в
N=1000
memcpy()
два раза быстрее, когда оптимизация не включена.Исходный код (требуется Google Benchmark):
источник
Если вам действительно нужна максимальная производительность копирования (чего у вас нет), не используйте ни одну из них .
Можно многое сделать для оптимизации копирования памяти - даже больше, если вы хотите использовать для этого несколько потоков / ядер. Смотрите, например:
Чего не хватает / неоптимально в этой реализации memcpy?
и вопрос, и некоторые ответы предложили варианты реализации или ссылки на реализации.
источник
Профилирование показывает это утверждение:
std::copy()
всегда так быстро, какmemcpy()
или быстрее, ложно.Моя система:
Код (язык: c ++):
Red Alert указал, что код использует memcpy из массива в массив и std :: copy из массива в вектор. Это может быть причиной для более быстрого memcpy.
Так как есть
v.reserve (SizeOf (arr1));
не должно быть никакой разницы в копировании в вектор или массив.
Код исправлен для использования массива в обоих случаях. memcpy еще быстрее:
источник
std::copy
с вектора на массив каким-то образом заставилоmemcpy
занять почти вдвое больше времени? Эти данные весьма подозрительны. Я скомпилировал ваш код, используя gcc с -O3, и сгенерированная сборка одинакова для обоих циклов. Таким образом, любая разница во времени, которую вы наблюдаете на своей машине, является случайной.