Немного взглянув на оптимизацию, я обнаружил (буквально повсюду), что кажется общепризнанным грехом слишком рано оптимизировать игру.
Я действительно не понимаю этого, не будет ли невероятно сложно изменить некоторые основные структуры игры в конце, вместо того, чтобы разрабатывать их в первый раз с учетом производительности?
Я получаю, что ожидание, пока игра не закончится, скажет вам, если вам даже нужны оптимизации, но не следует ли вам делать это в любом случае, в конце концов, это может расширить разнообразие устройств, на которых может работать игра, что увеличит количество потенциальных возможностей. игроки.
Может ли кто-нибудь объяснить мне, почему такая плохая идея оптимизировать слишком рано?
performance
optimization
MR-матовый
источник
источник
Ответы:
Преамбула:
В комментариях было высказано несколько возражений, и я думаю, что они во многом связаны с неправильным пониманием того, что мы имеем в виду, когда говорим «преждевременная оптимизация», - поэтому я хотел бы добавить небольшое разъяснение по этому поводу.
«Не оптимизировать преждевременно» не означает «писать код, который вы знаете, это плохо, потому что Кнут говорит, что вам не разрешено его чистить до конца»
Это означает, что «не жертвуйте временем и удобочитаемостью для оптимизации, пока вы не узнаете, какие части вашей программы действительно нуждаются в ускорении». Поскольку обычная программа проводит большую часть своего времени в нескольких узких местах, инвестиции в оптимизацию «всего» могут не дать вам такой же прирост скорости, как сосредоточение тех же инвестиций только на узком месте кода.
Это означает, что в случае сомнений мы должны:
Предпочитайте код, который легко писать, понятен и легко модифицировать для начинающих
Проверьте, нужна ли дальнейшая оптимизация (как правило, путем профилирования работающей программы, хотя один комментарий ниже отмечает выполнение математического анализа - единственный риск, который существует, вы также должны проверить, что ваша математика верна)
Преждевременная оптимизация не является :
Архитектурные решения для структурирования вашего кода таким образом, чтобы он соответствовал вашим потребностям - выбирая подходящие модули / обязанности / интерфейсы / коммуникационные системы продуманным образом.
Простая эффективность, которая не требует дополнительного времени или затрудняет чтение кода. Такие вещи, как использование строгой типизации, могут быть эффективными и сделать ваши намерения более ясными. Кэширование ссылки вместо ее повторного поиска - это еще один пример (если ваш случай не требует сложной логики аннулирования кэша - возможно, отложите запись, пока вы сначала не профилируете простой способ).
Используя правильный алгоритм для работы. A * является более оптимальным и более сложным, чем исчерпывающий поиск графа поиска пути. Это также отраслевой стандарт. Повторяя тему, придерживаясь таких проверенных методов, на самом деле ваш код легче понять, чем если бы вы делали что-то простое, но в отличие от известных лучших практик. Если у вас есть опыт работы с узкими местами, реализующими игровую функцию X одним способом в предыдущем проекте, вам не нужно снова сталкиваться с тем же узким местом в этом проекте, чтобы знать, что это реально - вы можете и должны повторно использовать решения, которые работали в прошлом. игры.
Все эти типы оптимизаций вполне оправданы и, как правило, не будут помечены как «преждевременные» (если только вы не спускаетесь по кроличьей норе, внедряя передовой поиск путей для вашей карты шахматной доски 8x8 ...)
Итак, теперь, когда все прояснилось, почему мы можем найти эту политику полезной в играх, в частности:
Особенно в gamedev скорость итерации - самое ценное. Мы часто реализуем и повторно реализуем гораздо больше идей, чем в конечном итоге поставим с готовой игрой, пытаясь «найти удовольствие».
Если вы можете прототипировать механику простым и, возможно, немного наивным способом и тестировать ее на следующий день, вы окажетесь в гораздо лучшем положении, чем если бы вы потратили неделю на то, чтобы сначала сделать наиболее оптимальный вариант. Особенно, если это оказывается неудачным, и вы в конечном итоге выбрасываете эту функцию. Выполнение этого простым способом, позволяющим провести раннее тестирование, может сэкономить массу потерянной работы, оптимизируя код, который вы не сохраняете.
Неоптимизированный код, как правило, также легче модифицировать и пробовать другие варианты, чем код, который точно настроен для оптимального выполнения одной точной задачи, которую обычно трудно ломать и сложнее модифицировать, не ломая ее, не вводя ошибок или не замедляя его. Поэтому поддержание кода простым и легким для изменения часто стоит небольшой неэффективности времени выполнения на протяжении большей части разработки (мы обычно разрабатываем на машинах, превышающих целевую спецификацию, поэтому мы можем поглотить накладные расходы и сосредоточиться на получении целевого опыта в первую очередь), пока мы Мы заблокировали то, что нам нужно от этой функции, и можем оптимизировать части, которые, как мы теперь знаем, работают медленно.
Да, рефакторинг частей проекта на поздней стадии разработки для оптимизации медленных мест может быть трудным. Но то же самое происходит с рефакторингом на протяжении всей разработки, потому что оптимизации, которые вы сделали в прошлом месяце, не совместимы с направлением, в котором развивалась игра с тех пор, или исправляли что-то, что оказалось не самым узким местом, когда вы получили больше функций и контента. в.
Игры странные и экспериментальные - трудно предсказать, как будет развиваться игровой проект и его технические потребности и где производительность будет минимальной. На практике мы часто начинаем беспокоиться о неправильных вещах - ищите ответы на вопросы о производительности здесь, и вы увидите, что появляется общая тема, в которой разработчики отвлекаются на бумагу, что, вероятно, вообще не является проблемой.
В качестве драматического примера: если ваша игра связана с GPU (не редкость), то все это время, потраченное на гипероптимизацию и многопоточность работы ЦП, может вообще не принести ощутимой выгоды. Все эти часы разработки могли бы быть потрачены на реализацию и оттачивание функций игрового процесса для лучшего игрового опыта.
В целом, большую часть времени, которое вы проводите, работая над игрой, не будет потрачено на код, который в итоге оказывается узким местом. Особенно, когда вы работаете над существующим движком, очень дорогие компоненты внутреннего цикла в системах рендеринга и физики в значительной степени не в ваших руках. В этот момент ваша задача в сценариях геймплея заключается в том, чтобы в основном не мешать движку - если вы не добавите туда гаечный ключ, то вы, вероятно, выйдете довольно неплохо для первой сборки.
Таким образом, кроме небольшого количества гигиены кода и бюджетирования (например, не повторяйте поиск / конструирование чего-либо, если вы можете легко использовать его повторно, сохраняйте свои запросы на поиск пути / физики или скриншоты обратного вызова графического процессора и т. Д.), Делая привычку не более - оптимизация до того, как мы узнаем, где реальные проблемы, оказывается полезной для производительности - избавляет нас от необходимости тратить время на оптимизацию неправильных вещей и делает наш код проще и проще в целом.
источник
примечание: этот ответ начинался как комментарий к ответу Д.Г.Григория, и поэтому он не дублирует очень хорошие замечания, которые он высказывает.
«Разве не будет невероятно трудно изменить некоторые основные структуры игры в конце, вместо того, чтобы разрабатывать их в первый раз с учетом производительности?»
Это, для меня, суть вопроса.
Создавая свой оригинальный дизайн, вы должны стараться проектировать для эффективности - на высшем уровне. Это меньше оптимизации, а больше о структуре.
Пример:
вам нужно создать систему для пересечения реки. Очевидные проекты - мост или паром, так что вы выбираете?
Ответ, конечно, зависит от размера перекрестка и объема трафика. Это не оптимизация, а начинание с дизайна, подходящего для вашей проблемы.
Когда вам предлагают выбор дизайна, вы выбираете тот, который лучше всего подходит для того, что вы хотите сделать.
Итак, допустим, что наш объем трафика довольно низкий, поэтому мы решили построить два терминала и купить паром для обработки трафика. Хорошая простая реализация
К сожалению, как только мы его запустим, мы обнаружим, что он видит больше трафика, чем ожидалось. Нам нужно оптимизировать паром! (потому что это работает, и строительство моста сейчас не очень хороший план)
Параметры:
Здесь вы должны попытаться сделать свой оригинальный дизайн как можно более модульным.
Все вышеперечисленное является возможной оптимизацией, и вы могли бы даже сделать все три.
Но как вы делаете эти изменения без больших структурных изменений?
Если у вас есть модульная конструкция с четко определенными интерфейсами, то эти изменения должны быть простыми.
Если ваш код не тесно связан, то изменения в модулях не влияют на окружающую структуру.
Давайте посмотрим на добавление дополнительного парома.
«Плохая» программа может быть построена вокруг идеи единого парома, и иметь состояния стыковки и состояния парома, а также объединять и объединять все состояния. Это будет трудно изменить, чтобы добавить дополнительный паром в систему.
Лучшим дизайном было бы иметь доки и паром как отдельные объекты. Между ними нет тесной связи, но у них есть интерфейс, куда паром может прибыть, разгрузить пассажиров, взять новых и уйти. Док-станция и паром совместно используют только этот интерфейс, и это позволяет легко вносить изменения в систему, в этом случае путем добавления второго парома. Дока не волнует, какие паромы там на самом деле, все, что его интересует, это то, что что-то (что-либо) использует его интерфейс.
ТЛ; др:
Затем вы можете изменить механизмы внутри каждого модуля, не перестраивая всю кодовую базу, когда вам нужно оптимизировать.
источник
IRiverCrossing
, а когда станет ясно, что объем трафика слишком велик для его установки, реализовать мост какIRiverCrossing
и сбросить его. Конечно, хитрость заключается в сокращении внешнего API парома. и мост к общей функциональности, чтобы они могли быть представлены одним и тем же интерфейсом.«Не оптимизировать рано» не означает «выбрать худший из возможных способов». Вам все еще нужно учитывать влияние на производительность (если только вы не создаете прототипы). Дело не в том, чтобы наносить ущерб другим, более важным вещам на данном этапе разработки, таким как гибкость, надежность и т. Д. Выбирайте простые, безопасные оптимизации - выбирайте вещи, которые вы ограничиваете, и вещи, которые вы держите свободными; отслеживать расходы. Стоит ли использовать строгую типизацию? Большинство игр работали и работают нормально; сколько вам стоило бы удалить это, если бы вы нашли интересное использование гибкости для геймплея?
Намного сложнее модифицировать оптимизированный код, особенно «умный» код. Это всегда выбор, который делает некоторые вещи лучше, а другие хуже (например, вы могли бы тратить процессорное время на использование памяти). Делая такой выбор, вы должны знать обо всех последствиях - они могут быть катастрофическими, но они также могут быть полезными.
Например, Commander Keen, Wolfenstein и Doom были созданы на основе оптимизированного движка рендеринга. У каждого был свой «трюк», который позволил игре существовать в первую очередь (у каждого также была дальнейшая оптимизация, разработанная с течением времени, но это не важно здесь). Это нормально . Это нормально - сильно оптимизировать саму суть игры, мысль, которая делает игру возможной; особенно, если вы исследуете новую территорию, где эта конкретная оптимизированная функция позволяет вам рассматривать игровые конструкции, которые не были сильно изучены. Ограничения, которые вводит оптимизация, могут также дать вам интересный игровой процесс (например, ограничения количества юнитов в играх RTS могут начаться как способ повышения производительности, но они также имеют эффект игрового процесса).
Но обратите внимание, что в каждом из этих примеров игра не может существовать без оптимизации. Они не начинали с «полностью оптимизированного» двигателя - они начинали с крайней необходимости и продвигались вверх. Они разрабатывали новые технологии и использовали их для создания забавных игр. И уловки движка были ограничены настолько малой частью кодовой базы, насколько это возможно - более тяжелые оптимизации были введены только тогда, когда игровой процесс был в основном завершен, или когда он позволил появиться интересной новой функции.
Теперь рассмотрим игру, которую вы можете захотеть сделать. Есть ли какое-то технологическое чудо, которое делает или разрушает эту игру? Может быть, вы представляете игру с открытым миром в бесконечном мире. Это действительно центральная часть игры? Будет ли игра просто не работать без нее? Возможно, вы думаете об игре, в которой местность деформируется без ограничений, с реалистичной геологией и тому подобным; Вы можете заставить это работать с меньшим объемом? Будет ли это работать в 2D вместо 3D? Получите что-нибудь интересное как можно скорее - даже если для оптимизации может потребоваться переработать огромный кусок существующего кода, это может стоить того; и вы можете даже понять, что увеличение масштабов на самом деле не делает игру лучше.
В качестве примера недавней игры с большим количеством оптимизаций я бы указал на Factorio. Одной из важнейших частей игры являются ремни - их много тысяч, и они несут множество отдельных кусочков материалов по всей вашей фабрике. Игра началась с сильно оптимизированного ременного двигателя? Нет! На самом деле, оригинальную конструкцию ремня было почти невозможно оптимизировать - он как бы выполнял физическое моделирование предметов на ремне, что создавало некоторые интересные вещи, которые вы могли бы сделать (именно так вы получаете «эмерджентный» геймплей - геймплей, который удивляет дизайнер), но это означало, что вы должны были смоделировать каждый элемент на поясе. С тысячами ремней вы получаете десятки тысяч предметов, смоделированных физически - даже если вы просто удалите их и дадите ремням работу, вы сможете сократить время, затрачиваемое на процессор, на 95-99%, даже без учета таких вещей, как локальность памяти. Но это полезно делать только тогда, когда вы действительно достигаете этих пределов.
Практически все, что имело отношение к ремням, должно было быть переделано, чтобы можно было оптимизировать ремни. И ремни нужно было оптимизировать, потому что вам нужно было много ремней для большой фабрики, а крупные фабрики - это одна из привлекательных сторон игры. В конце концов, если у вас не может быть больших фабрик, зачем нужен бесконечный мир? Забавно, что вы должны спросить - ранние версии этого не делали :) Игра была переработана и изменена много раз, чтобы добраться до того места, где они сейчас находятся - в том числе 100% -ный римейк, когда они поняли, что Java не является подходящим способом для игра, как это и перешел на C ++. И это сработало отлично для Factorio (хотя это было все же хорошо, что не было оптимизировано с самого начала - тем более, что это был хобби-проект, который мог бы просто потерпеть неудачу из-за отсутствия интереса).
Но дело в том, что естьмногие вещи вы можете сделать с фабрикой ограниченного масштаба - и многие игры показали это. Ограничения могут быть даже более вдохновляющими, чем свободы; будет ли Spacechem более увлекательным, если бы «карты» были бесконечными? Если бы вы начали с сильно оптимизированных «поясов», вы бы в значительной степени были бы вынуждены пойти по этому пути; и вы не могли бы исследовать другие направления дизайна (например, посмотреть, что интересного вы можете сделать с помощью имитирующих физику конвейерных лент). Вы ограничиваете свое потенциальное пространство дизайна. Может показаться, что это не так, потому что вы не видите много незавершенных игр, но сложная часть состоит в том, чтобы получить удовольствие от веселья - для каждой забавной игры, которую вы видите, возможно, есть сотни, которые просто не могли попасть туда и были выброшены (или хуже, выпущен как ужасные путаницы). Если оптимизация поможет вам это сделать - продолжайте. Если это не так ... это скорее преждевременно. Если вы думаете, что какая-то механика геймплея работает отлично, но нуждается в оптимизации, чтобы действительно сиять - продолжайте. Если у вас нет интересной механики,не оптимизируйте их . Сначала найдите забаву - вы обнаружите, что большинство оптимизаций не помогают с этим, и часто являются вредными.
Наконец, у вас есть отличная, веселая игра. Есть ли смысл оптимизировать сейчас ? Ха! Это все еще не так ясно, как вы думаете. Есть что-то веселоевы можете сделать вместо этого? Не забывайте, что ваше время все еще ограничено. Все требует усилий, и вы хотите сосредоточить эти усилия на том, где это наиболее важно. Да, даже если вы делаете «бесплатную игру» или «игру с открытым исходным кодом». Смотрите, как играется в игру; обратите внимание, где производительность становится узким местом. Оптимизация этих мест приносит больше удовольствия (например, строительство больших и запутанных фабрик)? Позволяет ли вам привлечь больше игроков (например, с более слабыми компьютерами или на разных платформах)? Вам всегда нужно расставлять приоритеты - ищите соотношение усилий и доходности. Скорее всего, вы найдете множество низко висящих фруктов, просто играя в свою игру и наблюдая, как другие играют в игру. Но обратите внимание на важную часть - чтобы попасть туда, вам нужна игра . Сосредоточиться на этом.
Как вишня на вершине, учтите, что оптимизация никогда не заканчивается. Это не задача с небольшой галочкой, которую вы заканчиваете и переходите к другим задачам. Вы всегда можете выполнить «еще одну оптимизацию», и большая часть любого развития заключается в понимании приоритетов. Вы не проводите оптимизацию ради оптимизации - вы делаете это для достижения конкретной цели (например, «200 единиц на экране одновременно на Pentium 333 МГц» - отличная цель). Не забывайте о конечной цели только потому, что вы слишком много внимания уделяете промежуточным целям, которые могут даже не быть предпосылками для конечной цели.
источник
Многие ответы, кажется, сосредоточены на аспекте производительности «оптимизации», в то время как мне самому нравится смотреть на оптимизацию и все испытания оптимизации слишком рано на более абстрактном уровне.
Обмани меня, когда я пытаюсь уточнить мою точку зрения с помощью polyominoes.
Предположим, у нас есть некоторые фиксированные границы, установленные фреймворком или движком, с которым мы работаем.
Затем мы приступаем к созданию нашего первого слоя / модуля игры следующим образом.
Двигаясь дальше, мы строим наш второй слой / модуль.
В этот момент мы можем заметить, что между двумя модулями есть свободное пространство, и мы можем испытать желание оптимизировать его, чтобы в полной мере использовать выделенные нам границы.
Отлично, теперь приложение полностью использует доступные нам ресурсы, приложение лучше, хорошо, правда?
Мы приступаем к созданию третьего уровня / модуля нашего приложения, и внезапно мы осознаем (возможно, даже тот, который мы не могли предвидеть при первоначальном планировании), что третий уровень / модуль у нас не работает.
Мы ищем альтернативу, мы находим ее, и, в конце концов, это также требует от нас изменения 2-го модуля нашего приложения, поскольку он несовместим с нашим недавно выбранным 3-м модулем. (К счастью, он несколько совместим с нашим первым модулем, поэтому нам не нужно переписывать все с нуля.)
Итак, мы собрали все вместе ...
Хм, ты видишь, что случилось?
Оптимизируя слишком рано, мы теперь фактически ухудшили ситуацию с точки зрения эффективности, поскольку то, против чего мы оптимизировали, - это не то, что мы в итоге получили.
И если бы мы захотели добавить некоторые дополнительные модули или дополнительные лакомые кусочки после этого, у нас больше не было бы возможности делать их на этом этапе.
И регулировка самого низкого уровня нашей системы больше не возможна, так как она была похоронена под всеми другими слоями.
Однако, если бы мы решили подождать с нашим стремлением немедленно его оптимизировать, мы бы получили что-то вроде этого:
И теперь, если мы выполним оптимизацию на этом этапе, мы получим что-то удовлетворяющее.
Надеюсь, что, по крайней мере, некоторым из вас было так же весело читать это, как и мне было весело делать это :), и если вы сейчас чувствуете, что лучше разбираетесь в этом вопросе - тем лучше.
источник
Деньги.
Это сводится к этому. Деньги. Поскольку время - деньги *, чем больше времени вы тратите на действия, которые не гарантируют получение большего количества денег (т.е. вы не можете рассматривать эти действия как инвестиции ), тем больше денег вы тратите впустую, и тем меньше денег вы зарабатываете игра.
Вот еще несколько потенциальных побочных эффектов оптимизации слишком рано и причины, по которым вам следует избегать этого:
* Другие ответы достаточно хорошо выделили часть времени
Как дополнительное примечание, как правило , опыт помогает определить, что является, а что нет, преждевременной оптимизацией, а также тем, что имеет ценность для бизнеса, а что нет.
Например, если вы работали над игрой A и к концу проекта поняли, что функция XYZ была особенно тяжелой в вашем игровом цикле, и вы в конечном итоге начинаете работать над игрой B, которая имеет точно такую же функцию, решив переписать функцию и оптимизировать ее с самого начала не является преждевременной оптимизацией, так как вы знаете, что это будет узким местом, если ничего не будет сделано.
источник
Оптимизация - это, по определению, процесс повышения эффективности решения до того момента, когда оно теряет свою эффективность. Этот процесс подразумевает сокращение пространства решения.
На ранней стадии разработки программного обеспечения все еще могут быть «скрытые требования»: если вы уменьшите слишком много места для своего решения, вы можете оказаться в ситуации, когда вы не сможете удовлетворить «скрытое требование», когда оно появляется на более позднем этапе разработки вы будете вынуждены затем модифицировать архитектуру, добавляя таким образом нестабильность и возможное множество нежелательных действий.
Идея состоит в том, чтобы заставить все решение работать, и только тогда, когда все требования будут исправлены и реализованы, ужесточить код. Тогда вы увидите, что многие оптимизации, которые вы без особого труда реализовали бы сразу во время кодирования, теперь более не осуществимы из-за взаимодействия с запоздалыми требованиями.
Реальное пространство решения всегда больше, чем мы ожидали в начале, потому что у нас не может быть совершенного знания очень сложной системы.
Заставь это работать первым. Затем затяните веревки.
источник
Короче говоря, очень часто ранняя оптимизация становится напрасной тратой усилий, если вы хотите что-то изменить позже, и тогда оказывается, что вы оптимизировали легко изменяемые структуры кода до чего-то гораздо более низкого уровня, и теперь вам нужно изменить его обратно на высокий уровень. уровень подхода и оптимизировать результат еще раз.
Это распространенная ошибка для начинающих разработчиков, которые любят сосредоточиться на получении удовольствия от «полезной работы», такой как оптимизация, не думая о том, настало ли время сделать это. По мере приобретения опыта программирования больших проектов, таких как игры, вы узнаете, когда необходимо оптимизировать существующую кодовую базу и когда это слишком рано. Не бойтесь совершить эту ошибку, вы извлечете из нее пользу.
В общем, оптимизируйте, только если вы действительно не можете работать со сборкой разработки прямо сейчас. Например, если вы создаете и удаляете миллионы объектов 60 раз в секунду.
Итак, я бы сказал, что с точки зрения учебного процесса хорошо бы оптимизировать несколько раз заранее: p
источник
Оптимизация направлена на то, чтобы заставить компьютер лучше работать с кодом, в то время как разработка требует, чтобы программист лучше всего работал с кодом.
Оптимизация не дает понимания. Это заставляет компьютер работать меньше, а программист - больше.
Конечно, есть разные категории, например, с дизайном автомобиля. Нет ничего плохого в оптимизации двигателя перед сборкой первого шасси, но оптимизация шасси до того, как вы не знаете форму двигателя, может привести к пустой трате времени. Модульность и технические характеристики могут сделать множество мест пригодными для оптимизации работы даже до того, как продукт в целом будет собран.
источник
Я категорически не согласен со всеми этими утверждениями о том, что код не должен быть оптимизирован на ранних стадиях. Это зависит от того, на каком этапе вы находитесь. Если это MVP - прототип и вы просто пытаетесь посмотреть на игру, которая занимает от 1 до 2 недель, то вы готовы переписать все, что вы уже написали. Да, оптимизация не имеет значения. Но если вы уже работаете над игрой, которая, как вы знаете, будет выпущена, она должна была оптимизировать код с самого начала. Сказки, которые люди говорят о новых возможностях и вещах, которые можно добавить, - это неправильное представление. Это плохо спроектированная архитектура кода, а не оптимизированный код. Вам не нужно ничего переписывать, чтобы добавлять новые элементы, если у вас хороший дизайн кода.
Например, если у вас есть алгоритм поиска пути A *, не лучше ли написать его наиболее оптимальным способом? Вместо того, чтобы позже менять половину кода, который у вас есть, потому что вам пришлось внести некоторые изменения в алгоритм, потому что теперь ему нужны другие вызовы методов и обратные вызовы? И если у вас уже есть оптимизированный код с самого начала, это сэкономит вам много времени. Потому что вы можете сами нарисовать отношения между объектами и тем, как они взаимодействуют, вместо того, чтобы слепо создавать тонны новых сценариев и сразу создавать все соединения - это приводит к неясному sphagetti-коду.
Последнее, что я хочу добавить, - это то, что позже, даже если вы больше не хотите создавать игру, у вас есть оптимизированный алгоритм A *, который вы можете использовать для следующих игр. Повторное использование. Например, во многих играх есть инвентарь, поиск пути, процедурная генерация, взаимодействие между NPC, боевая система, AI, взаимодействие с интерфейсом, управление вводом. Теперь вы должны спросить себя: «Сколько раз я переписывал эти элементы с нуля?» Они составляют 70-80% игры. Вам действительно нужно переписывать их все время? А что если они неоптимизированы?
источник