Почему так рано оптимизировать слишком рано?

168

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

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

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

Может ли кто-нибудь объяснить мне, почему такая плохая идея оптимизировать слишком рано?

MR-матовый
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Джош
3
На это не может быть проще ответить: «это пустая трата времени». Вы могли бы также спросить "зачем пытаться сэкономить при покупке?"
Толстяк
27
И наоборот, обратите внимание, что многие инженеры просто говорят, что предложение «не оптимизировать слишком рано» просто неверно . Предложение должно быть просто «не оптимизировать, пока вы не знаете, что оптимизировать». Итак, довольно просто, если есть три системы A, B и C, совершенно бессмысленно оптимизировать A, если, оказывается, A никоим образом не является узким местом, а C фактически является узким местом. В этом примере, очевидно, оптимизация А была бы пустой тратой времени. Так что - довольно просто - не оптимизируйте, пока вы не знаете, какой из A, BC оптимизировать: это все, что это значит, это так просто.
Толстяк
2
Оптимизация временного класса вашего приложения во время разработки сильно отличается от попытки вывести несколько инструкций из цикла, который обычно является постоянным временем. Сосредоточьтесь на O (n) против O (n log n) вместо того, чтобы «писать цикл for таким образом, чтобы сохранить одну инструкцию процессора».
1
@phyrfox На самом деле, сокращение времени загрузки кажется мне более впечатляющим. Время загрузки три секунды заметно. Я не уверен, заметны ли 55fps к 60fps.
immibis

Ответы:

237

Преамбула:

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

«Не оптимизировать преждевременно» не означает «писать код, который вы знаете, это плохо, потому что Кнут говорит, что вам не разрешено его чистить до конца»

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

Это означает, что в случае сомнений мы должны:

  • Предпочитайте код, который легко писать, понятен и легко модифицировать для начинающих

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

Преждевременная оптимизация не является :

  • Архитектурные решения для структурирования вашего кода таким образом, чтобы он соответствовал вашим потребностям - выбирая подходящие модули / обязанности / интерфейсы / коммуникационные системы продуманным образом.

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

  • Используя правильный алгоритм для работы. A * является более оптимальным и более сложным, чем исчерпывающий поиск графа поиска пути. Это также отраслевой стандарт. Повторяя тему, придерживаясь таких проверенных методов, на самом деле ваш код легче понять, чем если бы вы делали что-то простое, но в отличие от известных лучших практик. Если у вас есть опыт работы с узкими местами, реализующими игровую функцию X одним способом в предыдущем проекте, вам не нужно снова сталкиваться с тем же узким местом в этом проекте, чтобы знать, что это реально - вы можете и должны повторно использовать решения, которые работали в прошлом. игры.

Все эти типы оптимизаций вполне оправданы и, как правило, не будут помечены как «преждевременные» (если только вы не спускаетесь по кроличьей норе, внедряя передовой поиск путей для вашей карты шахматной доски 8x8 ...)

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


Особенно в gamedev скорость итерации - самое ценное. Мы часто реализуем и повторно реализуем гораздо больше идей, чем в конечном итоге поставим с готовой игрой, пытаясь «найти удовольствие».

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

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

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

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

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

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

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

Д.М.Григорий
источник
38
Здесь есть дальнейшее обсуждение StackOverflow , подчеркивающее важность читабельности и ясности в коде, а также опасность усложнения понимания кода (и упрощения введения ошибок) за счет его преждевременной оптимизации.
DMGregory
8
Отличный ответ, я хотел бы добавить, в ответ на вопрос « не будет ли невероятно сложно изменить некоторые основные структуры игры в конце, вместо того, чтобы разрабатывать их в первый раз с учетом производительности? », Что Конечно, вы пытаетесь спроектировать для эффективности в первую очередь. Когда представлены два варианта, вы выбираете более эффективный. В противном случае вы пишете игру как можно более модульно с четко определенными интерфейсами. Затем вы можете изменить механизмы внутри каждого модуля, не перестраивая всю кодовую базу.
Baldrickk
1
@Baldrickk Я бы сказал, что стоит поделиться в качестве дополнительного ответа. (Я бы высоко оценил это!) В моем ответе пропущена важность архитектуры и планирования, чтобы в первую очередь избежать больших проблем. Что касается ссылки Гизмо на «разработку программ в целом», то отсюда и возникает эта концепция преждевременной оптимизации. Как указано выше, неправильная оптимизация может стать техническим долгом так же легко, как и отсутствующая оптимизация. Но мы должны подчеркнуть, что мы никогда не делаем вещи намеренно медленно, просто предпочитая простоту и удобочитаемость, пока не сможем
описать
1
Хотя я, пожалуй, самый неопытный здесь из Вас, я должен сказать, что нахожу этот «преждевременный корень оптимизации всего зла» немного странным. Примерно два года назад я пытался что-то сделать в Unity3D. Я написал несколько запросов LINQ, каждый из которых использовал предыдущий. Я посмотрел на свой код и начал испытывать сильные сомнения. Я думал, что сложность вычислений была ужасной. Я взял лист бумаги и рассчитал, что обработка этих запросов с ожидаемым объемом данных займет несколько часов. Поэтому я добавил немного кеширования здесь и там. И запросы работали нормально.
гаазкам
3
Итак, вы проявили должную осмотрительность и убедились, что проблема реальная, прежде чем менять свой код с того, что было изначально наиболее понятным и интуитивным для вас. Это означает, что ваша оптимизация не была преждевременной. ;) «Избегать преждевременной переоптимизации» не означает «писать неоправданно дорогой код» - просто для простоты, удобочитаемости и простоты модификации до тех пор, пока не будет выявлена ​​определенная проблема с производительностью. Комментарии не предназначены для обсуждения, поэтому мы можем перейти в чат, если вы хотите поговорить об этом дальше.
DMGregory
42

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

«Разве не будет невероятно трудно изменить некоторые основные структуры игры в конце, вместо того, чтобы разрабатывать их в первый раз с учетом производительности?»

Это, для меня, суть вопроса.

Создавая свой оригинальный дизайн, вы должны стараться проектировать для эффективности - на высшем уровне. Это меньше оптимизации, а больше о структуре.

Пример:
вам нужно создать систему для пересечения реки. Очевидные проекты - мост или паром, так что вы выбираете?
Ответ, конечно, зависит от размера перекрестка и объема трафика. Это не оптимизация, а начинание с дизайна, подходящего для вашей проблемы.

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

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

Параметры:

  • Куплю второй паром (параллельная обработка)
  • Добавить другую автомобильную палубу к парому (сжатие трафика)
  • Модернизируйте двигатель парома, чтобы он стал быстрее (переписаны алгоритмы обработки)

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

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

Давайте посмотрим на добавление дополнительного парома.
«Плохая» программа может быть построена вокруг идеи единого парома, и иметь состояния стыковки и состояния парома, а также объединять и объединять все состояния. Это будет трудно изменить, чтобы добавить дополнительный паром в систему.
Лучшим дизайном было бы иметь доки и паром как отдельные объекты. Между ними нет тесной связи, но у них есть интерфейс, куда паром может прибыть, разгрузить пассажиров, взять новых и уйти. Док-станция и паром совместно используют только этот интерфейс, и это позволяет легко вносить изменения в систему, в этом случае путем добавления второго парома. Дока не волнует, какие паромы там на самом деле, все, что его интересует, это то, что что-то (что-либо) использует его интерфейс.

ТЛ; др:

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

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

Baldrickk
источник
16
В начале вы также можете реализовать свой паром как IRiverCrossing, а когда станет ясно, что объем трафика слишком велик для его установки, реализовать мост как IRiverCrossingи сбросить его. Конечно, хитрость заключается в сокращении внешнего API парома. и мост к общей функциональности, чтобы они могли быть представлены одним и тем же интерфейсом.
Мистер Миндор
2
Возможно, у вас даже есть автоматические тесты, написанные для этих модульных интерфейсов, так что вы можете быстро выполнить рефакторинг, не беспокоясь о том, чтобы сломать что-либо вне модуля, к которому вы прикасаетесь.
user3067860
2
Очень хороший пример того, почему ООП так важен в наши дни.
Хайме Гальего
1
Вы нашли правильную причину. Мантра Дональда Кнута верна только тогда, когда скорость не является одним из основных требований, и акцент на оптимизации ставит под угрозу основной дизайн. Увы, в играх скорость часто лежит в основе, и, следовательно, должна учитываться при разработке на ранней стадии. Озабоченность ОП (и ваш ответ) вполне оправданны.
Питер А. Шнайдер
3
@AlexandreVaillancourt, вместо того, чтобы отвечать на заголовок вопроса, я попытался ответить на вопрос, который, на мой взгляд, является ключевой частью вопроса, на который действительно дается ответ; то есть «почему я не должен оптимизировать рано, если оптимизация будет намного больше работы позже, потому что мой дизайн исправлен» (перефразировано) . Этот ответ также начинался как комментарий к ответу Д.Г.Григория, и в дополнение к нему, и нет особого смысла дублировать его ответ.
Baldrickk
24

«Не оптимизировать рано» не означает «выбрать худший из возможных способов». Вам все еще нужно учитывать влияние на производительность (если только вы не создаете прототипы). Дело не в том, чтобы наносить ущерб другим, более важным вещам на данном этапе разработки, таким как гибкость, надежность и т. Д. Выбирайте простые, безопасные оптимизации - выбирайте вещи, которые вы ограничиваете, и вещи, которые вы держите свободными; отслеживать расходы. Стоит ли использовать строгую типизацию? Большинство игр работали и работают нормально; сколько вам стоило бы удалить это, если бы вы нашли интересное использование гибкости для геймплея?

Намного сложнее модифицировать оптимизированный код, особенно «умный» код. Это всегда выбор, который делает некоторые вещи лучше, а другие хуже (например, вы могли бы тратить процессорное время на использование памяти). Делая такой выбор, вы должны знать обо всех последствиях - они могут быть катастрофическими, но они также могут быть полезными.

Например, Commander Keen, Wolfenstein и Doom были созданы на основе оптимизированного движка рендеринга. У каждого был свой «трюк», который позволил игре существовать в первую очередь (у каждого также была дальнейшая оптимизация, разработанная с течением времени, но это не важно здесь). Это нормально . Это нормально - сильно оптимизировать саму суть игры, мысль, которая делает игру возможной; особенно, если вы исследуете новую территорию, где эта конкретная оптимизированная функция позволяет вам рассматривать игровые конструкции, которые не были сильно изучены. Ограничения, которые вводит оптимизация, могут также дать вам интересный игровой процесс (например, ограничения количества юнитов в играх RTS могут начаться как способ повышения производительности, но они также имеют эффект игрового процесса).

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

Теперь рассмотрим игру, которую вы можете захотеть сделать. Есть ли какое-то технологическое чудо, которое делает или разрушает эту игру? Может быть, вы представляете игру с открытым миром в бесконечном мире. Это действительно центральная часть игры? Будет ли игра просто не работать без нее? Возможно, вы думаете об игре, в которой местность деформируется без ограничений, с реалистичной геологией и тому подобным; Вы можете заставить это работать с меньшим объемом? Будет ли это работать в 2D вместо 3D? Получите что-нибудь интересное как можно скорее - даже если для оптимизации может потребоваться переработать огромный кусок существующего кода, это может стоить того; и вы можете даже понять, что увеличение масштабов на самом деле не делает игру лучше.

В качестве примера недавней игры с большим количеством оптимизаций я бы указал на Factorio. Одной из важнейших частей игры являются ремни - их много тысяч, и они несут множество отдельных кусочков материалов по всей вашей фабрике. Игра началась с сильно оптимизированного ременного двигателя? Нет! На самом деле, оригинальную конструкцию ремня было почти невозможно оптимизировать - он как бы выполнял физическое моделирование предметов на ремне, что создавало некоторые интересные вещи, которые вы могли бы сделать (именно так вы получаете «эмерджентный» геймплей - геймплей, который удивляет дизайнер), но это означало, что вы должны были смоделировать каждый элемент на поясе. С тысячами ремней вы получаете десятки тысяч предметов, смоделированных физически - даже если вы просто удалите их и дадите ремням работу, вы сможете сократить время, затрачиваемое на процессор, на 95-99%, даже без учета таких вещей, как локальность памяти. Но это полезно делать только тогда, когда вы действительно достигаете этих пределов.

Практически все, что имело отношение к ремням, должно было быть переделано, чтобы можно было оптимизировать ремни. И ремни нужно было оптимизировать, потому что вам нужно было много ремней для большой фабрики, а крупные фабрики - это одна из привлекательных сторон игры. В конце концов, если у вас не может быть больших фабрик, зачем нужен бесконечный мир? Забавно, что вы должны спросить - ранние версии этого не делали :) Игра была переработана и изменена много раз, чтобы добраться до того места, где они сейчас находятся - в том числе 100% -ный римейк, когда они поняли, что Java не является подходящим способом для игра, как это и перешел на C ++. И это сработало отлично для Factorio (хотя это было все же хорошо, что не было оптимизировано с самого начала - тем более, что это был хобби-проект, который мог бы просто потерпеть неудачу из-за отсутствия интереса).

Но дело в том, что естьмногие вещи вы можете сделать с фабрикой ограниченного масштаба - и многие игры показали это. Ограничения могут быть даже более вдохновляющими, чем свободы; будет ли Spacechem более увлекательным, если бы «карты» были бесконечными? Если бы вы начали с сильно оптимизированных «поясов», вы бы в значительной степени были бы вынуждены пойти по этому пути; и вы не могли бы исследовать другие направления дизайна (например, посмотреть, что интересного вы можете сделать с помощью имитирующих физику конвейерных лент). Вы ограничиваете свое потенциальное пространство дизайна. Может показаться, что это не так, потому что вы не видите много незавершенных игр, но сложная часть состоит в том, чтобы получить удовольствие от веселья - для каждой забавной игры, которую вы видите, возможно, есть сотни, которые просто не могли попасть туда и были выброшены (или хуже, выпущен как ужасные путаницы). Если оптимизация поможет вам это сделать - продолжайте. Если это не так ... это скорее преждевременно. Если вы думаете, что какая-то механика геймплея работает отлично, но нуждается в оптимизации, чтобы действительно сиять - продолжайте. Если у вас нет интересной механики,не оптимизируйте их . Сначала найдите забаву - вы обнаружите, что большинство оптимизаций не помогают с этим, и часто являются вредными.

Наконец, у вас есть отличная, веселая игра. Есть ли смысл оптимизировать сейчас ? Ха! Это все еще не так ясно, как вы думаете. Есть что-то веселоевы можете сделать вместо этого? Не забывайте, что ваше время все еще ограничено. Все требует усилий, и вы хотите сосредоточить эти усилия на том, где это наиболее важно. Да, даже если вы делаете «бесплатную игру» или «игру с открытым исходным кодом». Смотрите, как играется в игру; обратите внимание, где производительность становится узким местом. Оптимизация этих мест приносит больше удовольствия (например, строительство больших и запутанных фабрик)? Позволяет ли вам привлечь больше игроков (например, с более слабыми компьютерами или на разных платформах)? Вам всегда нужно расставлять приоритеты - ищите соотношение усилий и доходности. Скорее всего, вы найдете множество низко висящих фруктов, просто играя в свою игру и наблюдая, как другие играют в игру. Но обратите внимание на важную часть - чтобы попасть туда, вам нужна игра . Сосредоточиться на этом.

Как вишня на вершине, учтите, что оптимизация никогда не заканчивается. Это не задача с небольшой галочкой, которую вы заканчиваете и переходите к другим задачам. Вы всегда можете выполнить «еще одну оптимизацию», и большая часть любого развития заключается в понимании приоритетов. Вы не проводите оптимизацию ради оптимизации - вы делаете это для достижения конкретной цели (например, «200 единиц на экране одновременно на Pentium 333 МГц» - отличная цель). Не забывайте о конечной цели только потому, что вы слишком много внимания уделяете промежуточным целям, которые могут даже не быть предпосылками для конечной цели.

Luaan
источник
Я считаю, что разработчики Factio теперь снова оптимизируют пояса, так что им нужно только периодически моделировать предметы, а в остальное время он просто интерполирует их положение, когда вы на них смотрите. Но они делают это только сейчас, когда вся игра эффективно базировалась на поясах (и роботах) в течение длительного времени, а не до тех пор, пока люди не начали замечать и жаловаться на производительность.
immibis
2
@immibis Точно. Заставить его работать лучше, прежде чем люди действительно достигнут пределов, было бы пустой тратой ресурсов, которые лучше потратить в другом месте. Даже с учетом последних оптимизаций вы, скорее всего, столкнетесь с проблемой только для мега-заводов, выходящих далеко за рамки обычного игрового процесса (запуска ракеты). Но он включил новые настройки геймплея Marathon, которые требуют гораздо более тяжелой логистики. И снова, они определили узкие места, получив статистику от людей, фактически играющих в игру - около половины узких мест были для них большим сюрпризом.
Луаан
@ Толстяк Так ты понимаешь вопрос, но не ответ? Вы просто пытаетесь сказать, что возможен более простой ответ (например, «Экономика»)? Я не понимаю, как ваш комментарий должен быть конструктивным.
Луаан
2
@ Толстяк Если вы думаете, что ответ «Оптимизируйте только тогда, когда вы знаете, что является узким местом», опубликуйте его как ответ. Вы уже потратили намного больше слов, чем потребовалось бы для хорошего ответа, так что продолжайте. Какой вид оптимизации не делает вещи лучше и хуже? Я, конечно, никогда не видел ни одного. Я повторяю фундаментальную вещь: это всегда компромисс - время, которое лучше потратить в другом месте, сложность, которая ограничивает вашу гибкость ... Примеры указывают на то, что «рано» не является абсолютным термином; некоторые оптимизации необходимы с первого дня, в то время как другие вредны.
Луаан
1
@Fattie "Оптимизация только тогда, когда вы знаете, что является узким местом" - это ответ на вопрос "Когда мне следует оптимизировать?" и это не ответ на вопрос «Почему так плохо оптимизировать слишком рано?».
immibis
10

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

Обмани меня, когда я пытаюсь уточнить мою точку зрения с помощью polyominoes.

Предположим, у нас есть некоторые фиксированные границы, установленные фреймворком или движком, с которым мы работаем.

введите описание изображения здесь

Затем мы приступаем к созданию нашего первого слоя / модуля игры следующим образом.

введите описание изображения здесь

Двигаясь дальше, мы строим наш второй слой / модуль.

введите описание изображения здесь

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

введите описание изображения здесь

Отлично, теперь приложение полностью использует доступные нам ресурсы, приложение лучше, хорошо, правда?

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

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

Итак, мы собрали все вместе ...

введите описание изображения здесь

Хм, ты видишь, что случилось?

Оптимизируя слишком рано, мы теперь фактически ухудшили ситуацию с точки зрения эффективности, поскольку то, против чего мы оптимизировали, - это не то, что мы в итоге получили.

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

введите описание изображения здесь

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

Однако, если бы мы решили подождать с нашим стремлением немедленно его оптимизировать, мы бы получили что-то вроде этого:

введите описание изображения здесь

И теперь, если мы выполним оптимизацию на этом этапе, мы получим что-то удовлетворяющее.

введите описание изображения здесь

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

Потолочный геккон
источник
3
Я удаляю рекламу. Нам все равно, как вы сделали изображения; единственной важной частью является предположение, что вы имеете право размещать их.
Гнемлок
2
@ Гнемлок достаточно справедлив, хотя мое намерение не было направлено на рекламу, я также вижу, как его можно легко неправильно истолковать как таковое, и я согласен, что оно ничего не добавляет к ответу, поэтому ему не место в нем.
Потолок
2
Я думаю, что эта визуальная метафора чрезвычайно эффективна. Отличный подход к ответу на вопрос! :)
DMGregory
8

Деньги.

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

Вот еще несколько потенциальных побочных эффектов оптимизации слишком рано и причины, по которым вам следует избегать этого:

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

* Другие ответы достаточно хорошо выделили часть времени


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

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

Александр Vaillancourt
источник
1
@JBentley, это верный момент. Тем не менее, есть прямой ответ на вопрос «почему» как прямое следствие накопленного опыта. Итак, 1) ранняя оптимизация может привести к потере времени на части кода, которые не влияют на среднее время выполнения (учитывая опыт, вы должны знать, какие конструкции кода являются кандидатами для оптимизации)) 2) ранняя оптимизация может усложнить код поддерживать из-за проектных ограничений, наложенных на некоторую систему. Таким образом, вы можете получить слишком мало в обмен на более длительные циклы разработки / громоздкий код для обслуживания.
Теодрон
3
@JBentley Рассмотрите это как дополнение к ответу Д.Г.Григория.
Александр Vaillancourt
1
Казалось бы, это единственный достойный ответ здесь. Вопрос в тавтологии. Вы можете также спросить: «Так почему же вы хотите, чтобы игра приносила больше, а не меньше денег в магазинах приложений?» Как вы можете ответить на это?
Толстяк
@ Толстяк Хорошо, что в ответе есть такая перспектива. Но сказать, что это единственный стоящий ответ, неоправданно сужает область вопроса. Например, вы предполагаете, что единственные игры сделаны внутри коммерческих организаций (явно не соответствует действительности). Вы также предполагаете, что для ОП очевидно, что оптимизация на ранних этапах ведет к увеличению затрат времени (и, следовательно, к увеличению денег, если мы говорим о бизнесе). На самом деле вопрос ОП показывает, что он не уверен в этом.
Дж
6

Оптимизация - это, по определению, процесс повышения эффективности решения до того момента, когда оно теряет свою эффективность. Этот процесс подразумевает сокращение пространства решения.

На ранней стадии разработки программного обеспечения все еще могут быть «скрытые требования»: если вы уменьшите слишком много места для своего решения, вы можете оказаться в ситуации, когда вы не сможете удовлетворить «скрытое требование», когда оно появляется на более позднем этапе разработки вы будете вынуждены затем модифицировать архитектуру, добавляя таким образом нестабильность и возможное множество нежелательных действий.

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

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

Заставь это работать первым. Затем затяните веревки.

Earendel
источник
2
Я думаю, что «эффективность» - это не то слово, к которому вы стремитесь - оно означает «способность производить эффект», так что в некотором смысле оптимизация - это повышение эффективности. Собираетесь ли вы для какой-то конкретной версии эффективности? (Не могли бы вы дать ссылку на него?) Вы подразумеваете что-то вроде гибкости?
doppelgreener
2
@doppelgreener Это означает, что вы повышаете эффективность решения до тех пор, пока оно не прекратит создавать
нужные
1
«Сначала заставь это работать. Затем затяни веревки». - Это должно стоить столько же, сколько остальные ответы вместе взятые. И не сказать, что другие вещи не были хорошими.
ebyrob
1
@ebyrob: есть еще одна поговорка: «Сделай так, сделай это правильно, сделай это быстро» wiki.c2.com/?MakeItWorkMakeItRightMakeItFast
ninjalj
@ninjalj Это тоже хорошо. Конечно, мой начальник всегда говорит мне: «Не торопись, сделай это правильно». Так что я не знаю, является ли то, что вы говорите, настолько же универсальным, как и принципал, о котором оригинальный автор запрашивает более подробную информацию.
ebyrob
2

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

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

В общем, оптимизируйте, только если вы действительно не можете работать со сборкой разработки прямо сейчас. Например, если вы создаете и удаляете миллионы объектов 60 раз в секунду.

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

user1306322
источник
2

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

Оптимизация не дает понимания. Это заставляет компьютер работать меньше, а программист - больше.

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

user101307
источник
Это можно было бы улучшить, руководствуясь чем-то иным, чем определением оптимизации, и поговорив о практической ситуации в разработке игр, а не о аналогиях с автомобилями.
Doppelgreener
Идеальный ответ на тавтологический вопрос.
Толстяк
2

Я категорически не согласен со всеми этими утверждениями о том, что код не должен быть оптимизирован на ранних стадиях. Это зависит от того, на каком этапе вы находитесь. Если это MVP - прототип и вы просто пытаетесь посмотреть на игру, которая занимает от 1 до 2 недель, то вы готовы переписать все, что вы уже написали. Да, оптимизация не имеет значения. Но если вы уже работаете над игрой, которая, как вы знаете, будет выпущена, она должна была оптимизировать код с самого начала. Сказки, которые люди говорят о новых возможностях и вещах, которые можно добавить, - это неправильное представление. Это плохо спроектированная архитектура кода, а не оптимизированный код. Вам не нужно ничего переписывать, чтобы добавлять новые элементы, если у вас хороший дизайн кода.

Например, если у вас есть алгоритм поиска пути A *, не лучше ли написать его наиболее оптимальным способом? Вместо того, чтобы позже менять половину кода, который у вас есть, потому что вам пришлось внести некоторые изменения в алгоритм, потому что теперь ему нужны другие вызовы методов и обратные вызовы? И если у вас уже есть оптимизированный код с самого начала, это сэкономит вам много времени. Потому что вы можете сами нарисовать отношения между объектами и тем, как они взаимодействуют, вместо того, чтобы слепо создавать тонны новых сценариев и сразу создавать все соединения - это приводит к неясному sphagetti-коду.

Последнее, что я хочу добавить, - это то, что позже, даже если вы больше не хотите создавать игру, у вас есть оптимизированный алгоритм A *, который вы можете использовать для следующих игр. Повторное использование. Например, во многих играх есть инвентарь, поиск пути, процедурная генерация, взаимодействие между NPC, боевая система, AI, взаимодействие с интерфейсом, управление вводом. Теперь вы должны спросить себя: «Сколько раз я переписывал эти элементы с нуля?» Они составляют 70-80% игры. Вам действительно нужно переписывать их все время? А что если они неоптимизированы?

Candid Moon _Max_
источник
5
Если код A * должен быть оптимизирован, насколько оптимизирован «оптимизирован»? Вы вручную кодируете вещи перед сборкой? Я думаю, что в целом нетривиальная работа по микрооптимизации должна быть оставлена ​​до тех пор, пока не будут выполнены контрольные показатели. Оптимизирующий компилятор может лучше справляться с микрооптимизацией, чем вы, и намного дешевле.
Gmatht
@gmatht Ну, A * может отличаться, так как я сказал, что он может иметь плохой дизайн и работать не очень хорошо. Но он может быть многопоточным, основанным на куче Фибоначчи, иметь некоторые прогнозы, разбиваться на куски. Если мы говорим о поиске пути, мы можем добавить поле потока, которое будет смешано с A *. Также способ сглаживания, мульти высота. Возможно, A * не лучший пример, но, как я уже сказал, оптимизация не имеет ничего общего с фактическим изменением кода, разработчик изменяет код, потому что он был плохо спроектирован, например, он не следовал SOLID в качестве одной из причин. Если вы написал это A * ну, вам больше не нужно писать это.
Candid Moon _Max_
@gmatht микро-оптимизации на самом деле не проблема. Я думаю, что OP означает лучший и наиболее эффективный способ вычисления поведения AI, шейдера или чего-либо еще.
Candid Moon _Max_
Если вы знаете, как написать это эффективно, я не вижу проблемы в этом. Это займет столько же времени или даже меньше. Это зависит больше от опыта разработчиков. Как программист, если бы я знал лучшее решение, я бы не написал дерьмовое. Если я знаю, как написать кучу Фибоначчи, я не буду использовать Список и сортировку, чтобы получить минимальное или максимальное значения. Конечно, для его написания потребуется больше времени и усилий, чем для использования List.Sort (); , но что, если у меня уже есть многоразовый?
Candid Moon _Max_
2
@CandidMoon Я не согласен с «Это займет столько же времени или даже меньше». написать эффективный код. Возможно, это заняло бы то же самое время, чтобы написать код, который не является явно неэффективным, но когда ваше представление об «эффективности» означает рассмотрение конкуренции за кэш, добавление многопоточности, использование специфических для ЦП встроенных функций, доступных только на некоторых ЦП, переключение алгоритмов для большого N из-за O (N log N) против O (N) - такие вещи сложны и подвержены ошибкам и занимают намного больше времени, чем простая реализация. Вы делаете это только тогда, когда знаете, что это существенно повлияет на конечный результат.
Майкл Андерсон