#c #vector
#c #вектор
Вопрос:
Я определил узкое место в своем коде на c , и моя цель — ускорить его. Я перемещаю элементы из одного вектора в другой вектор, если условие истинно.
В python pythonic способ сделать это — использовать понимание списка:
my_vector = [x for x in data_vector if x > 1]
Я взломал способ сделать это на C , и он работает нормально. Тем не менее, я вызываю это миллионы раз в цикле while, и это происходит медленно. Я не очень разбираюсь в распределении памяти, но я предполагаю, что моя проблема связана с повторным выделением памяти с использованием push_back
. Есть ли способ выделить мою память по-другому, чтобы ускорить этот код? (Я не знаю, насколько большим my_vector
должен быть, пока цикл for
-loop не будет завершен).
std::vector<float> data_vector;
// Put a bunch of floats into data_vector
std::vector<float> my_vector;
while (some_condition_is_true) {
my_vector.clear();
for (i = 0; i < data_vector.size(); i ) {
if (data_vector[i] > 1) {
my_vector.push_back(data_vector[i]);
}
}
// Use my_vector to render graphics on the GPU, but do not change the elements of my_vector
// Change the elements of data_vector, but not the size of data_vector
}
Комментарии:
1. При поиске полезных вещей часто помогает обращение к ссылке на контейнер. Это поможет вам найти полезные функции, такие как
reserve
.2. кстати
push_back
, не всегда выделяется память.vector
резервирует некоторую память (которую вы можете проверить, вызвавcapacity()
метод ofvector
), и когдаsize
он достигает tocapacity
, только тогда выделяется новая память. Но вы всегда можете указатьvector
, чтобы зарезервировать некоторую память, прежде чем что-то делать с ней, вызвавreserve
3. Вам нужно
data_vector
после того, как вы скопировали его вmy_vector
? Можетswap
быть, вам подойдет?4. «Я вызываю это миллионы раз в цикле времени» Звучит для меня так, как будто вам нужно отступить и пересмотреть свое требование, поскольку для меня это красный флаг.
5. Одной из основных проблем здесь является «я предполагаю, что …». Вы должны с уверенностью определить, что ваше предположение верно и что рендеринг или модификация
data_vector
не являются истинным узким местом. (Кроме того, если вы выполняете циклdata_vector
после рендеринга, вы можете вносить измененияmy_vector
во время выполнения.)
Ответ №1:
Используйте std::copy_if
и резервируйте data_vector.size()
для my_vector
изначально (поскольку это максимально возможное количество элементов, для которых ваш предикат может оценить значение true):
std::vector<int> my_vec;
my_vec.reserve(data_vec.size());
std::copy_if(data_vec.begin(), data_vec.end(), std::back_inserter(my_vec),
[](const autoamp; el) { return el > 1; });
Обратите внимание, что вы могли бы избежать reserve
вызова здесь, если ожидаете, что количество раз, когда ваш предикат принимает значение true, будет намного меньше размера data_vector
.
Комментарии:
1. Мне это нравится. Если потраченная впустую память является большой проблемой, вы можете вызвать
shrink_to_fit()
ее позже (даже если реализация не требуется для фактического сжатия).
Ответ №2:
Хотя существуют различные отличные решения, опубликованные другими для вашего запроса, кажется, что по-прежнему нет особого объяснения выделения памяти, которое вы не очень понимаете, поэтому я хотел бы поделиться с вами своими знаниями по этой теме. Надеюсь, это поможет.
Во-первых, в C существует несколько типов памяти: stack
, heap
, data segment
.
Stack
предназначен для локальных переменных. С ним связаны некоторые важные функции, например, они будут автоматически освобождены, работа с ним выполняется очень быстро, его размер зависит от операционной системы и мал, так что хранение некоторых КБ данных в stack
может вызвать переполнение памяти и так далее.
Heap
к памяти можно обращаться глобально. Что касается его важных функций, которые у нас есть, его размер может быть динамически увеличен при необходимости, и его размер больше (намного больше stack
), работа с ним медленнее stack
, требуется ручное освобождение памяти (в современной ОС память будет автоматически освобождена в конце программы),и так далее.
Data segment
предназначен для глобальных и статических переменных. Фактически, этот фрагмент памяти можно разделить на еще более мелкие части, например, BBS.
В вашем случае vector
используется. Фактически, элементы vector
хранятся во внутреннем динамическом массиве, то есть во внутреннем массиве с динамическим размером массива. На ранних версиях C динамический массив можно было создать в stack
памяти, однако это уже не так. Чтобы создать динамический массив, его нужно создать heap
. Поэтому элементы vector
хранятся во внутреннем динамическом массиве on heap
. Фактически, для динамического увеличения размера массива необходим процесс, а именно memory reallocation
. Однако, если vector
пользователь продолжает увеличивать его или ее vector
, то накладные reallocation
расходы будут высокими. Чтобы справиться с этим, a vector
сначала выделил бы часть памяти, размер которой превышает текущую потребность, то есть выделил бы память для потенциального использования в будущем. Следовательно, в вашем коде это не тот случай, который memory reallocation
выполняется при каждом push_back()
вызове. Однако, если vector
копируемый объект довольно большой, памяти, зарезервированной для будущего использования, будет недостаточно. Затем memory allocation
произойдет. Для решения этой vector.reserve()
проблемы может использоваться.
Я новичок. Надеюсь, я не допустил никакой ошибки в своем совместном использовании. Надеюсь, это поможет.
Ответ №3:
Запустите код дважды, в первый раз подсчитав, сколько новых элементов вам понадобится. Затем используйте reserve
, чтобы уже выделить всю необходимую память.
while (some_condition_is_true) {
my_vector.clear();
int newLength = 0;
for (i = 0; i < data_vector.size(); i ) {
if (data_vector[i] > 1) {
newLength ;
my_vector.reserve(newLength);
for (i = 0; i < data_vector.size(); i ) {
if (data_vector[i] > 1) {
my_vector.push_back(data_vector[i]);
}
}
// Do stuff with my_vector and change data_vector
}
Комментарии:
1. Примечание: изменено на
reserve
. Изначально у меня былоresize
, что, конечно, неправильно.2. У вас есть хорошее решение с точки зрения высокого уровня, но ваша реализация может потребовать некоторой работы. Это может быть намного проще и понятнее. Просмотрите другие примеры, чтобы увидеть, как заставить стандартную библиотеку работать на вас, и избежать написания большого количества циклов и условных выражений вручную.
Ответ №4:
Я сомневаюсь, что проблема в распределении my_vector
, особенно если while
цикл выполняется много раз, поскольку емкость my_vector
должна быстро стать достаточной.
Но чтобы быть уверенным, вы можете просто зарезервировать емкость в my_vector
соответствии с размером data_vector
:
my_vector.reserve(data_vector.size());
while (some_condition_is_true) {
my_vector.clear();
for (auto value : data_vector) {
if (value > 1)
my_vector.push_back(value);
}
}
Ответ №5:
Если вы используете Linux, вы можете зарезервировать память для my_vector
предотвращения std::vector
перераспределений, что является узким местом в вашем случае. Обратите внимание, что reserve не будет тратить память из-за перегрузки, поэтому любая приблизительная верхняя оценка значения резерва будет соответствовать вашим потребностям. В вашем случае размера data_vector
будет достаточно. Эта строка кода перед while
циклом должна устранить узкое место:
my_vector.reserve(data_vector.size());