Улучшение времени компиляции монолитной сборки .NET - инкрементная компиляция? - PullRequest
0 голосов
/ 02 октября 2018

Мы используем .NET Framework 4.6, и в настоящее время у нас есть монолитная DLL, содержащая большую часть нашего приложения.Он содержит около 700 тыс. Строк кода.Каждый раз, когда мы вносим изменения, перекомпиляция занимает более минуты.Мы хотели бы ускорить это, если это возможно.

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

Другие считают, что новые функции "инкрементной сборки" в .NET Core 2.x могут радикально увеличить время сборки монолита,подразумевается, что компилятор может перестраивать только те части монолита, которые изменились, игнорируя остальные.

Действительно ли это так, или это не так, как работает новая функция инкрементной сборки?

Или у кого-нибудь есть другие советы о том, как улучшить время компиляции?

Ответы [ 3 ]

0 голосов
/ 08 октября 2018

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

Способ, которым вы можете достичь этогоis (в Visual Studio 2015–2017):

  1. Откройте свое решение
  2. Для каждой сборки, которую вы хотите добавить, создайте отдельный проект в своем решении ( щелкните правой кнопкой мыши) в решении, чтобы открыть контекстное меню, затем выберите добавить -> новый проект ... в контекстном меню, затем выберите Visual C # -> Библиотека классов )
  3. Добавьте ссылку на ваш основной проект (который является вашим монолитным приложением) для каждой созданной вами сборки ( щелкните правой кнопкой мыши по ссылкам, затем выберите добавить ссылку ... и просмотрите навигатор Проекты -> Решение , чтобы выбрать его)
  4. Щелкните правой кнопкой мыши по основному проекту и выберите Зависимости сборки -> Зависимости проекта ... В диалоговом окне убедитесь, что ваш мав проекте выбрано в выпадающем списке.Отметьте каждую сборку, которую вы добавили.Теперь ваш главный проект зависит от сборок и будет компилироваться в правильном порядке.
  5. Перемещение классов из вашего основного проекта в проекты сборки (вырезать и вставить их).Если это создает какие-либо дополнительные зависимости, откройте Зависимости сборки -> Зависимости проекта ... , на этот раз для проекта, который зависит от разных сборок, и отметьте галочкой сборки, от которых зависит.
  6. Щелкните правой кнопкой мыши по решению и выберите перестроить решение. Если вы все сделали правильно, то компиляция должна завершиться успешно.В противном случае исправьте все возникающие ошибки (например, из-за пропущенных ссылок).Проверьте, не пропустили ли вы какие-либо операторы using для импорта пространств имен.

С этого момента вам нужно собирать сборку только в том случае, если она изменилась (щелкните правой кнопкой мыши по проекту и выберите «построить»).).Если есть какие-либо зависимости, компилятор автоматически компилирует их в правильном порядке сборки.

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

Те, которые не зависят от других сборок, могут компилироваться параллельно - поэтому, если вы хотите запустить разные процессы компилятора, вы можете подумать о создании пакета, который делает это.Диалог зависимостей проекта поможет вам определить сборки, которые могут быть скомпилированы параллельно.

Вкладка «Порядок сборки» показывает, в каком порядке они должны работать, вкладка «Зависимости» показывает для каждой сборки, от каких других сборок она зависит.Таким образом, вы можете узнать, есть ли какие-либо «ветви», которые можно скомпилировать параллельно с другими «ветвями» (с «ветвью» я имею в виду не ветки, управляемые исходным кодом, а ветви вашего решения: представьте себе дерево, в которомосновным проектом является корень - каждая ветвь состоит из последовательности сборок вашего решения, которые могут компилироваться параллельно).

0 голосов
/ 09 октября 2018

Я согласен с тем, чтобы разбить его.Но есть много способов сломать решение.Я думаю, вы должны сделать это поэтапно.

Этап 1 - самый быстрый способ разбить его сейчас.

Наиболее распространенные способы получить самые быстрые результаты:

  1. Все модели - Создайте проект библиотеки моделей и переместите в него все свои файлы моделей.Сделайте так, чтобы ваш монолит ссылался на проект моделей.Не изменяйте файлы или пространства имен вообще.Просто вырезайте и вставляйте из одного проекта в другой.Если какие-либо классы моделей были закрытыми, сделайте их внутренними (найдите и замените в проекте) и добавьте атрибут InternalsVisibleTo в сборку вашего монолита.Предполагаемое время перемещения всех модельных классов: 20 минут.

  2. Все интерфейсы - создайте проект библиотеки интерфейсов и переместите в него все файлы классов интерфейса.Сделайте так, чтобы ваш монолит ссылался на проект моделей.Не изменяйте файлы или пространства имен вообще.Просто вырезайте и вставляйте из одного проекта в другой.Если какие-либо классы интерфейса были закрытыми, сделайте их внутренними (найдите и замените в проекте) и добавьте атрибут InternalsVisibleTo в сборку вашего монолита.Предполагаемое время для перемещения всех классов интерфейса: 20 минут.

  3. Сгенерированный код - создание проекта GeneratedCode.Переместите все классы, которые на 100% сгенерированы, в этот проект.Конечно, предполагается, что, поскольку у вас есть 700 тыс. Строк кода, многие классы полностью сгенерированы.Если у вас нет сгенерированного кода, пропустите этот.Предполагаемое время для перемещения всего сгенерированного кода: 1 час.

  4. Методы расширения - создайте проект расширения и переместите все классы методов расширения в этот проект.Предполагаемое время для перемещения всех классов модели: 30 минут.

  5. FixedLogic - создание проекта с фиксированной логикой.Найдите все ваши редко затрагиваемые классы - в проекте его размера, вероятно, есть сотни файлов классов, которые не были затронуты в течение многих лет.Вы перекомпилируете эти классы снова и снова без всякой причины.Вы должны выяснить, что это такое.Надеюсь, вы можете легко увидеть это в вашем контроле исходного кода.Расчетное время: 2 часа, чтобы выяснить запрос в вашей системе контроля версий.10 минут, чтобы переместить эти файлы в новый проект.

  6. Оставайтесь на уровне 1 dll для вашего выпуска.Теперь я предполагаю, что у вас есть процесс сборки, развертывания, установки.Если вы этого не сделаете, игнорируйте этот шаг.Если вы это сделаете, и это сложно, вы можете , а не хотеть добавить dll к вашим существующим процессам, потому что это добавило бы дополнительное время к этим задачам.Поэтому убедитесь, что все файлы компилируются таким образом, что все еще приводит к 1 DLL.См. Эту статью: https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-build-a-multifile-assembly. Предполагаемое время для выполнения этой статьи: 4 часа.

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

Фаза 2 - лучший, но долгосрочный путь.

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

  1. Проанализируйте ваш проект и составьте список областей или доменов, которые покрывает ваш код.Это простой список в текстовом файле.Вероятно, у вас много доменов.

  2. Назначьте файлы классов этим доменам, но также пометьте файлы классов как Модель, Интерфейс, Логика.

  3. При следующем касании файла в домене создайте три проекта: Domain.Models, Domain.Interfaces и Domain.Logic.Переместите код в правильный проект.

Основная цель - разделение интересов

0 голосов
/ 05 октября 2018

Инкрементные сборки могут помочь вам с вашей единственной монолитной DLL.В 2.0 они были представлены, но в основном они были направлены на обнаружение изменений зависимостей - то, что Visual Studio уже делает для вас в вашем случае.

Однако , из Что нового в.NET Core 2.1 :

Использование долговременных серверов сборки SDK, которые представляют собой процессы, охватывающие отдельные вызовы сборки dotnet.Они исключают необходимость JIT-компиляции больших блоков кода каждый раз при запуске сборки dotnet.

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

Обновление , я пытался проверить, что именно это может означать, но, к сожалению, у меня нет проекта на 700 тыс. строк.Я использовал этот пример проекта MS и выполнил 2 копии - одна соединилась в одну DLL, а другая разбилась.Каждый тест, который я проводил, пример «монолитного» комбинированного проекта выполнялся быстрее, но я предполагаю, что это неверные результаты, поскольку объединенный проект вряд ли можно считать «монолитным».

Одна интересная вещь, которую я обнаружил в этом образце «монолит»: внесла ли я небольшое изменение в представление или радикальные изменения в проекте, я никогда не видела разницы между свежим временем компиляции.Это заставляет меня задаться вопросом, что именно кэширует этот новый сборочный сервер с JIT.Возможно, опять же, это только потому, что мой «монолит» невелик.

В конце я не могу найти доказательств того, что ваш действительно монолитный проект обеспечит более быструю сборку во время изменений в .NET Core 2.1.Я могу увидеть доказательство того, что разделение его на несколько функциональных проектов даст преимущества.В итоге, проекты, которые не изменились, не будут перекомпилированы.

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

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...