Какой самый быстрый переносимый способ скопировать массив в C ++ - PullRequest
16 голосов
/ 13 сентября 2010

Этот вопрос беспокоит меня уже некоторое время. Возможности, которые я рассматриваю:

  1. memcpy
  2. станд :: копия
  3. cblas_dcopy

Кто-нибудь знает, каковы плюсы и минусы этих трех? Другие предложения также приветствуются.

Ответы [ 8 ]

26 голосов
/ 13 сентября 2010

В C ++ вы должны использовать std :: copy по умолчанию, если у вас нет веских причин поступить иначе.Причина в том, что классы C ++ определяют свою собственную семантику копирования через конструктор копирования и оператор присваивания копии, и из перечисленных операций только std :: copy соблюдает эти соглашения.

memcpy () использует raw, побайтнокопия данных (хотя, вероятно, сильно оптимизированная для размера строки кэша и т. д.) и игнорирует семантику копирования C ++ (в конце концов, это функция C ...).

cblas_dcopy () - специализированная функция для использованияв подпрограммах линейной алгебры с использованием значений с плавающей запятой двойной точности.Это, вероятно, превосходит все это, но не должно рассматриваться как универсальное.

Если ваши данные являются «простыми» структурными данными типа POD или необработанными данными фундаментального типа, memcpy, скорее всего, будет работать так же быстро, как вы можете получить.Скорее всего, std :: copy будет оптимизирован для использования memcpy в этих ситуациях, поэтому вы никогда не узнаете разницу.

Короче, используйте std :: copy ().

1 голос
/ 13 сентября 2010

Используйте std :: copy, если профилирование не покажет вам необходимую выгоду, если вы поступите иначе. Он учитывает инкапсуляцию объектов C ++, вызывая конструкторы копирования и операторы присваивания, и реализация может включать в себя другие встроенные оптимизации, такие как избегание вызова функции out-of-line для memcpy (), если размер известен во время компиляции и слишком мал для обосновать накладные расходы на вызов функции. (Некоторые системы могут иметь макросы memcpy, которые делают аналогичные определения, но в целом компилятор C ++ будет лучше понимать, какие оптимизации функционально эквивалентны.)

FWIW / на старом Linux-боксе, который мне удобен, GCC не выполняет каких-либо впечатляющих оптимизаций, но бит / type_traits.h позволяет программе легко указать, должен ли std :: copy переходить на memcpy ():

 * Copyright (c) 1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and            
 * that both that copyright notice and this permission notice appear            
 * in supporting documentation.  Silicon Graphics makes no                      
 * representations about the suitability of this software for any               
 * purpose.  It is provided "as is" without express or implied warranty.        
 ...                                                                            

/*                                                                              
This header file provides a framework for allowing compile time dispatch        
based on type attributes. This is useful when writing template code.            
For example, when making a copy of an array of an unknown type, it helps        
to know if the type has a trivial copy constructor or not, to help decide       
if a memcpy can be used.

The class template __type_traits provides a series of typedefs each of
which is either __true_type or __false_type. The argument to
__type_traits can be any type. The typedefs within this template will
attain their correct values by one of these means:
    1. The general instantiation contain conservative values which work
       for all types.
    2. Specializations may be declared to make distinctions between types.
    3. Some compilers (such as the Silicon Graphics N32 and N64 compilers)
       will automatically provide the appropriate specializations for all
       types.

EXAMPLE:

//Copy an array of elements which have non-trivial copy constructors
template <class _Tp> void
  copy(_Tp* __source,_Tp* __destination,int __n,__false_type);
//Copy an array of elements which have trivial copy constructors. Use memcpy.
template <class _Tp> void
  copy(_Tp* __source,_Tp* __destination,int __n,__true_type);

//Copy an array of any type by using the most efficient copy mechanism
template <class _Tp> inline void copy(_Tp* __source,_Tp* __destination,int __n) {
   copy(__source,__destination,__n,
        typename __type_traits<_Tp>::has_trivial_copy_constructor());
}
*/
1 голос
/ 13 сентября 2010

memcpy, вероятно, самый быстрый способ скопировать непрерывный блок памяти. Это потому, что он, вероятно, будет высоко оптимизирован для вашего конкретного оборудования. Это часто реализуется как встроенная функция компилятора.

Сказав это, и объект, не являющийся POD C ++, вряд ли будет смежным, и поэтому копирование массивов объектов C ++ с использованием memcpy может дать вам неожиданные результаты. При копировании массивов (или коллекций) объектов C ++ std::copy будет использовать собственную семантику копирования объекта и поэтому подходит для использования с объектами C ++, отличными от POD.

cblas_dcopy выглядит как копия для использования с определенной библиотекой и, вероятно, имеет мало смысла, когда не использует эту библиотеку.

1 голос
/ 13 сентября 2010

memcpy , однако, если ваш массив содержит нетривиальные объекты, используйте std :: copy .

1 голос
/ 13 сентября 2010

В большинстве случаев memcpy будет самым быстрым, так как это самый низкий уровень и может быть реализован в машинном коде на данной платформе. (однако, если ваш массив содержит нетривиальные объекты, memcpy, возможно, не решит правильно, поэтому может быть безопаснее придерживаться std :: copy)

Однако все зависит от того, насколько хорошо stdlib имплантирован на данной платформе и т. Д. Поскольку в стандарте не указано, насколько быстрыми должны быть операции, в « portable » нет способа узнать, поскольку что будет самым быстрым.

Профилирование вашего приложения покажет пост на данной платформе, но расскажет вам только о тестовой платформе.

Однако, когда вы профилируете свое приложение, вы, скорее всего, обнаружите, что проблемы заключаются в вашем дизайне, а не в выборе метода копирования массива. (Например, зачем вам копировать большие массивы, чтобы они совпадали?)

0 голосов
/ 19 июня 2018

Я сделал небольшой тест (VS 2018 Preview, MKL 2017 Update 4) для сравнения memcpy и последовательной версии cblas_?copy и нашел, что они одинаково быстро работают на float и double.

0 голосов
/ 13 сентября 2010

Я должен думать, что другие вызовут memcpy ().Сказав, что я не могу поверить, что будет какая-то заметная разница.

Если это действительно важно для вас, кодируйте все три и запустите профилировщик, но может быть лучше рассмотреть такие вещи, как удобочитаемость / удобство обслуживания, исключение и т. Д. (И кодирование вставки на ассемблере, пока вына это, не то, что вы, вероятно, увидите разницу)

Есть ли в вашей программе многопоточность?

И, самое главное, как вы объявляете массив?(что это за массив) и насколько он велик?

0 голосов
/ 13 сентября 2010

Просто профилируйте свою заявку.Скорее всего, вы обнаружите, что копирование не самая медленная часть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...