[textField setKeyboardType:UIKeyboardTypeNumberPad];
По крайней мере, есть неявная ветвь, которую нужно скопировать вперед или назад для memmove()
, если компилятор не может сделать вывод, что перекрытие невозможно. Это означает, что без возможности оптимизации в пользу memcpy()
, memmove()
как минимум медленнее одной ветвью, а любое дополнительное пространство занято встроенными инструкциями для обработки каждого случая (если встраивание возможно).
Чтение кода eglibc-2.11.1
для memcpy()
и memmove()
подтверждает это как подозрение. Кроме того, нет возможности копирования страниц при обратном копировании, существенное ускорение доступно только в том случае, если нет шансов для наложения.
Подводя итог, это значит: если вы можете гарантировать, что регионы не перекрываются, то выбор memcpy()
вместо memmove()
позволяет избежать ветвления. Если источник и назначение содержат соответствующие области, выровненные по размеру страницы и по размеру страницы, и не перекрываются, некоторые архитектуры могут использовать аппаратно ускоренные копии для этих областей, независимо от того, вызвали ли вы memmove()
или memcpy()
.
На самом деле есть еще одно отличие от предположений и наблюдений, которые я перечислил выше. Начиная с C99, следующие прототипы существуют для 2 функций:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
void *memmove(void * s1, const void * s2, size_t n);
Из-за возможности предположить, что 2 указателя s1
и s2
не указывают на перекрывающуюся память, прямые реализации C в memcpy
могут использовать это для генерации более эффективного кода без обращения к ассемблеру, подробнее см. здесь . Я уверен, что memmove
может сделать это, однако дополнительные проверки потребуются выше тех, которые я видел в eglibc
, означая, что затраты производительности могут быть немного больше, чем одна ветвь для реализаций этих функций на Си.
Вполне возможно, что в большинстве реализаций стоимость вызова функции memmove () не будет значительно выше, чем memcpy () в любом сценарии, в котором определяется поведение обоих. Однако есть еще два момента, которые еще не упомянуты:
mov esi,[src] mov edi,[dest] mov ecx,1234/4 ; Compiler could notice it's a constant cld rep movslЭто займет столько же встроенного кода, но будет выполнено намного быстрее, чем:
push [src] push [dest] push dword 1234 call _memcpy ... _memcpy: push ebp mov ebp,esp mov ecx,[ebp+numbytes] test ecx,3 ; See if it's a multiple of four jz multiple_of_four multiple_of_four: push esi ; Can't know if caller needs this value preserved push edi ; Can't know if caller needs this value preserved mov esi,[ebp+src] mov edi,[ebp+dest] rep movsl pop edi pop esi ret
Многие компиляторы будут выполнять такие оптимизации с помощью memcpy (). Я не знаю ни одного, который будет делать это с memmove, хотя в некоторых случаях оптимизированная версия memcpy может предлагать ту же семантику, что и memmove. Например, если numbytes было 20:
; Assuming values in eax, ebx, ecx, edx, esi, and edi are not needed mov esi,[src] mov eax,[esi] mov ebx,[esi+4] mov ecx,[esi+8] mov edx,[esi+12] mov edi,[esi+16] mov esi,[dest] mov [esi],eax mov [esi+4],ebx mov [esi+8],ecx mov [esi+12],edx mov [esi+16],edi
Это будет работать правильно, даже если диапазоны адресов перекрываются, поскольку эффективно делает копию (в регистрах) всей области, которую нужно переместить, прежде чем какая-либо из них будет написано. Теоретически, компилятор мог бы обработать memmove (), увидев, что если его использовать как memcpy (), получится реализация, которая будет безопасна, даже если диапазоны адресов перекрываются, и вызовет _memmove в тех случаях, когда подстановка реализации memcpy () не будет безопасный. Я не знаю ни одного, кто делает такую оптимизацию, хотя.
Ну, memmove
приходится копировать в обратном направлении, когда источник и место назначения перекрываются, и источник перед пунктом назначения. Таким образом, некоторые реализации memmove
просто копируют назад, когда источник находится перед адресатом, независимо от того, перекрываются ли эти две области.
Качественная реализация memmove
может обнаруживать, перекрываются ли регионы, и делать прямое копирование, если они этого не делают. В таком случае единственные дополнительные издержки по сравнению с memcpy
- это просто проверки перекрытия.