Быстрый способ многократного возврата вектора

#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() метод of vector ), и когда size он достигает to capacity , только тогда выделяется новая память. Но вы всегда можете указать 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());