Предположим, что кто-то хочет сделать копию массива, объявленного как
DATA_TYPE src[N];
Всегда ли memcpy
так же быстро или быстрее, чем следующий фрагмент кода, независимо от того, что такое DATA_TYPE
и количество элементов массива?
DATA_TYPE dest[N];
for (int i=0; i<N; i++)
dest[i] = src[i];
Для маленького типа, такого как char
, и большого N
мы можем быть уверены, что memcpy
быстрее (если только компилятор не заменит цикл вызовом memcpy
). Но что, если тип больше, например double
, и/или количество элементов массива мало?
Этот вопрос возник у меня при копировании множества массивов double
, каждый из которых состоит из 3 элементов.
Я не нашел ответа на свой вопрос в ответе на другой вопрос, упомянутый Вольстадом в комментариях. Принятый ответ на этот вопрос, по сути, говорит: «Оставьте это на усмотрение компилятора». Это не тот ответ, который я ищу. Тот факт, что компилятор может оптимизировать копирование памяти, выбрав одну альтернативу, не является ответом. Почему и когда один из вариантов быстрее? Возможно, компиляторы знают ответ, но разработчики, включая разработчиков компиляторов, не знают!
@wohlstad, спасибо. Я читаю это.
Используйте www.godbolt.org для проверки альтернативных кодов. Скорее всего, оптимизатор создаст почти одинаковую сборку для обоих.
@wohlstad, принятый ответ на этот вопрос, по сути, говорит: «Оставьте это на усмотрение компилятора». Это не тот ответ, который я ищу.
@wohlstad, тот факт, что компилятор может оптимизировать копирование памяти, выбрав одну альтернативу, не является ответом. Почему и когда один из вариантов быстрее? Возможно, компиляторы знают ответ, но разработчики, включая разработчиков компиляторов, не знают!
Во многих реализациях memcpy
есть оптимизации для копирования элементов размером слова на границах слов. Например, если вы DATA_TYPE
были байтом, то ваш ручной цикл, предполагающий отсутствие оптимизации, будет выполнять N итераций. На 64-битной машине memcpy может выполнить всю работу за N/8 итераций — копирование более 64-бит за раз.
Какой ответ находятся вы ищете? Нет серебряной пули, которая «лучше». Задействованные переменные слишком разнообразны и зависят от ситуации. Единственный способ узнать определенный — это создать свой оптимизированный asm, подсчитать тактовые циклы и остановки конвейера, включая потенциальные ошибки прогнозирования ветвлений, локальность кеша и, прежде всего, мера.
@WhozCraig, правильно, но обычно есть общие правила.
"для мелкого типа... мы можем быть уверены, что memcpy
быстрее" - Нет, нельзя.
Общее правило таково: если у вас нет особой потребности в оптимизации, пишите код так, чтобы его было как можно проще читать и поддерживать. Когда возникает потребность в оптимизации, решение сильно зависит от целевой системы.
Поскольку memcpy
является библиотечной функцией, она полностью зависит от реализации библиотеки, насколько она эффективна на самом деле, и окончательный ответ невозможен.
Тем не менее, любая предоставленная стандартная библиотека, вероятно, будет сильно оптимизирована и может даже использовать специфические аппаратные функции, такие как передача DMA. Принимая во внимание, что производительность вашего цикла кода будет варьироваться в зависимости от настроек оптимизации, поэтому, вероятно, она будет работать намного хуже в неоптимизированных сборках отладки.
Еще одно соображение заключается в том, что производительность memcpy()
не зависит от типа данных и, как правило, детерминированный, тогда как производительность вашего цикла, вероятно, будет зависеть от DATA_TYPE
или даже от значения N
.
Как правило, я ожидаю, что memcpy()
будет оптимальным и быстрым или таким же быстрым, как цикл присваивания, и, безусловно, более последовательным и детерминированным, не зависящим от конкретных настроек компилятора и даже от используемого компилятора.
В конце концов, единственный способ сказать это — измерить его для вашей конкретной платформы, набора инструментов, библиотеки и вариантов сборки, а также для различных типов данных. В конечном счете, поскольку вам пришлось бы измерять его для каждой комбинации использования до знать, если бы он был быстрее, я полагаю, что это, как правило, пустая трата времени и представляет только академический интерес — используйте библиотеку — не только для производительности и согласованности, но и для ясность и ремонтопригодность.
Вы бы сами использовали memcpy
для копирования массива из 3 double
?
@apadana Возможно, не потому, что для этого накладные расходы на вызов функции были бы значительными, но я бы, вероятно, не использовал цикл по той же причине. Хотя оптимизатор может развернуть такой цикл, это ни в коем случае не является данностью, и развертывание его самостоятельно приведет к повышению производительности в неоптимизированных сборках.
Хороший ответ. Теперь, когда вы упомянули DMA, забавно, что передача DMA может - в зависимости от платформы - быть медленнее, чем, например. memcpy()
, но тем временем это позволит процессору заниматься другими делами. В таком случае «быстрее» зависит от того, может ли приложение использовать эту разгрузку.
@apadana: в общем, я бы не стал париться по мелочам. Такие микрооптимизации редко бывают продуктивными или необходимыми, и их трудно обобщить. Единственный раз, когда я помню, что мне нужно было беспокоиться о подобных вещах, это код DSP в системе жесткого реального времени на основе микроконтроллера. И там я протестировал и профилировал различные реализации, чтобы определить лучшее решение для этого конкретного приложения на этой конкретной платформе ant toolchain.
@nielsen: да, на практике я сомневаюсь, что реализация будет использовать DMA, поскольку она менее детерминирована и не переносима. Также для небольших копий время установки будет непомерно высоким. Вместо этого могут быть предоставлены специализированные функции копирования памяти DMA.
Отвечает ли это на ваш вопрос? memcpy против присваивания в C