Рубрика развивается при поддержке
Gamedev
Codisrq
2126

Микро оптимизации в Unity для программистов

Введение

В закладки
Аудио

Ходят слухи среди программистов, что

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

Это не совсем корректное утверждение.

Объективность заключается в том, что нужно сперва договориться о какой оптимизации идёт речь. Любой диалог - это прежде всего договор о том, какие слова стоит использовать и в каком контексте, какие определения имеют слова и нужно ли уточнять или не нужно, чтобы ваш собеседник вас правильно понимал. Это логично. Иначе задайтесь вопросом "А зачем тогда разговаривать с человеком если он меня плохо/не понимает?" или, если собеседник понял вас не так как должен был по "плану", вам так или иначе придётся разговаривать на языке собеседника, если вы желаете быть услышанным.

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

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

Что такое микро оптимизация?

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

FPS (frames per second) может не хило подняться благодаря микро оптимизациям, если речь идёт о огромном количестве объектов (скажем, 1 000+ объектов) в проекте. Поэтому FPS не может быть критерием для определения, является ли оное микро или макро оптимизацией.

Как использовать? Примеры с Unity движком.

Пример с компонентом.

У вас есть нестандартный компонент, у которого есть переменная, которая должна ссылаться на другой компонент текущего объекта. В этом случае вы декларируете переменную в классе, а затем присваиваете ему значение в методе Awake, либо в Start. Либо вообще задаёте его самостоятельно через стандартный инспектор.

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

private void Reset() {

myComponent = GetComponent<T>();

}

Если объявить метод Reset в вашем компоненте, то Unity вызовет его, как только компонент добавится к объекту. Это избавит вас от ручной работы в инспекторе и даст преимущество в случае, если бы вы присвоили значение в Awake или Start.

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

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

Пример со стандартными библиотеками

1. Я был немного Triggered, когда увидел в исходниках, что стандартная функция Lerp помимо интерполяции делает сравнение. Это касается аргумента t, который должен варьироваться в диапазоне от 0 до 1. Метод Lerp приводит t к этому диапазону с помощью операторов сравнения if, else.

Используйте LerpUnclamped, либо если вы уверенный в своих расчётах, то сделайте себе удобную Lerp функцию без всяких сравнений.

Это касается всех Lerp в классах Mathf, Vector2, Vector3 и т.д.

2. Если вы часто используете Mathf.Approximately - откажитесь от него. И используйте мой метод сравнения двух float переменных:

public static bool Approximately(float a, float b) {

return Abs(a - b) < Max(Max(Abs(a), Abs(b)) * 1E-06f, 0.00001f);

}

Стандартный Mathf.Approximately помимо операций сравнения использует умножения.

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

Для создании переменной также требуется время.

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

5. Используйте [MethodImpl(MethodImplOptions.AggressiveInlining)] или подобное, когда требуется указать компилятору как поступать с методом, например вставлять участок кода текущего метода сразу заместо ссылки. Если сделать так, то никаких лишних обращений к методу не будет. Код метода вставится как константа. Подробнее о MethodImpl тут.

И обратите внимание на замеры таймеров: 7 ns против 0.3 ns.

6. Отбросьте в сторону выборку однотипных переменных с помощью Enum и if else. Лучше объявите массив и обращайтесь напрямую к индексам.

7. Если есть возможность использовать умножения, используйте их, вместо деления. На деление требуется больше времени, чем на умножение.

Делить на два / три / четыре совсем необязательно, когда можно сделать так:

v * 0.5

Конец

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

Cпасибо за внимание. Прошу прощения за оформление, я откровенно говоря ломался перед выбором в оформлении.

Наш телеграм канал.

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

Написать
{ "author_name": "Codisrq", "author_type": "self", "tags": ["if"], "comments": 91, "likes": 49, "favorites": 116, "is_advertisement": false, "subsite_label": "gamedev", "id": 66405, "is_wide": false, "is_ugc": true, "date": "Tue, 27 Aug 2019 23:31:51 +0300", "is_special": false }
0
{ "id": 66405, "author_id": 113347, "diff_limit": 1000, "urls": {"diff":"\/comments\/66405\/get","add":"\/comments\/66405\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/66405"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64954, "last_count_and_date": null }
91 комментарий
Популярные
По порядку
Написать комментарий...
17

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

Ответить
2

Так до конца и не понял в чём смысл Reset, если она относится только к работе в редакторе. Можете разжевать?)

Ответить
2

Reset/Validate отрабатывают только в редакторе, те вы можетете тяжёлые вычисления сделать в EditorTime, тот же GetComponent

Ответить
6

А в сбилженной игре Reset в какой момент отрабатывает?

Ответить
1

Ни в какой.

Ответить
0

и если вы используете Collab то словите нереальный лаг проекта при каждом обращении к облаку, потому что OnValidate выполняется для всех объектов проекта, префабов и т п. Так что такие вещи надо сувать не в резет и валидейт а в [Context menu ("моя клёвая тяжелая функция")]

Ответить
1

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

Ответить
13

Если честно, действительно экономия на спичках.
Не то чтобы Я против заранее подумать про узкие места, но объективно - Lerp и Mathf.Approximately не изменят ничего в 99,9% случаев. Конечно можно писать Unclaimed, но нужно быть уверенным, что значение не выйдет за диапазон.
Даже использование sqrMagnitude вместо magnitude - ерунда.
Все эти штуки будут заметны только на оптовых методах, выполняемых действительно часто, это довольно специфические задачи, под которые еще пойди придумай игровую логику.
Гораздо важнее следить за количеством ресурсов и объектов, уменьшать компоненты, оптимизировать графику, следить за перерисовками (overdraw) и жирностью отображаемых шейдеров, выключать там, где они не нужны тени, блики, упрощать свет и прочее.

Ответить
0

sqrMagnitude и magnitude в вычислениях отдельное спасибо)

Ответить
1

С другой стороны, про особенности поведения таких штук и оптимизацию алгоритмов стоит знать в оставшемся 0.01%, которые дают пик на профайлере.
И память ещё. Вместе с GC постоянный источник гемора - про поведение памяти стоит знать.

Ответить
0

Lerp... оптовых методах... пойди придумай игровую логику

блендинг процедурной геометрии, тех же самописных партиклов (которые, естесственно, рисуются в одном проходе и одним мешем)

Ответить
1

Ну Я еще могу придумать - симуляции, физика, построение мешей, процедурная генерация, много юнитов, воксели.

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

Но да, разумеется иногда такое возникает и замена Lerp на LerpUnclaimed процентик-другой глядишь и даст.

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

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

Ответить
0

Выключение и включение игрового объекта - менее тяжелые операции чем смена родителя

Прикольно, не подозревал.

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

Ответить
0

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

Ответить
0

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

И потому я блендинги спрайтов, текста и эффектов делал на ручном перекрашивании внутри меша, а не менял цвет на материале.

Ответить
0

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

Это может быть быстрее в вырожденном случае, если ты пользуешься устаревшими glbegin/end, но это уже давно не промышленный способ.

Ответить
0

Ну, на давних iPod Touch ручное окрашивание было втрое быстрее управления цветом на материале. Плюс - любое изменение параметра клонировало материал, то бишь, росли дравколы. Помню, народ удивлялся, как у меня в игрушке всего 6-10 дравколов, когда другие не могут вписаться в 60. :)

Ответить
0

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

Ответить
4

Про компоненты
Недавно узнал "удивительный" факт, о котором как-то никогда не задумывался (приложу скриншотом)

Ответить
2

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

Ответить
0

Да, так и есть. По сути это просто тег

Ответить
1

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

Ответить
4

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

Можно вообще с MonoBehaviour на DOTS перелезть, там вообще нет нужды получать компоненты, ибо дата-ориентированный код и всё работает в связке системами. Но тоже, извините, DOTS работает быстрее не из-за того, что там при запуске не получается какой-то там компонент.

2. Вот замена mathf функций собственными это частая практика имеющая смысл. Не только для аппроксимации. С другой стороны, например, mathematics под burst делает своё дело зачастую просто потрясающе. Там часть функционала прямо выражена хардварными SIMD инструкциями.

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

5. Агрессивный инлайнинг вещь, в общем, хорошая, если применяется с умом. Сюда же можно было бы тогда записать развертывание циклов и тому подобные практики. В узких местах вполне себе и помогает.

6. Не понял о чем речь. Что-что отбросить? Может кто-то разжевать, если не сложно? х)

7. Ну, умножение вместо деления это классика.

Ответить
1

Я уже 30 лет не погромист, но от замены деления умножением на дробь всплакнул. Some things just don't get old.

Ответить
4

>> 1

Ответить
1

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

Ответить
0

Да я про то, что уже новое тысячелетие, а способ все тот же :)

Ответить
2

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

Ответить
2

Вместо Reset я написал интерфейс IPrepare и специальный хэндлер, который вызывает Prepare() каждый раз на входе в плэймод.

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

Код и документация
https://github.com/Deadcows/MyBox/blob/master/Tools/Features/IPrepareFeature.cs
https://github.com/Deadcows/MyBox/wiki/Tools-and-Features#iprepare

Ответить
1

Спасибо, особенно за [MethodImpl(MethodImplOptions.AggressiveInlining)]. До этого не читал

Ответить
1

Пожалуйста) Я рад что статья пригодилась

Ответить
2

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

Ответить
1

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

Я тут на днях оптимизировал алгоритм боидов, и мне мозолили глаза 10 локальных переменных. Так вот я и не нашел насколько тормозится программа при создании переменной в стеке. И где она объявляется? Таки в кэше процессора или в ОЗУ?

P.S. Язык С++ если что.

Ответить
1

В C/C++ создание переменной на стеке(без инициализации) - сдвиг stack pointer на значение размера переменной, что по накладным расходам в конечном счете примерно ничего. Более того, компилятор достаточно умен, чтобы объединить несколько объявлений и сдвинуть указатель за одну операцию "sub esp N".

Ответить
0

А если с инициализацией? И таки по скорость то это как влияет? Грубо говоря по сравнению с операцией сложения.

Ответить
1

Для примитивных типов и pod-типов это в большинстве случаев тоже ничего не добавляет(опять же, компилятор не дурак), т.е. будет просто последовательное смещение указателя с присвоением значения. А вот в случае с вызовом конструктора объекта - тут уже все зависит от сложности конструктора. То есть он сперва выделит память, а затем произведет вызов конструктора. Никаких оптимизаций тут уже не провести. Побаловаться с этим можно тут (https://godbolt.org/)

Ответить
0

И где она объявляется? Таки в кэше процессора или в ОЗУ?

Сразу видно не С++ разработчика )

Ответить
0

Ну так просвети аутиста, ответь на вопрос.

Ответить
0

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

Ответить
0

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

Ответить
1

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

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

Ответить
0

В UE4 не лучше. Просто оба движка имеют низкий порог вхождения. Другое дело, что до выпуска продукта не доходит 99,999%

Ответить
0

на шарпах тоже самое все, дпже на ява скрипте v8 сейчас все на стеке лежит.

Ответить
0

Делить на два / три / четыре совсем необязательно, когда можно сделать так: v * 0.5

А на три как сделать?)

Ответить
3

На три v * 0.333 )
Но, я забыл упомянуть про int, блин. В общем int исключение

Ответить
1

Разве такие штуки компилятор сам не оптимизирует? Современные компиляторы довольно умные и сами делают туеву хучу подобных микро-оптимизаций.

Ответить
2

Разве такие штуки компилятор сам не оптимизирует?

Компилятору неоткуда знать какая тебе нужна точность при делении на три. Может тебе надо на 0,33333 умножать, а может и на 0,333333333.

Ответить
1

При делении на 3 понятно, я говорю про 2, что является достаточно однозначным.

Ответить
0

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

Но в юнити, я знаю точно, что компиль не оптимизирует некоторые моменты, описанные здесь

Ответить
0

Мы проверяли умножение и деление на
2, 4, 8, 16 ... Оптимизирует только когда это константа или литерал ( то есть на момент компиляции точно известно) И деление оптимизирует так же.
В случае если это переменная, вроде деление реально дольше.

Ответить
1

Используйте [MethodImpl(MethodImplOptions.AggressiveInlining)]

Да вроде Burst compiler и без этого всё оптимизирует по полной.

Ответить
1

Довольно базовые оптимизации. Вообще, лучший друг при подобном занятии - это исходники юнити, крайне рекомендую: https://github.com/Unity-Technologies/UnityCsReference
Помогают понять, почему многие моменты неочевидно тормознутые и как их оптимизировать.

Ответить
1

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

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

Ответить
0

Regex везде тормознутый и жрёт много памяти.

Ответить
0

В php я не вижу каких-то проблем с регулярками )

Ответить
0

php - одна большая проблема (¬‿¬ )

Ответить
0

Но он шустрый и удобный :)

Ответить
1

Объективность заключается в том, что нужно сперва договориться о какой оптимизации идёт речь.

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

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

Ответить
1

для особо угоревших по оптимизации >> 1 эквивалентео делению на 2 целого числа, << 1 умножерию. и работает еще быстрее

Ответить
–2

private void

Лучшая оптимизация кода в 2019 наконец то перестать использовать private...

Ответить
0

Эм, почему?
В смысле просто void писать или все пабликами фигячить?

Ответить
0

да просто void , если нужнен публичной то public
писать private не нужно.

Ответить
8

Я стараюсь придерживаться одной стилистики) И явно указывать модификатор видимости

Ответить
0

Куда уж явнее если есть паблик или нету

void
public void

или

private void
public void

Ответить
11

Загвоздка лишь в том, что accessibility levels в C# больше, чем 2. И про остальные все благополучно забывают. И то, что для членов класса значением по умолчанию является private не делает это таким же для других типов (например класс по умолчанию - internal).
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/accessibility-levels

Ответить
6

"Явное лучше, чем неявное."

Некто Г.в.Р.

Ответить
2

Я согласен с предыдущим оратором. Стараюсь указывать явно.

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

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

Я волновался про другое скорее - есть товарищи, которые считают за благо сделать все public, чтобы поудобнее.

Ответить
–15

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

Ответить
14

Хорошо что этого никогда не случится...

Ответить
7

Лол, какое время тратится на пустяки) Скорость набивания кода не решает в работе программиста

Ответить
0

Может она секретарь-машинисткой работает...

Ответить
7

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

Ответить
3

Не забудь ещё всех, кто типы переменных указывает, уволить. А то придумали что-то кроме var писать.

Ответить
0

Нет var я сам не всегда люблю, этих оставлю выборочно.

Ответить
0

Вот ты и тратишь время в пустую!

Ответить
2

написать модификатор

тратит лишнее время

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

Ответить
2

Флужу на дтф

Ответить
2

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

Ответить
1

static еще удобнее :)

Ответить
0

Удалено

Ответить
0

У Unity на канале много своих видео с конференций, где много чего про оптимизацию полезного, с намного большим результатом :) https://youtu.be/_wxitgdx-UI

Ответить
0

public static bool Approximately(float a, float b) {
return Abs(a - b) < Max(Max(Abs(a), Abs(b)) * 1E-06f, 0.00001f);
}

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

public static bool Approximately(float a, float b, float e = 0.001f) {
return Mathf.Abs(a - b) <= e;
}

Ответить
0

Это не работает, если a и b очень большие. Код из статьи чуть более правильный

Ответить
0

Я был немного Triggered

Знаешь, я тоже.

Ответить
0

"преждевременное", это в принципе не очень хорошее слово

Ответить
0

И какая же стоимость "создания переменной"?

Ответить
0

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

Ответить
0

для создании переменной также требуется время. чаво?? )))) на х86 она будет в стеке либо ее вообще не будет после оптимизации. что за гоупости

Ответить

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovz", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } } ]