Проблемы с производительностью от внедрения зависимости - PullRequest
7 голосов
/ 07 января 2009

В своих отчетах о профилировщике я все чаще вижу результаты фиктивного тестирования с внедрением зависимостей. Многие из зависимостей были статическими, но поскольку мы хотим тестировать методы изолированно, они заменяются членами экземпляра, как в следующем примере:

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   // Constructor used in production 
   public ShortLivedThing() {
     dep1 = new Dep1(); dep2 = new Dep2(); dep3 = new Dep3();
   }

   // DI for testing 
   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1(); dep2 = d2(); dep3 = d3();
   }
}

В свою очередь, зависимости в большинстве случаев имеют другие зависимости и так далее. Это приводит к созданию дерева (в основном «статических») объектов каждый раз, когда вызов метода выполняется вне тестов. Каждый из объектов очень маленький (всего несколько указателей), но эффект дерева превращает его в постоянно увеличивающийся удар по производительности.

Что мы можем с этим поделать?

Ответы [ 6 ]

8 голосов
/ 07 января 2009

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

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

Может быть, вы используете слишком мало областей применения синглтона и слишком большой объем прототипа? (Прототип = новый экземпляр каждый раз)

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

 A Singleton
 |
 B Singleton
 |
 C Prototype (per-invocation)
 |
 D Singleton
 |
 E Session scope (web app)
 |
 F Singleton

И каждый запрос будет создавать только 1 экземпляр C и один экземпляр E на сеанс. A, B, D и F являются синглетонами. Если это не веб-приложение, у вас по умолчанию нет области действия сеанса, но вы также можете создавать собственные области действия (область «Окно» звучит круто для оконного настольного приложения). Подсказка заключается в том, что вы можете «представить» области на любом уровне, фактически вы можете иметь десять слоев одноэлементных объектов, и внезапно появляется что-то на уровне сеанса. (Это действительно может революционизировать реализацию некоторых сквозных функций в многоуровневой архитектуре, но это другая история)

Это действительно дает минимальное возможное создание объекта в модели DI, я думаю.

Хотя это Spring для Java, я считаю, что ряд других DI-структур должен поддерживать аналогичные функции. Возможно, не самые минималистичные.

2 голосов
/ 07 января 2009

Я думаю, у вас должен быть только "конструктор DI". Этот конструктор вызывается как для тестирования, так и для производства.

class ShortLivedThing {
   IDependency1 dep1;
   IDependency1 dep2;
   IDependency1 dep3;
   ...

   int TheRealData;

   public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { 
     dep1 = d1; dep2 = d2; dep3 = d3;
   }
}

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

В итоге: не используйте кодирование 50% DI / 50%, переходите на 100% DI.

1 голос
/ 07 января 2009

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

Автоматизируйте этот процесс:

  • Когда кто-нибудь регистрируется, сделайте сборку из хранилища.
  • Запустите тесты для этой сборки.
  • E - отправить результаты разработчику, который зарегистрировался.

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

  • Если тесты пройдены (и необязательные критерии соблюдены), объедините новый код с реальным хранилищем.

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

1 голос
/ 07 января 2009

Как насчет передачи ссылок?

0 голосов
/ 07 января 2009

Если вы ориентируетесь на .NET, проверьте Autofac . Он имеет различные области действия (синглтон, фабрика, контейнер) для настройки аспектов создания, детерминированного удаления для предотвращения использования ресурсов и позволяет использовать GeneratedFactories и лямбда-выражения для настройки компонентов и избежать затрат на отражение.

0 голосов
/ 07 января 2009

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

...