Я преждевременно оптимизирую?

9

В настоящее время я нахожусь на стадии разработки архитектуры на основе компонентов в C ++.

Мой текущий дизайн включает в себя использование таких функций, как:

  • std::vectors std::shared_ptrдля хранения компонентов
  • std::dynamic_pointer_cast
  • std::unordered_map<std::string,[yada]>

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

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

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

Поскольку я нахожусь на стадии проектирования архитектуры, и они будут включены в ядро ​​проекта, я должен попытаться найти способы избежать их сейчас, так как будет очень трудно изменить это позже, если есть производительность вопросы?

Или я просто застрял в преждевременной оптимизации?

Vaillancourt
источник
3
Я бы очень неохотно остановился на дизайне, который потом очень трудно было изменить, независимо от проблем с производительностью. Избегайте этого, если можете. Есть много проектов, которые являются одновременно гибкими и быстрыми.
candied_orange
1
Даже не зная деталей, ответ на этот вопрос почти всегда звучит громко: «ДА !!».
Mawg говорит восстановить Монику
2
@ Mawg "... Но мы не должны упускать наши возможности в эти критические 3%." Так как это основа дизайна, как я могу узнать, работаю ли я над этими 3%?
Vaillancourt
1
Превосходные очки, Александр (+1), и, да, я знаю, что последняя половина цитаты, которая почти никогда не упоминается :-) Но, чтобы вернуться к моему комментарию до этого (что отражено в принятом ответе) , the answer to this question is almost always a resounding "YES !!". Я все еще чувствую, что лучше сначала заставить его работать, а потом оптимизировать, но YMMV, у каждого свое мнение, и все они верны, и только ОП может действительно ответить на его собственный - субъективный - вопрос.
Mawg говорит восстановить Monica
1
@AlexandreVaillancourt Продолжайте читать статью Кнута (PDF, цитата с правой стороны страницы с надписью 268, страница 8 в программе чтения PDF). «... ему будет разумно внимательно посмотреть на критический код; но только после того, как этот код будет идентифицирован. Часто априорно делать суждения о том, какие части программы действительно важны, поскольку универсальный опыт Программисты, которые использовали инструменты измерения, утверждают, что их интуитивные догадки не удаются ». (выделение его)
8bittree

Ответы:

26

Не читая ничего, кроме названия: Да.

После прочтения текста: да. Хотя это правда , что карты и общие указатели и т.д. , не выполняют также кэш-накрест, вы наверняка обнаружите, что вы хотите использовать их - насколько я понимаю - это не узкий и не будешь проходить или эффективно использовать кэш независимо от структуры данных.

Напишите программное обеспечение, избегая самых глупых ошибок, затем протестируйте, затем найдите узкие места и оптимизируйте!

Fwiw: https://xkcd.com/1691/

Штеффен
источник
3
Согласовано. Сначала заставьте это работать правильно, потому что не имеет значения, насколько быстро он не работает. И всегда помните, что наиболее эффективные оптимизации не включают в себя настройку кода, а поиск другого, более эффективного алгоритма.
Тодд Кнарр
10
Я хотел бы отметить , что первая строка не так , потому что оптимизация всегда преждевременно, а потому , что оптимизация только не преждевременно , если вы знаете , что это нужно, в этом случае вы не будете спрашивать об этом. Итак, первая строка верна только потому, что тот факт, что вы задаете вопрос о том, является ли оптимизация преждевременной, означает, что вы не уверены, что вам нужна оптимизация, что по определению делает ее преждевременной. Уф.
Йорг Миттаг
@ JörgWMittag: согласен.
Штеффен
3

Я не знаком с C ++, но в целом это зависит.

Вам не нужно преждевременно оптимизировать изолированные алгоритмы, где вы можете легко оптимизировать, когда дело доходит до этого.

Однако вам необходимо получить общий дизайн приложения для достижения желаемых ключевых показателей эффективности.

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

Низко летящий пеликан
источник
3

Если вам нужно спросить, тогда да. Преждевременная оптимизация означает оптимизацию, прежде чем вы убедитесь, что существует значительная проблема производительности.

JacquesB
источник
1

ECS? На самом деле, я полагаю, что это не будет преждевременным, если так много думать о ориентированной на данные стороне проекта и оценивать различные повторы, потому что это может повлиять на ваш дизайн интерфейса , а последнее очень дорого менять в конце игра. Кроме того, ECS просто требует большой работы и обдумывания заранее, и я думаю, что стоит потратить часть этого времени, чтобы убедиться, что это не приведет к печальному падению производительности на уровне дизайна, учитывая то, как это будет в основе вашей работы. весь долбанный двигатель. Эта часть смотрит на меня:

unordered_map<string,[yada]>

Даже с небольшими оптимизациями строк у вас есть контейнер переменного размера (строки) внутри другого контейнера переменного размера (unordered_maps). На самом деле, небольшая строковая оптимизация может быть настолько же вредной, насколько и полезной в этом случае, если ваша таблица очень разреженная, поскольку небольшая строковая оптимизация подразумевает, что каждый неиспользуемый индекс хеш-таблицы будет по-прежнему использовать больше памяти для оптимизации SS ( sizeof(string)будет намного больше) до такой степени, что общие накладные расходы памяти вашей хеш-таблицы могут стоить больше, чем все, что вы храните в ней, особенно если это простой компонент, такой как компонент позиции, в дополнение к большим потерям кеша с огромным шагом получить от одной записи в хеш-таблице к следующей.

Я предполагаю, что строка является своего рода ключом, например, идентификатором компонента. Если это так, это уже делает вещи значительно дешевле:

unordered_map<int,[yada]>

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

Тем не менее, если вы можете сопоставить строку с достаточно низким диапазоном плотно используемых индексов, то вы можете просто сделать это:

vector<[yada]> // the index and key become one and the same

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

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

// Abstract pixel that could be concretely represented by
// RGB, BGR, RGBA, BGRA, 1-bit channels, 8-bit channels, 
// 16-bit channels, 32-bit channels, grayscale, monochrome, 
// etc. pixels.
class IPixel
{
public:
    virtual ~IPixel() {}
    ...
};

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

Что касается ошибок кэша ECS, то, по моему мнению, разработчики часто слишком стараются сделать их ECS-дружественными к кешу. Он начинает приносить слишком мало денег, чтобы попытаться получить доступ ко всем вашим компонентам совершенно непрерывным образом, и часто подразумевает копирование и перетасовку данных повсюду. Обычно достаточно достаточно, скажем, просто использовать индексы компонентов радикальной сортировки до доступа к ним, чтобы вы обращались к ним таким образом, чтобы, по крайней мере, вы не загружали область памяти в строку кэша только для ее удаления, а затем загружали все снова и снова в одном цикле, чтобы получить доступ к другой части той же строки кэша. И ECS не должен обеспечивать удивительную эффективность по всем направлениям. Это не так, как система ввода извлекает из этого столько же, сколько физическая система или система рендеринга, поэтому я рекомендую стремиться к «хорошему». эффективность по всем направлениям и «отлично» только в тех местах, где это действительно нужно. Тем не менее, использованиеunordered_mapи stringздесь достаточно легко избежать.


источник