Skip to content

Быстродействие

Igor Polyakov edited this page Mar 10, 2018 · 3 revisions

Введение

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

Рекомендации по использованию

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

Замеры производительности

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

Method Mean Error StdDev
Вызов метода напрямую 1.902 ns 0.0755 ns 0.0927 ns
Вызов метода объекта через DispatchProxy 575.124 ns 11.2527 ns 14.2311 ns
Вызов метода скомпонованного объекта 1,402.062 ns 27.4180 ns 35.6512 ns
Вызов метода через рефлексию 344.422 ns 6.7755 ns 6.6544 ns

Окружение

BenchmarkDotNet=v0.10.12, OS=macOS 10.13.3 (17D47) [Darwin 17.4.0]
Intel Core i5-7360U CPU 2.30GHz (Kaby Lake), 1 CPU, 4 logical cores and 2 physical cores
.NET Core SDK=2.1.4
  [Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
  Core   : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT

Легенда
  Mean   : Arithmetic mean of all measurements
  Error  : Half of 99.9% confidence interval
  StdDev : Standard deviation of all measurements
  1 ns   : 1 Nanosecond (0.000000001 sec)

Как видно, вызов скомпонованного метода представленного 3м в списке, всего в 4 раза медленнее вызова этого же метода через рефлексию (MethodInfo.Invoke) и в менее чем 3 раза медленее, чем обернутый тип в динамический прокси, а точнее его реализации в CoreFX. При том речь тут идет о наносекундах. Для сравнения, бенчмарк на сериализацию простого объекта из двух строковых и одного целочисленного поля через бинарную сериализацию (BinaryFormatter) дает следующие результаты.

Method Mean Error StdDev
Сериализация 17.00 us 0.3384 us 0.6189 us
Десериализация 17.00 us 0.3374 us 0.7887 us

1 us - 1 Микросекунда (0.000001 sec).

То есть 17 us - 17000 ns. На основе этих цифр можно сделать вывод, что сериализация простого объекта из трех свойств почти в 13 раз медленнее, чем вызов метода объекта, скомпонованного с декларативным аспектом внутри динамического прокси. Попрофилировать самому можно запустив этот проект.

Clone this wiki locally