Что такое рефлексивный переход когда он используется
Рефлексия и кодогенерация в C++
Язык С++ по сей день является одним из самых востребованных и гибких языков программирования. Но иногда возможностей языка не хватает, несмотря на то что стандарт развивается и расширяется. С такой проблемой столкнулся и я в процессе разработки 2D движка для игр. Я стоял перед необходимостью решения несколько нетривиальных задач, таких как сериализация, анимирование и связка с редактором. Для этого отлично подходит рефлексия. Готовые решения из сети, к сожалению, мне не подошли, поэтому пришлось конструировать свой велосипед.
Далее описаны детали реализации и демо проект. Кому интересно — добро пожаловать под кат.
Требования
Существует мнение, что правильно поставленные условия задачи практически решают эту задачу. Именно поэтому перед началом реализации были собраны необходимые требования и ограничения:
Синтаксис
Следуя требованию о простоте был выработан довольно простой синтаксис определения объекта поддерживающего рефлексию: необходимо чтобы класс был унаследован от IObject, содержал в теле макрос IOBJECT(*имя класса*); и у членов класса были указаны необходимые атрибуты через комментарии.
Пример класса с поддержкой рефлексии:
Примеры использования рефлексии:
Реализация
Как вы наверное уже догадались, работает все просто: пользователь объявляет классы, которые поддерживают рефлексию, а отдельная утилита генерирует код, который инициализирует типы в системе. В данной статье я опишу принцип работы, с минимальным включением исходного кода, так как его наличие раздует статью. Но к статье прилагается демо проект со всеми исходниками, который можно посмотреть и протестировать.
Итак, подробнее о структуре рефлексии.
Тип объекта
Каждому классу соответствует описание типа, в котором содержится имя класса, информация о наследовании, члены и методы класса. С помощью типа можно создать экземпляр класса. Так же в типе класса есть довольно интересный функционал: можно получить указатель на член класса по его пути и наоборот, получить путь к члену класса по его указателю. Чтобы понять что делают последние две функции достаточно взглянуть на пример использования:
Зачем это нужно? Ближайший пример — анимация. Допустим, есть анимация, которая анимирует параметр value из примера. Но как сохранить такую анимацию в файл? Здесь и нужны эти функции. Мы сохраняем ключи анимации с указанием пути к анимируемому параметру «aObject/bObject/value», и при загрузке из файла по этому пути находим нужную нам переменную. Такой подход позволяет анимировать совершенно любые члены любых объектов и сохранять/загружать в файл.
Однако, есть небольшой недостаток, который необходимо учитывать. Поиск указателя на член класса по пути происходит быстро и линейно, но обратный процесс совершенно не линеен и может занять много времени. Алгоритму приходится «прочесывать» все члены класса, их члены и так далее, пока не встретит нужный нам указатель.
Члены класса
Каждый член класса определяется с помощью типа FieldInfo, который содержит в себе тип члена, его имя и список атрибутов. Так же тип члена класса позволяет получить значение члена класса для определенного экземпляра и наоборот, присвоить значение. Тип, имя и атрибуты заполняются сгенерированным кодом. Присваивание и получение значения работают через адресную арифметику. На этапе инициализации типа высчитывается смещение в байтах относительно начала памяти под объект, затем, при присваивании или получении значения к адресу объекта прибавляется это смещение.
Функции класса
Каждой функции класса соответствует объект с интерфейсом FunctionInfo, который хранит в себе тип возвращаемого значения, имя функции и список параметров. Почему именно интерфейс и как вызвать функцию из этого интерфейса? Чтобы вызвать функцию нам нужен указатель на нее. Причем, указатель на функцию класса должен быть такого вида:
Для хранения таких указателей определены классы определены шаблонные классы:
Шаблонная магия пугает, но работает все довольно просто. На этапе инициализации типа для каждой функции создается объект класса SpecFunctionInfo или SpecConstFunctionInfo в зависимости от константности функции и помещается в тип объекта Type. Затем, когда нам нужно вызвать функцию из интерфейса FunctionInfo, мы статически преобразуем интерфейс в ISpecFunctionInfo и вызываем у него виртуальную функцию Invoke, переопределенный вариант которой выполняет функцию по сохраненному адресу.
Атрибуты
Функция атрибутов — это добавление мета информации для членов классов. С помощью атрибутов можно указывать сериализуемые члены, тип их сериализации, отображение в редакторе, параметры анимации и так далее. Атрибуты наследуются от интерфейса IAttribute и добавляются к членам класса на этапе инициализации типа. Пользователю лишь необходимо обозначит необходимые атрибуты через комментарии к членам класса.
Модуль рефлексии
Итак, вся структура типов у нас есть, необходимо все это где-то хранить. С этой простой задачей справляется класс-синглтон Reflection. С его помощью можно получить тип по имени, создать экземпляр типа или получить список всех зарегистрированных типов.
Кодогенерация
Почему именно кодогенерация? Есть решения рефлексии с помощью хитрых макросов, прописывания функции сериализации и тому подобное. Но у таких подходов один общий минус — фактически приходится вручную описывать типы. В рефлексии от этого никуда не избавиться, поэтому проще всего доверить эту рутинную работу отдельной подпрограмме, которая работает до компиляции и генерирует отдельный исходный файл с инициализацией типов.
По-хорошему, это конечно же должен делать сам компилятор, ведь у него есть вся информация о классах, членах и методах. Но решение должно быть не зависимым от компилятора, в конце концов глупо навязывать свой определенный компилятор разработчику, тем более для 2D игр с кучей целевых платформ.
Именно поэтому кодогенерация отдельной утилитой. К сожалению, утилита в моем проекте не может похвастаться изящностью и стопроцентной стабильностью, но на моем уже довольно немаленьком проекте она работает прекрасно. В будущем она наверняка будет переписана и дополнена, так как у нее есть как минимум один существенный минус — она написана на C#, что требует Windows платформы, либо наличия Mono. Это путь наименьшего сопротивления, и я выбрал его потому что на этапе демо версии мне нужно как можно больше функционала в проекте. Тем не менее в этой статье я опишу этапы ее работы и с какими трудностями я столкнулся.
Работа утилиты делится на два этапа:
Парсинг исходников проекта
Здесь мой подход отличается от работы компиляторов.
Генерация итогового исходника с инициализацией типов
Данный этап включает в себя генерирование следующих частей:
В конечном итоге мы получаем исходных файл с функцией, которая инициализирует все типы приложения. Пользователю лишь необходимо ее вызвать при старте приложения.
Разбираемся с рефлексией на примерах в C#
Приветствую всех, сегодня поговорим о рефлексии, для чего она нужна, ее применения. Тема эта очень интересная, и ее методы использования часто приходится применять в больших проектах.
Рефлексия (отражение) — это процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения, это своего рода процесс обнаружения типов во время выполнения.
Рефлексия позволяет: перечислять члены типа, создавать новые экземпляры объекта, запускать на выполнение члены объекта, извлекать информацию о типе, извлекать информацию о сборке, исследовать пользовательские атрибуты, примененные к типу, создавать и компилировать новые сборки.
Метаданные описывают все классы и члены классов, определённые в сборке, а также классы и члены классов, которые текущая сборка вызывает из другой сборки.
Манифест сборки— коллекция данных, с описанием того, как элементы любой сборки (статической или динамической) связаны друг с другом. Манифест сборки содержит все
метаданные, необходимые для задания требований сборки к версиям и удостоверения безопасности, а также все метаданные, необходимые для определения области действия сборки и разрешения ссылок на ресурсы и классы. Манифест сборки может храниться в PE-файле (EXE или DLL) с кодом MSIL или же в отдельном PE-файле, содержащем только данные манифеста.
Модули — это контейнеры типов, расположенные внутри сборки. Модуль может быть контейнером в простой, или многофайловой сборке. Несколько модулей в одной сборке применяются в редких случаях, когда нужно добавить код на разных языках в одну сборку или обеспечить поддержку выборочной загрузки модулей.
Парадигма программирования, положенная в основу отражения, называется рефлексивным программированием. Это один из видов метапрограммирования.
System.Reflection — Пространство имен содержит типы, предназначенные для извлечения сведений о сборках, модулях, членах, параметрах и других объектах в управляемом коде путем обработки их метаданных. Эти типы также можно использовать для работы с экземплярами загруженных типов, например для подключения событий или вызова методов.
Класс Type.
Type является корневым классом для функциональных возможностей рефлексии и основным способом доступа к метаданным. С помощью членов класса Type можно
получить сведения об объявленных в типе элементах: конструкторах, методах, полях, свойствах и событиях класса, а также о модуле и сборке, в которых развернут данный класс.
Счастье от ума: как рефлексировать, чтобы быть счастливым
Разбираем распространенные ошибки при рефлексии, лайфхаки и правила для продуктивного самопознания.
Материал проверил и прокомментировал Аркадий Волков, психотерапевт, специалист сервиса по подбору психологов Alter
Что такое рефлексия
Рефлексия — способность сознательно обращать внимание на свои мысли, эмоции и поведение, оценивать принятые решения и перспективы. Термин произошел от латинского слова «reflectio» — «отражение». Это обращение внимания на себя, переосмысление, анализ. Человек смещает фокус извне на свои чувства, собственное «Я», переосмысливает происходящее. Сократ считал рефлексию доступным способом познания и совершенствования себя. Психотерапевт Аркадий Волков объясняет: «Роль рефлексии в разных психотерапевтических подходах существенно отличается. Например, если в когнитивно-бихевиоральном подходе рефлексия, внимание к собственным мыслительным процессам, их оценка и переоценка — одна из главных опор терапевтического процесса, то в психоанализе рефлексия скорее играет вспомогательную роль и используется терапевтом в работе с материалом, получить который помогают иные, нерефлексивные методы».
Для чего нужна рефлексия
Саморефлексия помогает определить реальные желания. Немецкий ученый Оливер Шультхайс из Университета Эрлангена-Нюрнберга доказал, что, когда сознательные цели совпадают с бессознательными мотивами, человек чувствует себя счастливее [1]. Кроме того, процесс раздумий позволяет:
Например, ночью перед сном вы задумываетесь о том, как утреннее совещание привело к остаточному гневу и повлияло на ваши отношения с членами семьи. Мысленно перебирая события дня, вы находите первопричину плохого настроения и ищете способы устранить ее в будущем. Как вариант, выделяете время для отдыха после стрессовых встреч вместо того, чтобы сразу продолжить работать.
Большинство людей склонны к саморефлексии, и она помогает сделать выводы для минимизации возможного ущерба психике. Этот процесс способен оградить нас от неправильных решений и поменять их. Когда рефлексия в избытке, она может тормозить развитие, отнимать много времени и сил. Было бы здорово уметь мгновенно осознавать проблему и делать выводы в реальном времени. Но чаще всего человек осмысливает ситуацию не в моменте, а постфактум. Ключ к осознанности здесь и сейчас — культивация привычки оценивать происходящее, развитие рефлексии как мышцы, которую можно задействовать в любой момент.
Виды рефлексии
В психологии существует следующая классификация рефлексии, в которой каждый вид обладает специфическими признаками:
Также рефлексию делят на ситуативную (анализ в процессе происходящего), проспективную (мысли о будущем) и ретроспективную (взгляд в прошлое). Кроме того, можно выделить такие ее виды:
Какой бы ни была рефлексия, она работает по одному и тому же алгоритму, не отличаясь по сути от процессов самопознания. Развить и оптимизировать эту способность может каждый.
Ошибки, которых стоит избегать
Рефлексия как терапевтический метод самоанализа сопряжена с позитивными перспективами и личностным ростом. Но не все так просто. Ученые провели эксперимент среди рефлексирующих студентов и обнаружили, что они отличаются низким уровнем психологического благополучия [2]. Психолог Таша Эврих считает, что виной тому не сам процесс, а неумение людей правильно к нему подойти [3]. Рефлексирующий человек часто скатывается в философию и концентрируется на вопросе «почему?», в то время как стоит спросить себя «что?»: «Что произошло? Что я чувствую? Что я могу сделать, чтобы мне стало легче и чтобы подобного не повторилось в будущем?».
Эту теорию подтверждают исследования: в одном из них студентам предложили рефлексировать по итогам проваленного экзамена [4]. Те, кто основывал размышления на вопросе «почему так произошло?», сфокусировались на своих неудачах, проблемах с дисциплиной, недостаточном уровне знаний и в итоге находились в подавленном состоянии даже на следующий день. Понять, что делать, всегда эффективнее, чем выяснять, кто виноват. Врачи оперируют термином «деструктивная рефлексия». Она присуща людям с высоким уровнем самокритики, часто встречается у пациентов с неврозами, склонными к постоянному анализу мыслей и поступков.
Простой пример: вы задержали сроки сдачи проекта на работе. Можно уйти в самокопание, признав поражение и собственное неумение контролировать время и расставлять приоритеты. Сделать вывод, что вы недостаточно компетентны, а может и вовсе виновата генетика, ведь дедушка тоже всегда везде опаздывал. Такая саморефлексия не даст ничего, кроме упаднических настроений и отсутствия мотивации для будущих достижений. А теперь попробуйте повернуть мысли в сторону эффективности. Поразмышляйте о том, как лучше выстроить график работы, может быть, стоит обсудить с руководителем переизбыток задач, с которыми трудно справиться в установленные сроки. Кто-то придет к выводу, что стоит изучить тайм-менеджмент, а другой найдет способы донесения своих желаний до окружающих, улучшив коммуникативные навыки.
Как рефлексировать правильно
Помимо постановки вопроса, необходимо придерживаться нескольких правил, чтобы процесс саморефлексии был позитивным и продуктивным:
Комментарий эксперта
Аркадий Волков, психотерапевт, специалист сервиса по подбору психологов Alter
Как в индивидуальной, так и в групповой психотерапии рефлексия часто оказывается важной частью более глубокого и масштабного процесса самоосознания. В ходе сессии или тренинга психолог часто задает вопросы: «Что вы сейчас чувствуете?», «Что вы испытываете, когда вспоминаете об этом, когда слышите такие слова в свой адрес?», «Какая мысль стоит за этой эмоцией?» и т. д. Эти и другие подобные вопросы помогают переключить внимание на себя и осознать, что наши мысли иногда не более и не менее, чем мысли, и в наших собственных силах принять решение: верна ли та или иная мысль и нужно ли нам ею руководствоваться. Чем честнее и конкретнее клиент отвечает на эти вопросы, тем эффективнее психологическая работа. Иногда такая рефлексия становится основным содержанием психотерапевтического процесса, в таком случае анализ обоснованности собственных решений и действий, поиск альтернатив сами по себе приближают клиента к поставленным целям. А иногда это части более сложного процесса, наряду с работой с эмоциями, образами и смыслами.
Базовый инструмент для развития навыков рефлексии — дневник мыслей. В самой простой форме это может быть обычный дневник, но при желании к нему добавляют записи своих эмоций и действий: пробовать установить связь между собственными мыслями, вызванным ими ощущением и поведением, которое они спровоцировали, отмечать типичные когнитивные искажения и формулировать альтернативные способы реагирования на ту или иную ситуацию.
Второй, более ситуативный инструмент — направленные вопросы. Например, в отношении завершенного рабочего процесса или проекта, в который вы вовлечены, это могут быть такие вопросы : «Что я вынес из этого опыта? С чем мне было сложно справиться? Что я сделал бы по-другому в следующий раз? Что я могу сделать сейчас, чтобы в следующий раз достичь другого результата?» Это могут быть и вопросы к себе: «Что сейчас важно для меня? Что было важно пять или десять лет назад? Как то, что для меня важно, влияет на мои действия? Могу ли я сделать что-то, чтобы мои действия больше соответствовали моим устремлениям?». Задать вопросы можно к книге или мысли, которая вас зацепила: «Что в этом меня привлекает? Может ли эта мысль или идея стать частью моей жизни? Что для этого нужно сделать?»
Часто поиск подходящей формы для рефлексии позволяет, с одной стороны, снизить ее объем, а с другой — превратить размышление в контролируемый процесс. Выберите для себя время в течение недели или дня, которое вы сможете посвятить только рефлексии. Поэкспериментируйте с оформлением этого процесса: может быть, вам подойдет запись мыслей на бумагу или проговаривание вслух на диктофон. Задавайте себе вопросы, которые помогут направить рефлексию в нужное вам русло. Каких целей вы хотите достичь? Желаете ли вы что-либо изменить? Что для этого нужно?
Способность и склонность к рефлексии сами по себе важные условия психического здоровья, развития человека, его интеграции в общество. При этом дезадаптивные формы рефлексии, такие как самокритичные безысходные руминации при депрессии могут оказывать весьма негативный эффект на психическое и физическое здоровье. Отдельные исследователи также выдвигали предположение о том, что внимание, сфокусированное на самом себе, является характерной чертой ряда психических заболеваний: депрессии, тревожных расстройств, шизофрении [5].
Где грань между полезной и разрушающей рефлексией? Ответ на этот вопрос всегда будет индивидуальным, но относительно общим критерием может быть эффект от рефлексии: приводит ли этот процесс к положительным изменениям в жизни, более глубокому и четкому пониманию себя и окружающего мира. При этом важно помнить, что рефлексия часто может быть очень непростым трудом, не однозначно приятным и комфортным процессом.
Важнее всего здесь прислушаться к себе и определить, что работает именно для вас. Для одного идеальной ситуацией для рефлексии будет прогулка по лесу или городу, для другого — время перед сном или за рабочим столом. Музыка, запахи, физическое состояние — со всем этим также можно экспериментировать, чтобы найти формат, подходящий именно вам.
Было очевидно, что следует использовать рефлексию. Однако при реализации я столкнулся со множеством тонкостей и в конечном счёте прибегнул к чёрной магии динамической генерации IL. В этой статье я хотел бы поделиться опытом и рассказать о том, как я решал эту задачу. Если Вас заинтересовала тема, то прошу под кат!
Часть 1. Объект в отражении
«Подать сюда
MFCIEqualityComparer!», — кричал он, топая всеми четырьмя лапами
Теперь сделаем шаг назад и точнее определим, что именно мы будем сравнивать. Объекты могут быть сколь угодно сложными, всё нужно учесть. Комбинируя следующие типы свойств мы сможем покрыть широкий класс входных данных:
Последний случай — сравнение массивов — во многом похож на сравнение коллекций. Отличие состоит в том, что вместо использования стандартного метода LINQ мы используем самописный обобщённый метод. Реализация сравнения массивов представлена ниже:
Итак, все части пазла собраны — наш рефлексивный компарер готов. Ниже представлены результаты сравнения производительности рефлексивного компарера по сравнению с ручной реализацией на «тестовых» данных. Очевидно, что время выполнения сравнения очень сильно зависит от типа сравниваемых объектов. В качестве примера здесь сравнивались объекты двух типов — условно «посложнее» и «попроще». Для оценки времени выполнения использовалась библиотека BenchmarkDotNet, за которую особую благодарность хочется выразить Андрею Акиньшину DreamWalker.
BenchmarkDotNet=v0.7.8.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel® Core(TM) i5-2410M CPU @ 2.30GHz, ProcessorCount=4
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit [RyuJIT]
Результаты сравнения объектов ComplexClass
Method | Platform | Jit | AvrTime | StdDev | op/s |
---|---|---|---|---|---|
ManualCompare | X64 | LegacyJit | 1,364.3835 ns | 47.6975 ns | 732,941.68 |
ReflectionCompare | X64 | LegacyJit | 36,779.9097 ns | 3,080.9738 ns | 27,188.92 |
ManualCompare | X64 | RyuJit | 930.8761 ns | 43.6018 ns | 1,074,294.12 |
ReflectionCompare | X64 | RyuJit | 36,909.7334 ns | 3,762.0698 ns | 27,093.98 |
ManualCompare | X86 | LegacyJit | 936.3367 ns | 38.3831 ns | 1,067,992.54 |
ReflectionCompare | X86 | LegacyJit | 32,446.6969 ns | 1,687.8442 ns | 30,819.81 |
Результаты сравнения объектов SimpleClass
Method | Platform | Jit | AvrTime | StdDev | op/s |
---|---|---|---|---|---|
Handwritten | X64 | LegacyJit | 131.5205 ns | 4.9045 ns | 7,603,376.64 |
ReflectionComparer | X64 | LegacyJit | 3,859.7102 ns | 269.8845 ns | 259,087.15 |
Handwritten | X64 | RyuJit | 61.2438 ns | 1.9025 ns | 16,328,222.24 |
ReflectionComparer | X64 | RyuJit | 3,841.4645 ns | 374.0006 ns | 260,317.46 |
Handwritten | X86 | LegacyJit | 71.5982 ns | 5.4304 ns | 13,966,823.95 |
ReflectionComparer | X86 | LegacyJit | 3,636.7963 ns | 241.3940 ns | 274,967.76 |
Естественно, прозводительность рефлексивного компарера ниже самописного. Рефлексия медленна и её API работает с object ‘ами, что незамедлительно сказывается на производительности, если в сравниваемых объектах присутствуют типы-значения, из-за необходимости выполнять boxing/unboxing. И тут уже необходимо исходить из собственных потребностей. Если рефлексия используется не часто, то с ней можно жить. Но в конкретном моём случае, рефлексивный компарер был недостаточно быстр. «Давай по новой, Миш», — сказал я себе и принялся за второй вариант.
Часть 2. Игра в emitацию
Можно жить так, но лучше ускориться
Группа Ленинград, «Мне бы в небо»
Нео: И ты это читаешь?
Сайфер: Приходится. Со временем привыкаешь. Я даже не замечаю код. Я вижу блондинку, брюнетку, рыженькую.
Фильм «Матрица» (The Matrix)
О генерации IL на Хабре уже писали. Поэтому лишь вкратце напомним об особенностях IL.
К примеру, инкрементация целочисленной переменной будет на IL записана следующим образом:
Посмотреть результат комплияции C# в IL можно посмотреть различными средствами, например, ILDasm (утилита, поставляемая вместе с Visual Studio) или ILSpy. Но мой любимый способ — это с помощью замечательного веб-приложения Try Roslyn, написанного Андреем Щёкиным ashmind.
Вернёмся к нашей задаче. Мы опять будем делать делать реализацию интерфейса IEqualityComparer :
Подробно разбирать каждый случай в статье не будем, в конце я дам ссылку на исходники. Но представим код сравнения массивов, чтобы прочувствовать, что из себя представляет разработка в условиях примитивных команд.
Декомпилированный метод выглядит достаточно понятно, чтобы проверить, что он корректно выполняет сравнение. Единственное к чему можно было бы придраться, так это обилие временных переменных, но это, как говорится, издержки производства.
Результаты
Ниже представлены результаты сравнения производительности DynamicCodeComparer, ReflectionComparer и написанного вручную сравнения на тех же входных данных, на которых мы проводили наш микробенчмарк для рефлексивного компарера. Как видно генерация IL позволяет получить гораздо более эффективную реализацию по сравнению с рефлексией.
Результаты сравнения объектов ComplexClass
Method | Platform | Jit | AvrTime | StdDev | op/s |
---|---|---|---|---|---|
DynamicCodeComparer | X64 | LegacyJit | 1,104.7155 ns | 32.9474 ns | 905,210.51 |
Handwritten | X64 | LegacyJit | 1,360.3273 ns | 39.9703 ns | 735,117.32 |
ReflectionComparer | X64 | LegacyJit | 38,043.3600 ns | 2,261.3159 ns | 26,290.11 |
DynamicCodeComparer | X64 | RyuJit | 834.8742 ns | 58.1986 ns | 1,197,785.93 |
Handwritten | X64 | RyuJit | 968.3789 ns | 33.1622 ns | 1,032,653.82 |
ReflectionComparer | X64 | RyuJit | 37,751.3104 ns | 1,763.3172 ns | 26,489.20 |
DynamicCodeComparer | X86 | LegacyJit | 776.0265 ns | 22.8038 ns | 1,288,615.79 |
Handwritten | X86 | LegacyJit | 915.5713 ns | 26.0536 ns | 1,092,214.32 |
ReflectionComparer | X86 | LegacyJit | 32,382.2746 ns | 1,748.4016 ns | 30,881.10 |
Результаты сравнения объектов SimpleClass
Method | Platform | Jit | AvrTime | StdDev | op/s |
---|---|---|---|---|---|
DynamicCodeComparer | X64 | LegacyJit | 215.7626 ns | 8.2063 ns | 4,634,725.08 |
Handwritten | X64 | LegacyJit | 160.4945 ns | 6.8949 ns | 6,230,741.94 |
ReflectionComparer | X64 | LegacyJit | 6,654.3290 ns | 380.7790 ns | 150,278.15 |
DynamicCodeComparer | X64 | RyuJit | 168.4194 ns | 9.4654 ns | 5,937,569.56 |
Handwritten | X64 | RyuJit | 87.8513 ns | 3.3118 ns | 11,382,874.20 |
ReflectionComparer | X64 | RyuJit | 6,954.6437 ns | 387.1803 ns | 143,789.85 |
DynamicCodeComparer | X86 | LegacyJit | 180.4105 ns | 6.5036 ns | 5,542,914.59 |
Handwritten | X86 | LegacyJit | 93.0846 ns | 4.0584 ns | 10,742,923.17 |
ReflectionComparer | X86 | LegacyJit | 6,431.5783 ns | 314.5633 ns | 155,483.09 |
Заключение
Засим откланиваюсь, дорогие читатели. Приятного программирования!