Когда я смогу повысить производительность с помощью memcpy
или чем я могу воспользоваться? Например:
float a[3]; float b[3];
это код:
memcpy(a, b, 3*sizeof(float));
быстрее, чем этот?
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
Когда я смогу повысить производительность с помощью memcpy
или чем я могу воспользоваться? Например:
float a[3]; float b[3];
это код:
memcpy(a, b, 3*sizeof(float));
быстрее, чем этот?
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
Эффективность не должна быть вашей заботой.
Пишите чистый поддерживаемый код.
Меня беспокоит, что так много ответов указывают на неэффективность memcpy (). Он разработан как наиболее эффективный способ копирования блоков памяти (для программ на языке C).
Поэтому в качестве теста я написал следующее:
#include <algorithm>
extern float a[3];
extern float b[3];
extern void base();
int main()
{
base();
#if defined(M1)
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
#elif defined(M2)
memcpy(a, b, 3*sizeof(float));
#elif defined(M3)
std::copy(&a[0], &a[3], &b[0]);
#endif
base();
}
Затем для сравнения код производит:
g++ -O3 -S xr.cpp -o s0.s
g++ -O3 -S xr.cpp -o s1.s -DM1
g++ -O3 -S xr.cpp -o s2.s -DM2
g++ -O3 -S xr.cpp -o s3.s -DM3
echo "=======" > D
diff s0.s s1.s >> D
echo "=======" >> D
diff s0.s s2.s >> D
echo "=======" >> D
diff s0.s s3.s >> D
Это привело к: (комментарии добавлены вручную)
======= // Copy by hand
10a11,18
> movq _a@GOTPCREL(%rip), %rcx
> movq _b@GOTPCREL(%rip), %rdx
> movl (%rdx), %eax
> movl %eax, (%rcx)
> movl 4(%rdx), %eax
> movl %eax, 4(%rcx)
> movl 8(%rdx), %eax
> movl %eax, 8(%rcx)
======= // memcpy()
10a11,16
> movq _a@GOTPCREL(%rip), %rcx
> movq _b@GOTPCREL(%rip), %rdx
> movq (%rdx), %rax
> movq %rax, (%rcx)
> movl 8(%rdx), %eax
> movl %eax, 8(%rcx)
======= // std::copy()
10a11,14
> movq _a@GOTPCREL(%rip), %rsi
> movl $12, %edx
> movq _b@GOTPCREL(%rip), %rdi
> call _memmove
Добавлены результаты по времени для выполнения вышеуказанного внутри цикла 1000000000
.
g++ -c -O3 -DM1 X.cpp
g++ -O3 X.o base.o -o m1
g++ -c -O3 -DM2 X.cpp
g++ -O3 X.o base.o -o m2
g++ -c -O3 -DM3 X.cpp
g++ -O3 X.o base.o -o m3
time ./m1
real 0m2.486s
user 0m2.478s
sys 0m0.005s
time ./m2
real 0m1.859s
user 0m1.853s
sys 0m0.004s
time ./m3
real 0m1.858s
user 0m1.851s
sys 0m0.006s
_memmove
не встроен?
- person Konrad Rudolph; 28.12.2010
std::copy
легче читать. Он также самый эффективный (равный memcpy). См. Прилагаемую сборку и, поскольку вы, очевидно, сами не рассчитали время, результаты синхронизации.
- person Martin York; 17.08.2015
std::copy
лучше, поскольку он будет использовать наиболее эффективную и действенную технику.
- person Martin York; 17.08.2015
std::array<float, 3>
, которое имеет имеет оператор присваивания, объединяет лучшее из обоих миров: удобочитаемость и эффективность. И, помимо прочего, у него есть дополнительное качество, заключающееся в том, что он не распадается на указатель. Кроме того, на момент написания и GCC 5.2, и Clang 3.7 генерируют идентичный код во всех случаях, поэтому производительность больше не актуальна, и следует отдавать предпочтение удобочитаемости.
- person user703016; 02.09.2015
memmove
не имел бы накладных расходов по сравнению с оптимизированным и встроенным memcpy
случаем.
- person user239558; 27.05.2016
std::copy
производит самый быстрый код? Учитывая способность компилятора анализировать и внедрять лучший код, кажется избыточно очевидным, что std::copy
будет самым быстрым методом (или, по крайней мере, не медленнее, чем метод, который вы можете использовать вручную).
- person Martin York; 27.05.2016
memmove
, который в дополнение к вышеупомянутому требованию проверки на перекрытие указателей, мог бы когда-либо быть таким же быстрым, как встроенный memcpy
. Это подделка.
- person user239558; 30.05.2016
Вы можете использовать memcpy
только в том случае, если копируемые объекты не имеют явных конструкторов, как и их члены (так называемые POD, «Обычные старые данные»). Так что можно вызывать memcpy
для float
, но нельзя, например, для std::string
.
Но часть работы уже сделана за вас: std::copy
из <algorithm>
специализируется на встроенных типах (и, возможно, для любого другого типа POD - зависит от реализации STL). Таким образом, запись std::copy(a, a + 3, b)
выполняется так же быстро (после оптимизации компилятора), как memcpy
, но менее подвержена ошибкам.
std::copy
правильно находится в <algorithm>
; <algorithm.h>
строго для обратной совместимости.
- person Karl Knechtel; 28.12.2010
Компиляторы специально оптимизируют memcpy
вызовы, по крайней мере, clang & gcc. Так что вы должны предпочесть его везде, где можете.
memcpy
, но вряд ли это будет быстрее, чем второй подход. Пожалуйста, прочтите сообщение Симоны.
- person Nawaz; 28.12.2010
Не делайте преждевременных микрооптимизаций, таких как использование memcpy, подобного этому. Использование присваивания более четкое и менее подверженное ошибкам, и любой достойный компилятор сгенерирует достаточно эффективный код. Если и только если вы профилировали код и обнаружили, что назначения являются значительным узким местом, тогда вы можете рассмотреть возможность какой-то микрооптимизации, но в целом вы всегда должны писать четкий и надежный код в первую очередь.
memcpy
? memcpy(a, b, sizeof a)
яснее, потому что при изменении размера a
и b
вам не нужно добавлять / удалять назначения.
- person Chris Lutz; 28.12.2010
memcpy
не сломается (трюк sizeof a
сломается, но только некоторые люди его используют). Точно так же и std::copy
, который явно превосходит их почти во всех отношениях.
- person Chris Lutz; 28.12.2010
Используйте 1_. В заголовочном файле для g++
примечания:
Эта встроенная функция по возможности сводится к вызову @c memmove.
Наверное, Visual Studio не сильно отличается. Идите обычным путем и оптимизируйте, как только вы заметите бутылочное горлышко. В случае простой копии компилятор, вероятно, уже оптимизирует для вас.
Преимущества memcpy? Наверное читабельность. В противном случае вам пришлось бы либо выполнить несколько назначений, либо создать цикл for для копирования, ни одно из которых не является таким простым и понятным, как просто выполнение memcpy (конечно, если ваши типы просты и не требуют построения / разрушение).
Кроме того, memcpy обычно относительно оптимизирован для конкретных платформ до такой степени, что он не будет намного медленнее, чем простое присваивание, а может даже быть быстрее.
Предположительно, как сказал Наваз, версия с назначением должна быть быстрее на большинстве платформ. Это потому, что memcpy()
будет копировать байт за байтом, в то время как вторая версия может копировать 4 байта за раз.
Как всегда, вы всегда должны профилировать приложения, чтобы быть уверенным, что то, что вы ожидаете стать узким местом, соответствует действительности.
Изменить
То же самое относится и к динамическому массиву. Поскольку вы упомянули C ++, в этом случае вам следует использовать алгоритм std::copy()
.
Изменить
Это выходной код для Windows XP с GCC 4.5.0, скомпилированный с флагом -O3:
extern "C" void cpy(float* d, float* s, size_t n)
{
memcpy(d, s, sizeof(float)*n);
}
Я выполнил эту функцию, потому что OP также указал динамические массивы.
Выходная сборка следующая:
_cpy:
LFB393:
pushl %ebp
LCFI0:
movl %esp, %ebp
LCFI1:
pushl %edi
LCFI2:
pushl %esi
LCFI3:
movl 8(%ebp), %eax
movl 12(%ebp), %esi
movl 16(%ebp), %ecx
sall $2, %ecx
movl %eax, %edi
rep movsb
popl %esi
LCFI4:
popl %edi
LCFI5:
leave
LCFI6:
ret
конечно, я предполагаю, что все эксперты здесь знают, что означает rep movsb
.
Это версия задания:
extern "C" void cpy2(float* d, float* s, size_t n)
{
while (n > 0) {
d[n] = s[n];
n--;
}
}
что дает следующий код:
_cpy2:
LFB394:
pushl %ebp
LCFI7:
movl %esp, %ebp
LCFI8:
pushl %ebx
LCFI9:
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
movl 16(%ebp), %eax
testl %eax, %eax
je L2
.p2align 2,,3
L5:
movl (%ecx,%eax,4), %edx
movl %edx, (%ebx,%eax,4)
decl %eax
jne L5
L2:
popl %ebx
LCFI10:
leave
LCFI11:
ret
Которая перемещает 4 байта за раз.
memcpy
реализации эффективны, а разработчики компиляторов потратили столько же времени на то, чтобы их компиляторы искали случаи, когда memcpy
мог бы сделать назначения быстрее, и наоборот. Ваш аргумент может быть настолько плохим, насколько вы этого хотите, а также ваша необычная реализация - отвлекающий маневр. Посмотрите, как это реализовано в GCC или других компиляторах / libc. Возможно, для вас этого будет достаточно быстро.
- person Chris Lutz; 28.12.2010
memcpy
, который может копировать только байт за раз?
- person jalf; 28.12.2010
memcpy
требуется для копирования одного байта. Конечно, он может проверять, кратен ли размер для копирования 4 или 8, но с назначениями вы можете пропустить проверку и получить более быстрый код.
- person Simone; 28.12.2010
memcpy
, когда это возможно.
- person Chris Lutz; 28.12.2010
while
для выполнения назначений и выполнение назначения вручную с использованием постоянного смещения НЕ одинаково по скорости. Последнее обычно быстрее.
- person Nawaz; 28.12.2010