Простой вопрос эффективности C++ (распределение памяти) .. и, может быть, помощь в обнаружении столкновений?

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

Вот как я это организовал (я просто придумал, чтобы это была дерьмовая система):

Каждый корабль состоит из круглых компонентов - количество компонентов в каждом корабле произвольно (больше компонентов, больше циклов процессора). У меня есть расстояние maxComponent, которое я вычисляю при создании корабля, которое, по сути, является самой длинной линией, которую я могу провести от центра корабля до края самого дальнего компонента. Я отслеживаю вещи на экране и использую это maxComponentDistance, чтобы увидеть, достаточно ли они близки, чтобы столкнуться.

Если они находятся в непосредственной близости, я начинаю проверять, пересекаются ли компоненты разных кораблей. Здесь возникает мой вопрос об эффективности.

У меня есть (x, y) местоположения компонента относительно центра корабля, но это не учитывает то, как корабль в настоящее время вращается. Я считаю их относительными, потому что не хочу пересчитывать компоненты каждый раз, когда корабль движется. Итак, у меня есть небольшая формула для расчета поворота, и я возвращаю 2d-вектор, соответствующий положению с учетом поворота относительно центра корабля.

Обнаружение столкновений находится в GameEngine и использует 2d-вектор. Мой вопрос о возвращаемых типах. Должен ли я просто создавать и возвращать объект 2d-вектора каждый раз, когда вызывается эта функция или же должен ли я предоставить этому компонентному объекту дополнительную частную двумерную векторную переменную, отредактировать частную переменную при вызове функции и вернуть указатель на этот объект?

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

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

Заранее спасибо.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
1 551
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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

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

В качестве общего (и приносим свои извинения, если это слишком очевидно) при выполнении такого рода обнаружения столкновений: возводите расстояния в квадрат, а не вычисляйте квадратные корни. :)

Если иногда между изменениями компонентов происходит несколько изменений компонентов, а иногда - несколько изменений между изменениями компонентов, то выделение при первом использовании может быть подходящим вариантом. В однопоточном варианте использования накладные расходы на проверку того, кэшировано ли нужное вам значение, должны быть незначительными.

Steve Jessop 03.12.2008 03:56
  1. Вы можете начать, просто вернув вектор и протестировав его. Кто знает, может быть, достаточно быстро. С помощью профилировщика вы даже можете увидеть, какая часть занимает время выполнения.
  2. Вы можете использовать Пул памяти для повторного использования векторов и уменьшения копирования
  3. Вы можете попробовать Схема наилегчайшего веса для координат, чтобы уменьшить копирование и распределение, если они повторяются во всем движке.
  4. Хранение данных в компоненте - хороший способ уменьшить выделение, но вносит некоторые подводные камни в ваш дизайн, например, тот, кто использует вектор, зависит от жизненного цикла компонента. Пул памяти, наверное, лучше.

Не используйте 2D-вектор. Скорее используйте vector из point. То же самое и для обнаружения столкновений. Использование 2D-вектора здесь - неправильная структура данных.

В зависимости от содержимого функции компилятор сможет выполнить NRVO (то есть оптимизация именованного возвращаемого значения), что означает, что в оптимальном случае возврат вектора имеет накладные расходы нет, т.е. он никогда не копируется. Однако это происходит только тогда, когда вы используете возвращаемое значение вашей функции для инициализации нового экземпляра, и когда компилятор может отслеживать пути выполнения внутри функции и видеть, что для каждого пути возврата возвращается один и тот же объект. Рассмотрим следующие два:

vector<int> f(int baz) {
    vector<int> ret;
    if (baz == 42)
        ret.push_back(42);
    return ret;
}

vector<int> g(int baz) {
    if (baz == 42)
        return vector<int>(1, 42);
    else
        return vector<int>();
}

Компилятор может выполнять NRVO для вызовов f, но не для g.

Я не возражаю, но замечу, что возвращение вектора все равно не очень "C++ - иш". Templatize, возьмите итератор вывода в качестве параметра, такого как std :: copy, и если вызывающий хочет его в векторе, он может использовать std :: back_inserter, который будет встроен. По-прежнему нет накладных расходов, и вызывающие абоненты получают большую гибкость.

Steve Jessop 03.12.2008 03:38

Итак, в этом случае код выглядит следующим образом: «template <typename IT> void f (int baz, IT it) {if (baz == 42) * it = 42;}». Вызывающий выполняет "vector <int> foo; f (42, back_inserter (foo));". Или вы можете взять итератор по неконстантной ссылке, но алгоритмы AFAIK STL этого не делают, они принимают итераторы по значению.

Steve Jessop 03.12.2008 03:45

... конечно, все это предполагает, что вызывающие абоненты могут использовать ваши шаблоны, что в вашем собственном коде является безопасным предположением, но падает, если шаблон находится на границе API библиотеки, а ваш компилятор / компоновщик не поддерживает внешние шаблоны . Что, давайте посмотрим правде в глаза, это не так.

Steve Jessop 03.12.2008 03:48

Ненавижу кого-то пометить. Но vector <> - совершенно неподходящий инструмент для работы! Сворачивание его собственного класса тривиально, дешево для копирования, позволяет добавлять специальные методы Vector2D и не требует постоянных действий по созданию / удалению! (Хотя ссылка NRVO была полезной.)

Mr.Ree 03.12.2008 04:26

onebyone: алгоритмы STL не часто генерируют данные, и их интерфейс определенно не нацелен на эту задачу. Ничто в C++ не заставляет вас соблюдать это ограничение. Утверждать, что это не C++ - довольно сильно. Тем не менее, я никогда не писал код, как указано выше.

Konrad Rudolph 03.12.2008 11:13

мри: что угодно. Я просто хотел указать, что 2D-массив (или 2D-вектор, т.е. предположительно вложенный вектор) - это совершенно неправильный инструмент для работы. vector<point> превосходит безусловно. Конечно, написание собственной структуры данных часто даже лучше, но так же часто это ненужно и излишне.

Konrad Rudolph 03.12.2008 11:15

mree: Забудьте, что я сказал. Я только заметил, что OP, вероятно, относится к «вектору» в математическом смысле, а не в смысле C++.

Konrad Rudolph 03.12.2008 11:17

Если ваш 2D-вектор просто:

 class Vector2D { double x, y; };

Тогда непременно верните его! Например:

  Vector2D function( ... );

Или пройдите по ссылке:

  void function( Vector2D * theReturnedVector2D, ... );

Избегайте любой ценой:

 vector<double> function(...);

Постоянное выделение / освобождение кучи, присущее классу Vector, является ошибкой!


Копирование собственного класса Vector2D очень дешево с вычислительной точки зрения. В отличие от Vector <>, ваш собственный класс Vector2D может включать любые методы, которые вам нравятся.

Я использовал эту функцию в прошлом для включения таких методов, как distanceToOtherPointSquared (), scanfFromCommandLineArguments (), printfNicelyFormatted () и operator [] (int).


или я должен предоставить этому компонентному объекту дополнительную частную двумерную векторную переменную, отредактировать частную переменную при вызове функции и вернуть указатель на этот объект?

Остерегайтесь многократных вызовов функций, делающих недействительными предыдущие данные. Это рецепт катастрофы!

Есть большая разница между распределением памяти в куче и в стеке. Выделение в куче, например, использование new / delete или malloc / free очень медленное. Размещение в стеке действительно довольно быстрое. Со стеком обычно медленная часть копирует ваш объект. Так что следите за вектором и т. д., Но возвращение простых структур, вероятно, нормально.

Другие вопросы по теме