Memcpy() обычно быстрее, чем strcpy()?

Является ли memcpy() обычно быстрее, чем strcpy() (на большинстве реальных платформ)? (Я предполагаю, что размер строки известен.)

Если я правильно помню ассемблер i386, есть loop инструкции, которые копируют заданное количество байтов или слов. Так что это самый быстрый способ, в то время как strcpy() реализация ассемблера i386 будет использовать ручную проверку '\0' в простом цикле.

Так что я чувствую, что на x86 memcpy() быстрее, чем strcpy().

А как насчет других архитектур?


person porton    schedule 26.07.2014    source источник
comment
memcpy может использовать преимущества › копирования по 1 символу за раз (ему известна длина); так что я бы предположил, что это будет быстрее (или, скорее, не медленнее), хотя они служат разным целям. См. fossies.org/dox/glibc-2.19/string_2memcpy_8c_source.html, danielvik.com/2010/02/fast-memcpy-in-c .html и т. д.   -  person user2864740    schedule 26.07.2014
comment
Будьте осторожны, на некоторых платформах memcpy работает только с выровненными указателями. Это особенно не так, если вы копируете подстроки (на основе индекса переменной). Таким образом, хотя memcpy более эффективен, его нельзя использовать во всех случаях (вы не заметите проблемы на Intel, поскольку он разрешает доступ без выравнивания (медленно)).   -  person eckes    schedule 26.07.2014
comment
@eckes: Что это будут за платформы?! Некоторые платформы могут иметь функции memcpy-ish, которые полезны только с выровненными указателями, но я не верю, что какая-либо соответствующая реализация memcpy может наложить какие-либо ограничения на src, кроме того, что он идентифицирует блок читаемой памяти достаточного размера, который не является псевдонимом. любая часть целевого блока.   -  person supercat    schedule 26.07.2014
comment
@supercat Да, на самом деле я был неправ, это не memcpy, у которого есть проблемы с невыровненным доступом, но вы должны быть осторожны, какие типы указателей вы используете с результатами или вводом. В случае strcopy у вас всегда есть указатели, выровненные по символам, которые менее эффективны, но более гибки. (Но я рад, что мне больше не нужно использовать C :)   -  person eckes    schedule 26.07.2014
comment
Код @eckes microsoft для memcpy много лет назад разбил копию на три части: невыровненный префикс, основную часть и невыровненный суффикс. Это означает, что вопросы выравнивания прозрачны для пользователя, а большая часть копии (основная часть) выполняется с максимальной скоростью выравнивания с использованием полноразмерных (например, 32-битных) передач.   -  person user3386109    schedule 26.07.2014
comment
Да, для того же количества перемещаемых байт memcpy, скорее всего, будет в несколько раз быстрее, чем strcpy. Единственными исключениями могут быть очень короткие операции, когда сложность настройки memcpy затмевает реальную копию.   -  person Hot Licks    schedule 26.07.2014
comment
Ваш вопрос бессмысленен или, в лучшем случае, неполный, поскольку strcpy и memcpy не взаимозаменяемы. Чтобы скопировать строку с помощью memcpy, вам нужно будет вызвать strlen, так что все сбережения будут потеряны.   -  person Jim Balter    schedule 26.07.2014
comment
Вероятно, есть какой-то причудливый код SSE, который намного быстрее, чем наивный цикл для strcpy. Hashcat содержит такой код для разбиения файла на строки, что является довольно похожей проблемой.   -  person CodesInChaos    schedule 26.07.2014
comment
@CodesInChaos: Полезность strcpy, которая эффективна с большими строками, немного ограничена по сравнению со значением быстрой memcpy, поскольку для повышения эффективности memcpy в случае больших копий требуется только добавить одно целочисленное сравнение к стоимости любого случая, когда SSE setup не будет генерировать чистую победу, но код не будет знать, будет ли целесообразна настройка SSE для strcpy, не зная длины строки, а код, который знает длину строки, обычно не будет использовать strcpy .   -  person supercat    schedule 06.04.2015
comment
loop — один из самых медленных способов, его больше никто не использует. Старые реализации libc используют rep movsb, тогда как более новые используют SIMD для ускорения. Почему сложные memcpy/memset лучше?   -  person phuclv    schedule 29.08.2018


Ответы (2)


Если вы знаете размер копируемых данных, то memcpy() должно быть таким же быстрым или быстрее, чем strcpy(). В противном случае memcpy() нельзя использовать отдельно, а strcpy() должно быть таким же быстрым или быстрее, чем strlen(), за которым следует memcpy().

Тем не мение...

Многие реализации memcpy() и/или strcpy() и/или strlen() предназначены для эффективной обработки больших объемов данных. Это часто означает дополнительные накладные расходы при запуске (например, определение выравнивания, настройка SIMD, управление кешем и т. д.) и делает эти реализации плохими (медленными) для копирования небольших объемов данных (что гораздо более вероятно в хорошо написанном коде). Из-за этого «должно быть так же быстро или быстрее» не обязательно означает «так же быстро или быстрее». Например, для небольших объемов данных memcpy(), оптимизированный для больших объемов данных, может быть значительно медленнее, чем strcpy(), не оптимизированный для больших объемов данных.

Также обратите внимание, что основная проблема здесь заключается в том, что общий код (например, memcpy() и strcpy()) не может быть оптимизирован для конкретного случая. Лучшим решением было бы иметь несколько функций, например. memcpy_small(), оптимизированный для копирования небольших объемов данных, и memcpy_large(), оптимизированный для плохого кода, который не удалось избежать копирования большого объема данных.

person Brendan    schedule 26.07.2014
comment
Я ожидаю, что версии memcpy(), оптимизированные для больших блоков, будут начинаться с проверки того, достаточно ли велик размер, чтобы сделать целесообразной оптимизацию больших блоков, и, если нет, просто использовать процедуру для небольших блоков. Может быть, когда, например. при копировании двухсимвольной строки стоимость strcpy оказывается меньше, чем у memcpy, но я бы не ожидал, что точка безубыточности будет намного выше этой. - person supercat; 06.04.2015
comment
С memcpy гораздо проще работать как с большими, так и с маленькими размерами, потому что размер известен заранее. strcpy должен избегать чтения на другой странице после конца строки и должен загружать + проверять некоторые исходные данные, прежде чем он сможет даже выбрать маленькую или большую стратегию. Реализации Glibc довольно хорошо оптимизированы для обоих, с быстрым путем, который хорошо работает для небольших. (Небольшие дополнительные накладные расходы при запуске менее значительны для больших копий.) Но да, memcpy_large может избежать некоторых условных ветвей, прежде чем перейти к стратегии больших копий. Помогает больше для strcpy! - person Peter Cordes; 29.08.2018
comment
код, копирующий большой объем данных, не обязательно является плохо написанным кодом. например, если вам нужно сериализовать большой объем данных, необходимость скопировать его на место, чтобы он соответствовал определенному формату, меня не удивит (например, сериализация 100 МБ данных в JSON или что-то еще, с оккационным символом, требующим \uescape , тогда вам, вероятно, придется копировать всю чертову штуку, даже, возможно, по одному символу utf8 за раз) - person hanshenrik; 05.01.2020
comment
@hanshenrik: Так много вариантов ... создайте копию при записи клона (fork()?), Так что большинство на самом деле не копируется, вообще не изменяйте его и только создавайте список различий, объединяйте оба из них (разделить его на блоки любого размера и отслеживать, какие блоки были изменены), ... - person Brendan; 06.01.2020

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

person supercat    schedule 26.07.2014
comment
Это зависит от того, говорите ли вы memcpy копировать весь буфер, или вы заранее знаете длину строки (возможно, она хранится отдельно в переменной), и вы можете указать memcpy копировать именно столько. - person Wyzard; 26.07.2014
comment
И ни memcpy(), ни strcpy() нельзя безопасно использовать, если вы не знаете доступное пространство в целевой области и пространство, используемое в исходной области. Таким образом, вы всегда должны использовать memcpy(), потому что вы всегда должны знать, с какой длиной строки имеете дело. - person Jonathan Leffler; 26.07.2014
comment
Что ж, strcpy безопасен, если вы знаете, что буфер назначения не меньше размера исходного буфера, даже если вы не знаете длину фактической строки. (Предполагая, что строка не выходит за конец исходного буфера, в этом случае у вас возникнет проблема еще до вызова strcpy.) - person Wyzard; 26.07.2014
comment
Верно. Существует множество реальных ситуаций, когда вы знаете границу длины исходной строки, но не знаете точную длину. Например, это может быть строка, полученная с помощью fgets, конец строки известной длины, полученной с помощью strchr или strstr, и т. д. - person R.. GitHub STOP HELPING ICE; 26.07.2014
comment
Пока строка короткая, выполнение strlen с последующим memcpy, вероятно, сравнимо по производительности с strcpy. Однако для огромных строк чтение строки дважды было бы очень дорого. - person R.. GitHub STOP HELPING ICE; 26.07.2014
comment
В любом ответе на вопрос OP следует отметить, что strcpy и memcpy не являются взаимозаменяемыми, поэтому вопрос неполный... это зависит от того, как определяется длина и/или содержат ли данные NUL. - person Jim Balter; 26.07.2014
comment
@JimBalter: эти два будут взаимозаменяемы в случаях, когда у одного есть известное количество ненулевых байтов, за которыми следует нулевой байт; на самом деле это довольно распространенная ситуация, учитывая, что многие библиотеки строк с отслеживанием длины добавляют необоснованный нулевой байт после каждой строки. - person supercat; 06.04.2015
comment
Я сказал, что это зависит от... того, содержат ли данные NUL. Шиш. - person Jim Balter; 06.04.2015