Я'м пишу внутренний цикл, который должен поставить структуры в непрерывной памяти. Я не'т знаю, сколько эти структуры будут загодя. Моя проблема заключается в том, что стл'ы
векторинициализирует его значениями в 0, так что неважно, что я делаю, я беру стоимость инициализации плюс стоимость установки
структура`'s членов их значения.
Есть ли способ, чтобы предотвратить инициализацию, или есть в STL-контейнер, как там с перемещаемом непрерывного хранения и неинициализированные элементы?
(Я'м уверена, что эта часть кода должна быть оптимизирована, и я'м уверены, что инициализация-это значительная статья расходов.)
Кроме того, см. Мой комментарий ниже для получения разъяснений о том, когда инициализация происходит.
НЕКОТОРЫЕ КОД:
void GetsCalledALot(int* data1, int* data2, int count) {
int mvSize = memberVector.size()
memberVector.resize(mvSize + count); // causes 0-initialization
for (int i = 0; i < count; ++i) {
memberVector[mvSize + i].d1 = data1[i];
memberVector[mvSize + i].d2 = data2[i];
}
}
СТД::вектор должен инициализировать значения в массиве, то, значит, у кого-конструктор (или копию-конструктор) должен быть вызван. Поведение "вектор" (или любой контейнер, класс) неопределено, если вы были, чтобы получить доступ к неинициализированной секции массива, как если бы он был инициализирован.
Лучшим способом является использование резерва () и push_back()`, так что копия-конструктор используется, во избежание дефолта-строительство.
Используя Ваш пример кода:
struct YourData {
int d1;
int d2;
YourData(int v1, int v2) : d1(v1), d2(v2) {}
};
std::vector<YourData> memberVector;
void GetsCalledALot(int* data1, int* data2, int count) {
int mvSize = memberVector.size();
// Does not initialize the extra elements
memberVector.reserve(mvSize + count);
// Note: consider using std::generate_n or std::copy instead of this loop.
for (int i = 0; i < count; ++i) {
// Copy construct using a temporary.
memberVector.push_back(YourData(data1[i], data2[i]));
}
}
Единственная проблема, с запасом звонит ()
(или изменить размер()
) как это, что вы можете в конечном итоге вызывая командлет copy-конструктор чаще, чем нужно. Если вы можете сделать хороший прогноз, так как на конечный размер массива, он's лучше, чтобы резерв()` пространство, один раз в начале. Если вы Don'т знаем конечный размер, хотя, по крайней мере, число копий будет минимальный в среднем.
В текущей версии C++, внутренний цикл-это немного неэффективно, как временная ценность создается на стеке, копии-построено в памяти векторов, и, наконец, временно разрушается. Однако в следующей версии C++ есть функция, которая называется R-значение ссылки (Т&&
), которая поможет.
Интерфейс поставляется `СТД::вектор не допускает и другой вариант, который используют некоторые фабрики класса для построения значений по умолчанию. Вот грубый пример того, что этот шаблон будет выглядеть реализована на языке C++:
template <typename T>
class my_vector_replacement {
// ...
template <typename F>
my_vector::push_back_using_factory(F factory) {
// ... check size of array, and resize if needed.
// Copy construct using placement new,
new(arrayData+end) T(factory())
end += sizeof(T);
}
char* arrayData;
size_t end; // Of initialized data in arrayData
};
// One of many possible implementations
struct MyFactory {
MyFactory(int* p1, int* p2) : d1(p1), d2(p2) {}
YourData operator()() const {
return YourData(*d1,*d2);
}
int* d1;
int* d2;
};
void GetsCalledALot(int* data1, int* data2, int count) {
// ... Still will need the same call to a reserve() type function.
// Note: consider using std::generate_n or std::copy instead of this loop.
for (int i = 0; i < count; ++i) {
// Copy construct using a factory
memberVector.push_back_using_factory(MyFactory(data1+i, data2+i));
}
}
Этим означает, что вы должны создать свой собственный класс Vector. В этом случае она также усложняет то, что должно было быть простой пример. Но могут быть случаи, когда с помощью функции фабрики, как это лучше, например, если вставить условно на некоторое другое значение, и вы должны были бы в противном случае безоговорочно построить какое-нибудь дорогостоящее временное, даже если бы это было'т действительно необходимо.
В C++0х добавляет новый шаблон функции-члена emplace_back
до Вектор
(которая опирается на шаблоны с переменным числом аргументов и точная пересылка), что позволяет избавиться от любых временных целиком:
memberVector.emplace_back(data1[i], data2[i]);
Чтобы уточнить на резерв() ответы: вам нужно использовать резерв() в сочетании с push_back(). Таким образом, конструктор по умолчанию не вызывается для каждого элемента, а конструктор копирования. Вы все равно понесете наказание за настройку структуры на стеке, а затем скопировать его в вектор. С другой стороны, это's возможно, что если вы используете
vect.push_back(MyStruct(fieldValue1, fieldValue2))
компилятор будет создавать новый экземпляр непосредственно в thatbelongs памяти для вектора. Это зависит от того, как умный оптимизатор. Вам нужно проверить сгенерированный код, чтобы узнать.
В C++11 (и увеличения) можно использовать массив версия `unique_ptr не выделять неинициализированный массив. Это'т достаточно контейнеров STL, но все же управляемой памяти и C++-иш чего будет достаточно для многих приложений.
auto my_uninit_array = std::unique_ptr<mystruct[]>(new mystruct[count]);
Так вот'проблема с resize-это призвание вставка, которая делает конструкцию копирование из умолчанию созданный элемент для каждого из вновь добавленных элементов. Чтобы получить это значение в 0 Стоимость вам нужно написать свой собственный конструктор по умолчанию и свой собственный конструктор копирования в качестве пустой функции. Делаю это в ваш конструктор копирования-это очень плохая идея так как это приведет к СТД::вектор'алгоритмы внутреннего перераспределения с.
Резюме: вы'вновь не собираюсь быть в состоянии сделать это с std::вектор.
Эээ...
попробуйте способ:
std::vector<T>::reserve(x)
Это позволит вам зарезервировать достаточно памяти для х предметов без инициализации (ваш вектор остается пустым). Таким образом, выиграл'т быть перераспределение до переметнуться х.
Второй момент заключается в том, что вектор выиграл'т инициализации значений до нуля. Вы тестируете свой код в режиме отладки ?
После проверки на G++ следующий код:
#include <iostream>
#include <vector>
struct MyStruct
{
int m_iValue00 ;
int m_iValue01 ;
} ;
int main()
{
MyStruct aaa, bbb, ccc ;
std::vector<MyStruct> aMyStruct ;
aMyStruct.push_back(aaa) ;
aMyStruct.push_back(bbb) ;
aMyStruct.push_back(ccc) ;
aMyStruct.resize(6) ; // [EDIT] double the size
for(std::vector<MyStruct>::size_type i = 0, iMax = aMyStruct.size(); i < iMax; ++i)
{
std::cout << "[" << i << "] : " << aMyStruct[i].m_iValue00 << ", " << aMyStruct[0].m_iValue01 << "\n" ;
}
return 0 ;
}
дает следующие результаты:
[0] : 134515780, -16121856
[1] : 134554052, -16121856
[2] : 134544501, -16121856
[3] : 0, -16121856
[4] : 0, -16121856
[5] : 0, -16121856
Инициализация ты видел, был, вероятно, артефакт.
[Править] после комментария изменить, я изменил код, чтобы добавить изменение размера линии. Размера фактически вызывает конструктор по умолчанию для объекта внутри вектора, но и если конструктор по умолчанию не делает ничего, то ничего и не инициализируется... я все еще верю, что это был артефакт (у меня получилось в первый раз, чтобы весь вектор zerooed со следующим кодом:
aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;
aMyStruct.push_back(MyStruct()) ;
Так что... :-/
[Править 2], Как уже предложил Аркадий, решением является использование встроенного конструктор, принимающий нужные параметры. Что-то вроде
struct MyStruct
{
MyStruct(int p_d1, int p_d2) : d1(p_d1), d2(p_d2) {}
int d1, d2 ;
} ;
Это, вероятно, получите встроить в свой код.
Но в любом случае следует изучить ваш код с профайлером, чтобы быть уверен, что этот кусок кода является узким местом вашего приложения.
Вы можете использовать тип оболочки тип элемента, с конструктор по умолчанию, который ничего не делает. Е. Г.:
template <typename T>
struct no_init
{
T value;
no_init() { static_assert(std::is_standard_layout<no_init<T>>::value && sizeof(T) == sizeof(no_init<T>), "T does not have standard layout"); }
no_init(T& v) { value = v; }
T& operator=(T& v) { value = v; return value; }
no_init(no_init<T>& n) { value = n.value; }
no_init(no_init<T>&& n) { value = std::move(n.value); }
T& operator=(no_init<T>& n) { value = n.value; return this; }
T& operator=(no_init<T>&& n) { value = std::move(n.value); return this; }
T* operator&() { return &value; } // So you can use &(vec[0]) etc.
};
Использовать:
std::vector<no_init<char>> vec;
vec.resize(2ul * 1024ul * 1024ul * 1024ul);
Из вашего кода, похоже, что у вас есть вектор структур, каждая из которых состоит из 2 ИНЦ. Вы могли бы вместо этого использовать 2 векторы ИНЦ? Тогда
copy(data1, data1 + count, back_inserter(v1));
copy(data2, data2 + count, back_inserter(v2));
Теперь вы не'т платить за копирование структуры каждый раз.
Если вы действительно настаиваете на том неинициализированные элементы и пожертвовать некоторыми методами, как передние(), назад(), push_back(), использовать импульс вектора числовых <повышение/числовой/ublas/вектор.ГЭС и GT;. Это позволяет даже не на сохранение существующих элементов при вызове размеры()...
Сделать структуры сами должны быть в непрерывной памяти, или вы можете уйти с вектора структуры*?
Векторы сделайте копию все, что вы добавить к ним, так через векторы указателей, а не объектов-это один из способов повышения производительности.
Я не'т думаю, что STL-это ваш ответ. Вы'вновь придется свернуть свой собственный вид решения с помощью realloc(). Вы'будете иметь, чтобы хранить указатель и размер, или количество элементов, и использовать это, чтобы найти, где начать добавлять элементы после realloc().
int *memberArray;
int arrayCount;
void GetsCalledALot(int* data1, int* data2, int count) {
memberArray = realloc(memberArray, sizeof(int) * (arrayCount + count);
for (int i = 0; i < count; ++i) {
memberArray[arrayCount + i].d1 = data1[i];
memberArray[arrayCount + i].d2 = data2[i];
}
arrayCount += count;
}
Я хотел сделать что-то вроде:
void GetsCalledALot(int* data1, int* data2, int count)
{
const size_t mvSize = memberVector.size();
memberVector.reserve(mvSize + count);
for (int i = 0; i < count; ++i) {
memberVector.push_back(MyType(data1[i], data2[i]));
}
}
Вы должны определить конструктор для типа, который хранится в memberVector, но, что'с небольшой стоимости, поскольку это даст вам лучшее из обоих миров; нет ненужных инициализации и перераспределение не произойдет во время цикла.