2016-10-12 4 views
1

В некоторых случаях моей программе C++ 14 требуется «блок» около 100 миллионов complex<float>, для чего требуется около 1 ГБ ОЗУ. Мы можем с уверенностью предположить, что необходимая память будет доступна.выделяют, но не строят большой массив объектов C++.

Однако выделение нового std::vector происходит очень медленно, потому что сложный конструктор называется 100 миллионов раз. На моей машине для инициализации массива требуется код в течение полной секунды.

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

Оказывается, нам даже не нужна эта инициализация, потому что комплекс в большом массиве будет заселен вскоре позже из внешнего источника. Поэтому я смотрю на то, чтобы отложить построение объектов в этом «блоке» до более позднего времени, а затем построить их непосредственно из внешнего источника данных.

Итак, мой вопрос в том, существует ли безопасный идиоматический и эффективный способ C++, возможно, используя семантику перемещения C++ на этом пути? Если нет, и мы решаем просто блок памяти, можем ли мы тогда reinterpret_cast блок памяти на простой старый массив C complex<float>?

Благодарим Вас за помощь

Jean-Denis Muys

+7

Используйте 'std :: vector <>', но используйте функцию 'reserve()' member, а не 'resize()' или размерный конструктор. – ildjarn

+0

Действительно ли это так медленно, даже когда вы скомпилируете его с оптимизацией? Я не могу в это поверить. –

+0

Почему вы хотите использовать вектор в этом случае? Вы не можете прикреплять/отсоединять память к вектору, но вы можете использовать все std-алгоритмы, используя указатели вместо итераторов. – 0kcats

ответ

2

Я настоятельно рекомендую придерживаться C++ и избегать ручного управления памятью самостоятельно.

Стандартной библиотеки должно быть достаточно. Например.

std::vector<complex> my_vector; 

// Reserve the necessary space without constructing anything 
my_vector.reserve(100'000'000); 

// construct the elements when needed 
populate(my_vector); 
+0

Я согласен с вашим «сильным предложением». Я попытаюсь использовать 'vector :: reserve()' –

0

Так что ваш вопрос, если вы можете использовать malloc(). Я использовал тот же подход много лет назад с моим старым компилятором на C++, и он сработал. Но в конце мне пришлось позвонить free() вместо delete[]. Я думаю, что это специфично для реализации, поэтому вы должны попробовать его на своем компиляторе.

3

Если определить конструктор по умолчанию для класса complex<float>, как пустой, которая оставляет переменные члены неинициализированным, то не должно быть никакой реальной разницы между этими двумя операциями, учитывая, что компилятор оптимизаций включен.

Предполагая нижеследующее определение класса complex.

template <typename T> 
struct complex 
{ 
    complex() {}; // Empty constructor does nothing 
    T a, b; 
}; 

Сформированные сборки для использования vector инициализации с x86-64 GCC 6.2 и -O2 включена является:

std::vector<complex<float>> v(100); 

    mov  edi, 800 
    call operator new(unsigned long) 
    mov  rdi, rax 
    call operator delete(void*) 

И генерируемая сборка для ручного вызова malloc и free является:

auto v = malloc(100 * sizeof(complex<float>)); 
free(v); 

    mov  edi, 800 
    call malloc 
    mov  QWORD PTR [rsp+8], rax 
    mov  rdi, QWORD PTR [rsp+8] 
    call free 

Как вы можете видеть, реализация vector больше не вызывает конструктор complex<float> для каждый элемент. Использование vector является более правильным и читаемым, а также использует RAII, который помогает предотвратить утечку памяти.

+1

, это очень хороший момент. Конечно, это решение требует определения пользовательского сложного класса, а не использования 'std :: complex' –

+0

в моей среде (clang on the mac), сложный конструктор по умолчанию определяется следующим образом:' complex (const value_type & __re = value_type() , const value_type & __im = value_type()) : __re _ (__ re), __im _ (__ im) {} ' –

+0

Я вижу, поэтому он выглядит так:' std :: complex' всегда инициализирует свои реальные и мнимые значения, что всегда давало бы вам замедление, если вы инициализируете их все сразу в 'std :: vector'. –