Вот я и завидую: пока прогонишь и проанализируешь свои рабочие бенчмарки и поэксперементируешь с кодом - ни на какие другие бенчмарки, и уж тем более статьи, никаких ресурсов не остаётся ;) А вот почитать - самое то, спасибо.
И искренне завидую - у меня на подобное не остаётся ни времени, ни сил.
Просто интересно: а какая мотивация вкладывать столько времени и усилий? Это чистое любопытство или, может быть, побочный продукт реализации каких-либо оптимизаций?
Коротко: 3D экшен с тремя игровыми фракциями — лесные эльфы, дворцовая охрана и злодей — каждая с уникальным геймплеем, целями и стилем прохождения. Игрок выбирает фракцию и проходит кампанию, открывает навыки, участвует в динамических стычках и миссиях в открытом мире с деревнями, лесами, дорогами и караванами для грабежа.
Выбранный объём и допущения
Я предполагаю, что под «корованы» вы имели в виду караваны — передвижные торговые колонны, которые можно грабить. Я также беру за основу одиночную кампанию с опцией кооператива и PvP-арен. Если нужно изменить — скажите, и я адаптирую.
Фракции и их особенности
Лесные эльфы
Стиль: скрытность, паркур по деревьям, дальний бой с луком и ловушки.
Уникальные механики: влияние на мир через интриги; возможность менять правила миссии (например, вызвать разбойников).
Мир и уровни
Открытый региональный мир: леса, дороги, деревни, дворцовый комплекс, караванные маршруты.
Деревянные домики в лесных поселениях, укреплённые посты вдоль дорог, скрытые тропы в кронах.
Динамическая система событий: рейды, караваны, патрули, сезонные погодные эффекты, которые влияют на видимость и передвижение.
Ядро геймплея и механики
Боевая система: ближний бой с комбо, парирование и уклонения; дальний бой с баллистикой; магические способности с кулдаунами.
Стелс и паркур: эльфы получают бонусы за скрытность; паркур по деревьям открывает альтернативные маршруты.
Грабёж караванов: караван — движущаяся цель с охраной; можно атаковать в пути, устраивать засады или подкупать охрану. Награды: золото, ресурсы, редкие предметы.
Система репутации: действия игрока влияют на отношение фракций и NPC; грабёж караванов повышает репутацию у разбойников, понижает у торговцев.
Управление отрядами: дворцовая охрана может отдавать приказы NPC; злодей вербует наёмников; эльфы используют духовых союзников.
Прогрессия и экономика
Дерево навыков для каждой фракции с ветками: бой, тактика, выживание, магия.
Лут и крафт: ресурсы с караванов и окружения; улучшение оружия и снаряжения; создание ловушек и зелий.
Экономика мира: торговцы реагируют на безопасность дорог; успешные грабежи повышают цены на редкие товары.
Примеры миссий и сценариев
Эльфы: «Ночная засада» — остановить караван, не убивая мирных торговцев; спасти похищенных.
Охрана: «Эскорт каравана» — провести караван через лес, отбивая засады; защитить ценный груз.
Злодей: «Подкуп и саботаж» — подкупить командира охраны и устроить диверсию в деревне.
Нравится мне, когда восторженно описываются все эти "люди с неопределённым статусом", все эти движухи до упора - и забывают рассказать о героиновой волне, детско-молодёжной проституции включая гомо и прочей питательной среде всего этого ;)
Ещё прикольно, что у многих (большинства?) упомянутых богемных экспатов в те годы как раз был жестко-героиновый период творчества, так скажем - совпадение? ...хотя они, конечно, и не в берлине бы нашли.
Ну вот именно это, да: байтоебля, ковбои, "за любой unsafe код насмехаются", "удачи вашему продакшну" - именно в этом статья и... на любителя. Что статья, что ответ - очень aligned, иф ю ноу вот ай мин.
Во-первых, я читал её давно, когда она была опубликована. Сейчас, когда увидел её в списке ваших - вспомнил, в том числе то, что был "во многом не согласен" и особенно - с достаточно безапеляционным тоном рекомендаций. При этом, запомнил её как полезную. Сейчас перечитываю, отвечу по пунктам.
Что однозначно хорошо - напоминание про работу GC и связанные риски неуправляемых указателей (fixed/pinned контексты и риски утекания указателей за их пределы).
Что, возможно, ещё более важно - напоминание о хранении невалидных указателей в управляемых ref var - типа ref arr[-1] или ref arr[arr.Length + 1]. Об этом вполне возможно никогда и не задуматься, сам исправлял подобные собственные ошибки несколько лет назад.
С чем не согласен:
"9. Удаление проверок границ (bounds check)" Там все советы можно свести к "не обходите проверки границ". Что, как по мне - плохой совет, потому, что если уж встал вопрос о том, что стоит от них избавиться - значит нужно избавиться. Есть масса вариантов, которые никакие эвристики JITа не ускорят: random access, доступ к элементам в "сегменте" массива, границы которого вычисляются сложным алгоритмом или вообще хранятся в условном Dictionary<SegmentKey, (int offset, int length)>, обработка в не-заинлайненом методе. При этом совет использовать Debug.Assert для выявления ошибок - это чисто проверка того, что ваши тесты запущенные в Дебаг-режиме написаны для happy pass. Я бы советовал подготовить необходимые данные и карты/рейнжи, убедиться, что они валидны - а потом использовать их без лишних проверок.
10. Объединение доступа к памяти (Memory access coalescing) Тоже самое. Напрмер, у меня был тип, содержавший список структур, состоявших из множества полей примитивных типов (от byte до long), тип использовался как ключ в Dictionary. Угадаете, как были написаны GetHashCode и Equal? - да, как GetHashCode/Equal над спаном байтов начиная с адреса list[0] и до list[count]. И это был самый быстрый и эффективный вариант, остальные работали в разы медленнее. О чём я бы отдельно напомнил в этом разделе - о паддингах, которые могут содержать мусор.
12. Бинарная (де)сериализация структур с паддингами или non-blittable членами С non-blittable членами всё понятно, их нужно обрабатывать отдельно.А вот с blittable, и даже с паддингами - почему бы и нет? Я бы здесь отдельно напомнил об endianness - должны совпадать для источника и получателя, но в целом - вполне себе оптимизация для контролируемых случаев.
13. Null управляемые указатели Вполне валидный случай, как и null для nullable переменных - почему нет? Я бы советовал проверять с Unsafe.IsNullRef и пользоваться со спокойной совестью.
15. Буферы фиксированного размера (Fixed-size buffers) "буферы фиксированного размера могут иметь ненулевое содержимое в определенных сценариях" как риск, так и экономия на очистке - главное об этом помнить.
16. Передача непрерывных данных как указатели + длины Тут вообще не понимаю проблемы. Самый дешевый способ передать (или хранить в ref struct) буфер для bounds unchecked обработки. Не забываем, что new Span/AsSpan сам по себе проверяет границы - а нам, может быть, уже и не надо? - разве сто создавать спан с помощью MemoryMarshal.CreateSpan(ref T reference, int length), ха-ха. И на каждом обращении к индексеру (не в "удачном" цикле) - тоже проверяет. Дальше, на х64 спан занимает 16 байт, тогда как размер без паддинга - 12. Для единственного инстанса ок, но представим, что вы храните несколько ссылок на буферы в ref struct, которая кроме этого содержит и множество полей - вполне себе оверхед при передаче по значению.
18. Ручной IL код (например, System.Reflection.Emit и Mono.Cecil) Тут всё просто: сложно в написании и отладке - но раз надо - значит надо.
19. Неинициализированные локальные переменные [SkipLocalsInit] и Unsafe.SkipInit По мне так сильно преувеличенные риски. Во-первых, GC-переменные JIT всё равно обнуляет. Во-вторых - C# всё равно не позволяет обратиться к неинициализированной переменной. Отдельно о Unsafe.SkipInit: представьте, у вас тяжелая структура со множеством полей. Ваш конструктор в любом случае инициализируют все их них, так зачем вам предварительная очистка? Это не говоря о том, что как минимум НЕТ8 (а то и 9), если у вас в структуре overlapped поля (FieldOffset) - то он ухитрялся эмитить MSIL для индивидуальной очистке каждого из этих полей. Не смотрел, что было в асме и не следил, как работает 10/11 - но такое вот было.
Примерно так.
В целом, достаточно было бы объяснить поведение рантайма во всех этих аспектах и свазать "соблюдайте осторожность и сохраняйте здравый смысл" :) А безапеляционные "советы" выглядят странно.
При этом статья полезная, то, что опубликована и на русском - ещё лучше. А работа по переводу - так и вообще выще всяких похвал.
Я пытатся сказать, что небезопасность, сомнительность с т.з. выигрыша или надежды на ненужность в будущем тех или иных трюков - не повод игнорировать знания о них. Это, как минимум, расширяет кругозор, углубляет понимание низкоуровневых процессов и экономит немало времени в ситуации, когда срочно нужно что-нибудь ускорить, а как - неясно.
При этом ваши ремарки (как и ваш перевод статьи об ансейф коде, с которой я во многом не согласен как в части рекомендаций, так и, особенно, в тоне этих рекомендаций) тоже, безусловно, полезное и совсем не лишнее знание.
то что вчера надо было костылить сегодня уже работает как надо
При всём моём восхищении от развития НЕТ рантайма, я совершенно уверен, что многие проблемы никогда не смогут быть решены на этом уровне. Так что костылить - вполне себе необходимый навык ;)
Но и определение unsafe context изменится, и как по мне - так к лучшему, и вполне можно будет держать его компактным и при желании скрытым в приватном скоупе.
Большинство обычного вида циклов не нуждаются в этом, jit вставит оптимальный код как есть
Другой бы спорил, а я не стану.
Но что умеешь - за плечами не носить, как говорится. Лично я был бы рад, если бы в своё время имел возможность начитаться о всякого рода трюках чисто про запас - меньше пришлось бы тратить времени на "изобретения", когда они реально понадобились.
Там, где нужно гарантированно избавиться от bounds checking - ref var, MemoryMarshal.Get*Reference + Unsafe.Add.
При желании, с их помощью можно соорудить цикл с обратным счётчиком и при этом "прямым" доступом к данным - экономим инструкции на загрузке предельного значения в регистр и сравнении счётчика с регистром.
По поводу выравниваний - влияет очень сильно, мало того, что выравнивание это лотерея, так ещё и даже пару лишних байт в горячем цикле может как серьёзно замедлить, так и ускорить процесс.
Это вполне ожидаемо - АОТ код просто не может быть оптимизирован на столько же, на сколько может быть оптимизирован код JITa. Отсюда же и аллокации, и скорость.
Для исправления скорости интересно было бы попробовать скомпилировать с собранным заранее профилем - я в этом совершенно не имею опыта, но чисто логически - может и поможет. Может быть и с некоторыми аллокациями поможет за счёт guarded devirtualization.
Если проведёте эксперимент - поделитесь результатом, пожалуйста, для общего развития.
Если я вас ни с кем не путаю, то уже во второй ветке я читаю эту вашу интерпретацию боксинга.
Так вот, она неверная.
Ниже вам верно ответили: боксинг - это превращение инстанса типа без заголовка (в .нете - всегда struct) в инстанс типа с заголовком (в .нете всегда любой ссылочный тип)
Интерфейс в .нете - всегда ссылочный тип с заголовком (вкл. vtable)
Не обязательно верить мне, достаточно прочитать о боксинге в документации .нета/clr.
Если же вы так уверены в своей интерпретации - можно получить подтверждающие ссылки на какие-либо авторитетные источники?
Вот я и завидую: пока прогонишь и проанализируешь свои рабочие бенчмарки и поэксперементируешь с кодом - ни на какие другие бенчмарки, и уж тем более статьи, никаких ресурсов не остаётся ;)
А вот почитать - самое то, спасибо.
Успехов.
Я повторюсь, но: великолепная серия статей.
И искренне завидую - у меня на подобное не остаётся ни времени, ни сил.
Просто интересно: а какая мотивация вкладывать столько времени и усилий? Это чистое любопытство или, может быть, побочный продукт реализации каких-либо оптимизаций?
Ну, декаданс не мы придумали, тема известная и рабочая. Как и иные экстримы, впрочем.
Я просто идиллической картинкой из статьи был... впечатлён ;)
Похоже, мы всегда недооценивали идею Кирилла
Концепт игры
Коротко: 3D экшен с тремя игровыми фракциями — лесные эльфы, дворцовая охрана и злодей — каждая с уникальным геймплеем, целями и стилем прохождения. Игрок выбирает фракцию и проходит кампанию, открывает навыки, участвует в динамических стычках и миссиях в открытом мире с деревнями, лесами, дорогами и караванами для грабежа.
Выбранный объём и допущения
Я предполагаю, что под «корованы» вы имели в виду караваны — передвижные торговые колонны, которые можно грабить. Я также беру за основу одиночную кампанию с опцией кооператива и PvP-арен. Если нужно изменить — скажите, и я адаптирую.
Фракции и их особенности
Лесные эльфы
Стиль: скрытность, паркур по деревьям, дальний бой с луком и ловушки.
Цели: защищать лес, саботировать дворцовые отряды, грабить караваны, спасать деревенских.
Уникальные механики: маскировка в листве; перемещение по кронам; духи леса как времальные помощники.
Дворцовая охрана
Стиль: тяжёлая броня, стройная тактика, осадные орудия.
Цели: удерживать контроль над дорогами и поселениями, охранять караваны, подавлять мятежи.
Уникальные механики: построение заслонов; командные приказы NPC; осадные машины.
Злодей
Стиль: гибрид магии и хитрости; может вербовать наёмников и создавать ловушки.
Цели: сеять хаос, подкупать фракции, захватывать ключевые точки.
Уникальные механики: влияние на мир через интриги; возможность менять правила миссии (например, вызвать разбойников).
Мир и уровни
Открытый региональный мир: леса, дороги, деревни, дворцовый комплекс, караванные маршруты.
Деревянные домики в лесных поселениях, укреплённые посты вдоль дорог, скрытые тропы в кронах.
Динамическая система событий: рейды, караваны, патрули, сезонные погодные эффекты, которые влияют на видимость и передвижение.
Ядро геймплея и механики
Боевая система: ближний бой с комбо, парирование и уклонения; дальний бой с баллистикой; магические способности с кулдаунами.
Стелс и паркур: эльфы получают бонусы за скрытность; паркур по деревьям открывает альтернативные маршруты.
Грабёж караванов: караван — движущаяся цель с охраной; можно атаковать в пути, устраивать засады или подкупать охрану. Награды: золото, ресурсы, редкие предметы.
Система репутации: действия игрока влияют на отношение фракций и NPC; грабёж караванов повышает репутацию у разбойников, понижает у торговцев.
Управление отрядами: дворцовая охрана может отдавать приказы NPC; злодей вербует наёмников; эльфы используют духовых союзников.
Прогрессия и экономика
Дерево навыков для каждой фракции с ветками: бой, тактика, выживание, магия.
Лут и крафт: ресурсы с караванов и окружения; улучшение оружия и снаряжения; создание ловушек и зелий.
Экономика мира: торговцы реагируют на безопасность дорог; успешные грабежи повышают цены на редкие товары.
Примеры миссий и сценариев
Эльфы: «Ночная засада» — остановить караван, не убивая мирных торговцев; спасти похищенных.
Охрана: «Эскорт каравана» — провести караван через лес, отбивая засады; защитить ценный груз.
Злодей: «Подкуп и саботаж» — подкупить командира охраны и устроить диверсию в деревне.
Случайные события: лесной пожар, нападение хищников, смена погодных условий.
Искусственный интеллект и баланс
AI патрулей с разными поведениями: агрессивные, осторожные, кордоны.
Реакция на шум и следы: стелс-детекторы, следы на земле, разведчики.
Баланс: сильные стороны фракций компенсируются слабостями; система матчмейкинга для PvP по уровню и стилю игры.
Технические и художественные решения
Движок: Unreal Engine или Unity (в зависимости от команды и бюджета).
Архитектура: модульная — боёвка, AI, мир, экономика отдельно.
Арт: стилизованный реализм; тёплые деревянные текстуры для деревень, зелёные и мшистые тона для леса, контрастные металлы для дворца.
Звук: амбиент леса, динамическая музыка, звуки шагов по листве, сигналы караванов.
Мультиплеер и режимы
Кооператив: до 4 игроков в кампании; роли дополняют друг друга.
PvP: фракционные битвы за контроль точек; режим «караван против налётчиков».
Асинхронные события: мировые ивенты, где действия одних игроков влияют на мир других.
План прототипа и следующие шаги
Прототип боёвки (две фракции, базовые атаки и уклонения).
Мини-карта мира с одной деревней, дорогой и караваном для грабежа.
AI патруля и система обнаружения.
Прототип миссии эскорта/засады.
Игровой тест и баланс; собрать фидбек и расширять контент.
Если хотите, я могу дальше:
расписать детально одну миссию шаг за шагом;
предложить набор способностей для каждой фракции;
сделать план разработки на 3–6 месяцев с ролями команды и приоритетами.
Что предпочитаете дальше — детализировать миссию, способности или план разработки?
https://www.youtube.com/watch?v=DzUcBE5DKGs
Не мог не оставить это здесь ;)
А великий и многими любимый Пётр Ильич в вашей реплике - он как-бы метафора упомянутых мною богемных экспатов или самого Берлина в описываемую эпоху?
Нравится мне, когда восторженно описываются все эти "люди с неопределённым статусом", все эти движухи до упора - и забывают рассказать о героиновой волне, детско-молодёжной проституции включая гомо и прочей питательной среде всего этого ;)
Ещё прикольно, что у многих (большинства?) упомянутых богемных экспатов в те годы как раз был жестко-героиновый период творчества, так скажем - совпадение? ...хотя они, конечно, и не в берлине бы нашли.
Справедливости ради, JIT-компиляторы вполне себе не-детерминистик, особенно те, что используют динамическое профилирование и tiered оптимизации.
...но уровень не-дерминированности иной, конечно ;)
Отличная серия, спасибо.
...эту ещё не читал, но одобряю ;)
Ну вот именно это, да: байтоебля, ковбои, "за любой unsafe код насмехаются", "удачи вашему продакшну" - именно в этом статья и... на любителя.
Что статья, что ответ - очень aligned, иф ю ноу вот ай мин.
Итак, статья.
Во-первых, я читал её давно, когда она была опубликована. Сейчас, когда увидел её в списке ваших - вспомнил, в том числе то, что был "во многом не согласен" и особенно - с достаточно безапеляционным тоном рекомендаций. При этом, запомнил её как полезную.
Сейчас перечитываю, отвечу по пунктам.
Что однозначно хорошо - напоминание про работу GC и связанные риски неуправляемых указателей (fixed/pinned контексты и риски утекания указателей за их пределы).
Что, возможно, ещё более важно - напоминание о хранении невалидных указателей в управляемых ref var - типа ref arr[-1] или ref arr[arr.Length + 1].
Об этом вполне возможно никогда и не задуматься, сам исправлял подобные собственные ошибки несколько лет назад.
С чем не согласен:
"9. Удаление проверок границ (bounds check)"
Там все советы можно свести к "не обходите проверки границ". Что, как по мне - плохой совет, потому, что если уж встал вопрос о том, что стоит от них избавиться - значит нужно избавиться.
Есть масса вариантов, которые никакие эвристики JITа не ускорят: random access, доступ к элементам в "сегменте" массива, границы которого вычисляются сложным алгоритмом или вообще хранятся в условном Dictionary<SegmentKey, (int offset, int length)>, обработка в не-заинлайненом методе.
При этом совет использовать Debug.Assert для выявления ошибок - это чисто проверка того, что ваши тесты запущенные в Дебаг-режиме написаны для happy pass.
Я бы советовал подготовить необходимые данные и карты/рейнжи, убедиться, что они валидны - а потом использовать их без лишних проверок.
10. Объединение доступа к памяти (Memory access coalescing)
Тоже самое. Напрмер, у меня был тип, содержавший список структур, состоявших из множества полей примитивных типов (от byte до long), тип использовался как ключ в Dictionary. Угадаете, как были написаны GetHashCode и Equal? - да, как GetHashCode/Equal над спаном байтов начиная с адреса list[0] и до list[count].
И это был самый быстрый и эффективный вариант, остальные работали в разы медленнее.
О чём я бы отдельно напомнил в этом разделе - о паддингах, которые могут содержать мусор.
12. Бинарная (де)сериализация структур с паддингами или non-blittable членами
С non-blittable членами всё понятно, их нужно обрабатывать отдельно.А вот с blittable, и даже с паддингами - почему бы и нет?
Я бы здесь отдельно напомнил об endianness - должны совпадать для источника и получателя, но в целом - вполне себе оптимизация для контролируемых случаев.
13. Null управляемые указатели
Вполне валидный случай, как и null для nullable переменных - почему нет?
Я бы советовал проверять с Unsafe.IsNullRef и пользоваться со спокойной совестью.
15. Буферы фиксированного размера (Fixed-size buffers)
"буферы фиксированного размера могут иметь ненулевое содержимое в определенных сценариях" как риск, так и экономия на очистке - главное об этом помнить.
16. Передача непрерывных данных как указатели + длины
Тут вообще не понимаю проблемы.
Самый дешевый способ передать (или хранить в ref struct) буфер для bounds unchecked обработки.
Не забываем, что new Span/AsSpan сам по себе проверяет границы - а нам, может быть, уже и не надо? - разве сто создавать спан с помощью MemoryMarshal.CreateSpan(ref T reference, int length), ха-ха. И на каждом обращении к индексеру (не в "удачном" цикле) - тоже проверяет.
Дальше, на х64 спан занимает 16 байт, тогда как размер без паддинга - 12. Для единственного инстанса ок, но представим, что вы храните несколько ссылок на буферы в ref struct, которая кроме этого содержит и множество полей - вполне себе оверхед при передаче по значению.
18. Ручной IL код (например, System.Reflection.Emit и Mono.Cecil)
Тут всё просто: сложно в написании и отладке - но раз надо - значит надо.
19. Неинициализированные локальные переменные [SkipLocalsInit] и Unsafe.SkipInit
По мне так сильно преувеличенные риски. Во-первых, GC-переменные JIT всё равно обнуляет. Во-вторых - C# всё равно не позволяет обратиться к неинициализированной переменной.
Отдельно о Unsafe.SkipInit: представьте, у вас тяжелая структура со множеством полей. Ваш конструктор в любом случае инициализируют все их них, так зачем вам предварительная очистка?
Это не говоря о том, что как минимум НЕТ8 (а то и 9), если у вас в структуре overlapped поля (FieldOffset) - то он ухитрялся эмитить MSIL для индивидуальной очистке каждого из этих полей. Не смотрел, что было в асме и не следил, как работает 10/11 - но такое вот было.
Примерно так.
В целом, достаточно было бы объяснить поведение рантайма во всех этих аспектах и свазать "соблюдайте осторожность и сохраняйте здравый смысл" :)
А безапеляционные "советы" выглядят странно.
При этом статья полезная, то, что опубликована и на русском - ещё лучше.
А работа по переводу - так и вообще выще всяких похвал.
Нужно освежить. Завтра.
Я пытатся сказать, что небезопасность, сомнительность с т.з. выигрыша или надежды на ненужность в будущем тех или иных трюков - не повод игнорировать знания о них. Это, как минимум, расширяет кругозор, углубляет понимание низкоуровневых процессов и экономит немало времени в ситуации, когда срочно нужно что-нибудь ускорить, а как - неясно.
При этом ваши ремарки (как и ваш перевод статьи об ансейф коде, с которой я во многом не согласен как в части рекомендаций, так и, особенно, в тоне этих рекомендаций) тоже, безусловно, полезное и совсем не лишнее знание.
При всём моём восхищении от развития НЕТ рантайма, я совершенно уверен, что многие проблемы никогда не смогут быть решены на этом уровне. Так что костылить - вполне себе необходимый навык ;)
Будет.
Но и определение unsafe context изменится, и как по мне - так к лучшему, и вполне можно будет держать его компактным и при желании скрытым в приватном скоупе.
Другой бы спорил, а я не стану.
Но что умеешь - за плечами не носить, как говорится. Лично я был бы рад, если бы в своё время имел возможность начитаться о всякого рода трюках чисто про запас - меньше пришлось бы тратить времени на "изобретения", когда они реально понадобились.
Там, где нужно гарантированно избавиться от bounds checking - ref var, MemoryMarshal.Get*Reference + Unsafe.Add.
При желании, с их помощью можно соорудить цикл с обратным счётчиком и при этом "прямым" доступом к данным - экономим инструкции на загрузке предельного значения в регистр и сравнении счётчика с регистром.
По поводу выравниваний - влияет очень сильно, мало того, что выравнивание это лотерея, так ещё и даже пару лишних байт в горячем цикле может как серьёзно замедлить, так и ускорить процесс.
Это вполне ожидаемо - АОТ код просто не может быть оптимизирован на столько же, на сколько может быть оптимизирован код JITa. Отсюда же и аллокации, и скорость.
Для исправления скорости интересно было бы попробовать скомпилировать с собранным заранее профилем - я в этом совершенно не имею опыта, но чисто логически - может и поможет. Может быть и с некоторыми аллокациями поможет за счёт guarded devirtualization.
Если проведёте эксперимент - поделитесь результатом, пожалуйста, для общего развития.
Если я вас ни с кем не путаю, то уже во второй ветке я читаю эту вашу интерпретацию боксинга.
Так вот, она неверная.
Ниже вам верно ответили: боксинг - это превращение инстанса типа без заголовка (в .нете - всегда struct) в инстанс типа с заголовком (в .нете всегда любой ссылочный тип)
Интерфейс в .нете - всегда ссылочный тип с заголовком (вкл. vtable)
Не обязательно верить мне, достаточно прочитать о боксинге в документации .нета/clr.
Если же вы так уверены в своей интерпретации - можно получить подтверждающие ссылки на какие-либо авторитетные источники?
Спасибо.
А какие там нюансы, вкратце?
Любопытная интерпретация, но, боюсь, неверная.