Как стать автором
Обновить
170.26
JUG Ru Group
Конференции для Senior-разработчиков

.NET-разработка: девять вопросов взрослым

Время на прочтение 14 мин
Количество просмотров 38K
.NET становится по-настоящему кроссплатформенным: после долгого ожидания наконец объявлена дата релиза ASP.NET Core, JetBrains готовит альтернативу Visual Studio на базе ReSharper и IDEA, Microsoft приобрела Xamarin, сделала Xamarin Community бесплатной, а Mono перевела на MIT-лицензию и наконец, Windows Server 2016 получит поддержку Windows-контейнеров в Docker.

С новыми возможностями нас встречают новые вызовы:
  • Как будет работать один и тот же код под .NET Core и Mono, на Windows и Linux, в docker-контейнере?
  • Стоит ли переходить на .NET Core уже сейчас и как получить максимум от новой платформы?
  • Какие перспективы у Mono и Xamarin?
  • Какие изменения произошли «под капотом» .NET с переходом на Roslyn и .NET Core?

Всего через три недели на конференции DotNext в Питере 20 спикеров выступят с докладами о настоящем и будущем платформы .NET, об оптимизации производительности и многопоточности, о внутреннем устройстве платформы .NET и CLR, о профилировании и отладке .NET-кода.

А пока мы попросили четырех из них поделиться своим опытом и мнениями о грядущих изменениях в мире .NET. На наши вопросы ответили:

  • Ведущий мировой эксперт по производительности .NET-платформы, восьмикратный Microsoft MVP, автор прекрасной книги по производительности .NET «Pro .NET Performance» Саша Голдштейн;
  • Главный разработчик протокола реактивного многопроцессного взаимодействия в Rider Дмитрий Иванов из JetBrains;
  • Ещё один разработчик Rider’a из компании JetBrains, .NET MVP, к.ф.-м.н., серебряный призёр ACM ICPC, постдок в Вейцмановском институте науки Андрей Акиньшин;
  • CTO Promarket и эксперт в области Mono и Linux Никита Цуканов.

Что изменилось в .NET с переходом на Roslyn?


Саша Гольдштейн
Для Майкрософт Roslyn – проект огромной важности. Почти все в C#-команде работали над ним лет семь, наверное, и очень долго не могли выпустить. И сейчас он на github в open-source и весь процесс изменений языка перед глазами. С одной стороны, появилось много людей со странными идеями добавить в C# конструкции из других языков, с другой – весь процесс происходит в открытую и за два часа на любой вопрос тебе могут ответить три человека, которые пишут компилятор. Для community – это очень здорово.

Андрей Акиньшин
В целом Roslyn мне очень нравится. Как компилятор он лучше, чем старый: многие участки кода он компилирует более грамотно и более удачно с точки зрения производительности. Но если мы говорим о компиляции C#-кода, то тут меня больше интересует следующий этап: JIT-компиляция. Не так давно Microsoft представила новый 64-битный JIT-компилятор под названием RyuJIT.

Одна из его основных задач — снижение время JIT-компиляции. Мы даем пользователю очень быстрый старт, но из-за этого не можем сделать клевые оптимизации, поэтому сам код работает медленнее, чем мог бы, а иногда даже медленнее, чем старый JIT. При этом RyuJIT умеет хорошо использовать SSE и AVX инструкции, что позволяет при должном усердии реализовывать весьма эффективные программы силами одного C# без обращений к нативному коду.

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

Никита Цуканов
Главная фишка Roslyn в том, что Microsoft могут теперь с приемлемой скоростью добавлять новые фичи. Раньше был один компилятор для сборки, другой – для студии. Теперь этого нет – остался один компилятор и он написан на C#.

Дмитрий Иванов
С точки зрения API конечно стало лучше, но есть проблемы с производительностью и лишней памятью, и они далеки от решения. Теперь мы не можем в Visual Studio 2015 вместе с ReSharper открывать гигантские проекты, потому что не хватает памяти.

Можете сравнить Mono с .NET Core? Каковы перспективы того и другого?


Саша Гольдштейн
Mono переходит на MIT-лицензию. Значит, Microsoft смогут скопировать куски Mono в CoreFx и пользоваться ими. Мне кажется, что Mono потихоньку должен угаснуть. У Mono будут какие-то ниши, на которых .NET Core пока не будет работать. Но сам runtime у Mono так себе: GC только недавно перешёл на Generational GC, JIT – работает не очень. И сам фреймворк, который они написали во многих случаях не production-ready: там куча багов, баги обнаруживаются и подолгу не чинятся. Почти все мои клиенты, использовавшие Mono в production, сталкивались с проблемами. Это не значит, что в итоге система совсем не работала, но это не тот level of quality, к которому мы привыкли в .NET.

Но с .NET Core тоже не всё гладко. Проект должен был закончиться в январе, потом в марте, буквально несколько дней назад, сказали, что в конце июня будет готово. API, проектная модель, половина кода постоянно меняются.

На прошлой неделе один из клиентов с проектом на ASP.NET MVC 6 спросил, как я считаю, следует ли чинить код каждые две недели или лучше сделать backport на ASP.NET MVC 5 и подождать. Вопрос не простой.

Андрей Акиньшин
.NET Core – безусловно очень интересный проект, большой шаг вперед, но на сегодняшний день он еще сырой. Не будем забывать, что многие библиотеки под него еще не вышли или находятся в RC. Плюс Майкрософт постоянно что-то переименовывает, каждый день все меняется, одни баги чинятся, другие добавляются. Два года разрабатывалась и продвигалась новая проектная система на основе project.json-файлов, а буквально на днях её выкинули на помойку и решили вернуться к старым добрым csproj. Подобные ситуации отбивают желание в обозримом будущем пытаться переводить продакшн-системы на .NET Core.

Mono разрабатывается более 15 лет. Это взрослый, солидный рантайм, на котором работает много production-кода. Например, я в JetBrains работаю над проектом Rider (кроссплатформенная .NET IDE). Backend Rider’а (это в чистом виде R#) запущен под Mono, под .NET Core не очень получается запустить. Работает весьма неплохо: здоровое приложение из сотен проектов и очень нетривиальной логикой, со всеми фичами .NET и все это под *nix нормально заводится. Есть, конечно, свои проблемы, но они вполне себе решаются в рабочем порядке. А по поводу перехода на .NET Core: нужно ждать, когда этот новый кроссплатформенный рантайм станет более стабильным.

Никита Цуканов
Mono – достаточно зрелая среда, а .NET Core ещё в бете и по количеству доступных библиотек .NET Core безусловно проигрывает. Например, в .NET Core сейчас нет поддержки System.Drawing, а в Mono она появилась в 2000-лохматом году.

Что касается производительности и качества покрытия .NET Framework, Mono активно переносит к себе те куски, которые Microsoft открывает. Учитывая, что Microsoft приобрёл Xamarin, процесс должен пойти ещё быстрее. В какой-то момент Mono станет надстройкой над .NET Core, но пока имеет смысл использовать именно Mono.

Дмитрий Иванов
У нас, наверное, почти у всех есть мнение, что Mono в какой-то момент дропнут. В свое время Oracle тоже говорил, что объединит HotSpot JVM с JRockit, но в итоге дропнул последнюю.

Так что, наверное, стоит немного подождать, и переходить с Mono на .NET Core. Мы в Rider много работаем с Mono и видим, что там много ошибок и код сильно менее качественный, чем в .NET Framework.

Часто ли приходится придумать что-то своё и уникальное в области высокопроизводительных решений или есть хорошие готовые библиотеки?


Андрей Акиньшин
Я считаю, что нет серебряной пули, нет универсальной библиотек, которые подходят всем. Нужно подбирать решение под свою задачу. Если вы работаете с БД, есть базы данных, которые работают быстрее других на определенных показателях, если вы работаете с графикой, есть разные хорошие библиотеки для рендеринга 2D/3D, но каждая хороша для своего спектра задач. Нужно помнить, что каждый проект по-своему уникален. Чаще всего для одних задач подходят одни решения, для других – другие.

Очень популярная ошибка: человек слышал, что библиотека «ABC» хорошая, он её берёт и использует, не задумываясь о том, для каких задач она предназначена, на каких сценариях она ведёт себя быстро…

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

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

Если говорить о проблемах производительности на одной машине, она тоже постоянно решается, потому что, скажем так, мы довольно быстро выходим за рамки идиоматичного C#-кода. Довольно быстро приходится переходить с большого количества ссылок на массивы, потому уходить в unsafe. Без этого просто не работает. И наш протокол в Rider Framework мы делаем с оглядкой на высокую производительность.

В чём главная сложность многопоточного программирования на .NET? В чем главный вызов?


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

У многих разработчиков нет доступа к многопроцессорным системам. Когда один и тот-же код выполняется не на Core i7, а на сервере хотя-бы с 64 процессорами появляются совершенно другие проблемы. Какой-то маленький lock, который занимал 2% времени вдруг начинает занимать 50%, есть проблемы memory bandwidth (система памяти не способна отвечать на запросы достаточно быстро), не эффективное использования кеша. .NET-разработчики чаще всего даже не сталкиваются с такими задачами.

Андрей Акиньшин
Я думаю, что сложности многопоточного программирования на .NET такие-же, как и на любой другой платформе. Нужно хорошо понимать, как работает многопоточный мир. Что касается примитивов синхронизаций и concurrent-структур данных, то в .NET с этим все хорошо. Плюс в C# 5 появились async/await, которые позволяют писать многопоточный код в красиво и понятно.

Никита Цуканов
Нет возможности нормально предотвратить возможность доступа одним потоком к данным, которые находятся во владении другого. В Rust’е для этого сделали более-менее нормальную систему (уничтожающее присвоение). В .NET такого нет и без этого очень тяжко: может получиться так, что, когда мы из одного потока в другой что-то передаём, в силу невнимательности или иных причин, в другой поток может пройти что-то лишнее. И с «этим лишним» могут начать работать, хотя поток-владелец об этом ничего не знает. В этом случае конкурентные обращения на запись происходят там, где никто не знает. С ростом проекта отслеживать и бороться с этим становится все сложнее и сложнее.

Дмитрий Иванов
Хороший вопрос. Столько лет программирую многопоточно и рассказываю людям как это делать, но так сам и не разобрался. Наверное, главный challenge возникает на верхнем уровне – придумать модель, которая успешно подходит для большой команды разработчиков и большого продукта и заставить всех ей следовать и надеяться, что ты никогда не упрёшься в ограничения этой модели.

Команда Roslyn решила использовать транзакционную модель многопоточного взаимодействия, и они уперлись в Memory Wall. Мы с пессимистичными блокировками и read/write lock-моделью сталкиваемся с долгим не отпусканием read lock и не гладким тайпингом.

Серебряной пули нет, не спасают даже новомодные акторы: на них хорошо работает Hello World, но в реальности все сложнее.

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


Саша Гольдштейн
Не избавляют, нужно. Вопрос только, насколько нужно. Обязательно ли уметь читать ассемблер, понимать memory model, как устроен процессор? Стандартный совет – понимайте, как минимум на один уровень ниже того, на котором вы работаете. Если вы работаете на .NET вы должны понимать, как работает ОС, Runtime, GC, Framework. Без этого,  даже если вы найдёте проблему, вы не будете знать, как её исправить.

Андрей Акиньшин
Зависит от того, какие задачи решает программист. 90% времени, пока нет задачи выжимать из машины максимум, программист должен думать о том, чтобы код был надежным, читаемым, без багов. В оставшихся 10% случаев, когда возникли проблемы (или мы догадываемся, что проблемы явно возникнут), и мы стараемся их решить, то да, нужно знать, как все происходит внутри.

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

Никита Цуканов
Проблема абстракций в том, что они имеют свойство протекать, поэтому в команде нужен один человек, который понимает, как внутри всё работает. Примерно также с человеком, который знает математику хорошо. Традиционные низкоуровневые проблемы – это утечки памяти, исключения в native-коде, heap corruption.

Дмитрий Иванов
На мой взгляд, любой .NET-инженер, должен как минимум представлять, как работает процессор и процессорные кеши, чуть-чуть представлять, как работает branch prediction и понимать, почему последовательный доступ к массиву и диску всегда работает быстрее параллельного.

Что делать, если «тормозит»? Каковы первые 2-3 шага?


Саша Гольдштейн
Не надо гадать. Нужно запустить tool и получить информацию о том, что происходит в процессе. Посмотрите память, CPU, сколько потоков, что делают. Сейчас много бесплатных утилит. После этого есть куча методов. Мой любимый – USE (utilization, saturation, errors). Его придумал Брэндон Грег (Brendan Gregg) — эксперт по производительности в Linux. Utilization значит, что мы должны для каждого ресурса (CPU, память, диск …) определить, на сколько он используется. Saturation – значит, что мы должны для всех этих ресурсов определить если ли over subscription, например, читаем ли мы 30 файлов с одного диск одновременно. Errors – количество ошибок в программе. После этого можно что-то оптимизировать: будем добавлять процессоры или код улучшать. Это в общем.

Андрей Акиньшин
Первое, что нужно сделать – попрофилировать и понять, что именно тормозит. Очень распространённая ошибка — оптимизация без профилирования. После того, как мы нашли узкое место, второй шаг – задать себе вопрос: «почему этот участок кода тормозит»? Здесь порой нужно иметь действительно много знаний: нужно догадаться, какие факторы действительно важны и проверять только их, в противном случае программист рискует потратить очень много времени на никому не нужную работу. И третий шаг – исправить проблему и аккуратно проверить, что программа действительно теперь работает быстрее (а ещё, что во время оптимизаций ничего не отломалось).

Дмитрий Иванов
Классики рекомендуют взять профайлер, найти узкие места и пофиксить их. Но когда вы это несколько раз уже делали, откровенно узких мест уже не осталось и нельзя ускорить программу в несколько раз, пофиксив пару строчек. Иногда в этих случаях нужно пересмотреть подход, например, вставить по коду Assert’ы, которые убеждаются, что операции завершаются за отведённое время. И заставить каждого члена команды исправлять падающие в его зоне ответственности перфоманс-ассёрты. Ещё полезно посмотреть на memory traffic и начать с ним бороться. Возможно даже уйти в unsafe.

Какие инструменты — ваши любимые при поиске Performance-проблем?


Саша Гольдштейн
На Windows нужно начинать с Performance Counters. После этого зависит от ситуации. Кучу проблем можно решать только с PerfView, но у него большие проблемы с визуализацией. Visual Studio Profiler – совсем неплох, кстати, у него есть standalone-версия. dotMemory или .NET Memory Profiler– для профилирования памяти.

Андрей Акиньшин
Я являюсь мейнтейнером утилиты BenchmarkDotNet. Последние версии этой библиотеки уже вполне себе стабильно себя ведут и предоставляют широкие возможности для бенчмаркинга. Увы, многие люди из интернетов постоянно пытаются делать свои самопальные бенчмарки на основе Stopwatch и пары циклов. При этом их на каждом шагу окружают проблемы, о которых они не подозревают, и которые способны полностью испортить выводы из проводимого эксперимента. Но это только часть беды: ведь можно чудом пройти мимо всех граблей и в данной конкретной ситуации написать корректный код, который действительно замеряет что-то полезное. Но вот только каждый второй начинающий перфоманс-инженер запускает свои бенчмарки в DEBUG’е, потому что это конфигурация выбрана по умолчанию в студии.

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

В качестве профилирования я могу порекомендовать продукты компании JetBrains: dotMemory и dotTrace. Я ими часто пользуюсь, и они мне очень-очень нравятся.

Дмитрий Иванов
Все наши инструменты: dotTrace, dotMemory. Почти для всего хватает. Иногда нужно посмотреть на sys internals-утилиты.

Что посоветуете почитать и посмотреть на тему .NET Performance?


Саша Гольдштейн
Есть куча материалов online, есть мои курсы на pluralsight, довольно много информации можно найти в старых блогах Microsoft-разработчиков (Рико Мариани, Крис Брю, Ванс Мориссон). Про книги: есть моя книга Pro .NET Performance, есть книга поновее – Бена Уотсона «High Performance .NET Applications». Тоже достаточно интересно написано. Плюс, нужно что-то знать и понимать про устройство .NET. Есть классическая книга CLR via C#. Она немного устарела, но некоторые моменты просто нигде больше не упоминаются, например, о том, как работают исключения и делегаты.

Андрей Акиньшин
Я бы шел сверху-вниз и не стал бы уходить на уровень CPU (как некоторые пытаются делать), если нет базовых знаний об алгоритмах, структурах данных и о платформе, с которой работаешь. По .NET можно почитать того-же Рихтера, разобраться, как работают базовые классы и для чего они хороши. Крайне важно понимать устройство рантайма: как работает GC, чем классы отличаются от структур, что из себя представляет JIT-компилятор и т.д.

Дальше, хорошо бы разобраться как работает современно железо, в частности процессоры компании Intel (хотя бы в общих чертах). В этом плане есть много ресурсов в интернете, но это зачастую не очень простое чтиво: нельзя прочитать за вечерок книжку и разобраться деталях устройства CPU.

Впрочем, человеку, не специализирующемуся на performance, а работающему в enterprise, это и не нужно. Я советую таким людям прокачивать общую эрудицию и расширять кругозор. В этом плане очень здорово ходить на такие конференции как DotNext. Я сам очень люблю смотреть доклады по областям, которыми я профессионально не занимаюсь. У меня в любом случае нет времени основательно заниматься всеми областями программирования, но я хотя бы буду иметь общее представление и смогу разговаривать на соответствующие темы с другими программистами. Если мне придется решать задачи, связанные с новой для меня областью, то я буду знать в какие стороны начинать смотреть и что гуглить.

Дмитрий Иванов
Зайти на доклад Саши Гольдштейна:) Прочитать его книгу, а потом обязательно заняться на работе задачами, требующими оптимизации производительности, иначе мозг просто откинет информацию как ненужную.

О чём вы будете рассказывать на DotNext в Питере?


Саша Гольдштейн
У меня два доклада: про инструмент для профилирования и про модели памяти. В первом мы будем использовать PerfView. У меня заготовлены примеры кода с performance-проблемами, которые мы будем анализировать. PerfView – это фронтэнд для технологии ETW. Про неё, кстати, есть ещё один доклад на DotNext. ETW – это технология, с помощью которой можно собирать лог. В этот лог пишут многие компоненты Windows, .NET, CLR, ASP.NET. И пишут эти логи с очень большой скоростью. PerfView – это эффективный анализ логов ETW: CPU, время выполнения, доступы к БД, allocation profiling.

Второй доклад — про модели памяти. Memory Model – это «страшное» название для всех операций над памятью на всех высокоуровневых языках. Если программа работает в одном потоке – всё просто, а если потоков много, ситуация меняется. Запись и чтение могут идти не в том порядке, который мы указали, часть доступов к памяти компилятор может вообще оптимизировать. Тоже самое с процессорами. И когда делается порт с Intel на ARM, который во всех айфонах, андроидах, или Power PC, который во всех автомобилях, куча всего перестает работать, потому что процессор позволяет себе больше.

Я собираюсь показать несколько теоретических и практических примеров, когда на Intel работает правильно, а на айфоне ломается или, когда при переходе с одного потока на несколько ломается на Intel, хотя почти никто, глядя на этот код, не может сказать, что есть какая-то проблема. После того, как мы разберем тонкости Memory Model мы перейдём к примерам, как строить правильный multithreaded-код с правильной синхронизацией.

Андрей Акиньшин
Буду рассказывать про арифметику. Я много раз видел, как программисты допускают досадные ошибки в простейших арифметических выражениях, делают баги, которые потом неделями ищутся (почему же у нас там мат. часть неправильно работает?) – просто из-за того, что не понимают, как устроены числа с плавающей точкой.

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

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

Никита Цуканов
Планирую рассказать о Docker как об элементе инфраструктуры для разработки и деплоя. Как быстро и эффективно разворачивать микросервисы и управлять ими. Также я расскажу о том, как запускать Windows Server Core в Docker.

Дмитрий Иванов
Rider – это гибрид Idea и ReSharper. Мы используем общую реактивную модель. Idea выставляет какие-то параметры, на это реагирует R# и выставляет необходимые данные для отрисовки и такой процесс может повторяться несколько раз.

Rider Framework – это библиотека для реактивного взаимодействия Java и .NET и генератор модели из DSL на целевых языках. DSL у нас написан на Kotlin, потому что Kotlin позволяет некоторое мета-программирование в groovy-style, благодаря своим синтаксическим особенностям. В докладе будет live-demo.

Я думаю, что мой доклад будет интересен тем, кто хочет вынести свое приложение out-of-process и/или интересуются реактивными фреймворками и применением реактивной модели для desktop-приложении.



Если у вас есть вопросы к спикерам, вы хотите получить дозу хардкорного .NET и узнать, как и сколько раз изменится погода в Санкт-Петербурге, ждём вас 3 июня на конференции DotNext 2016 Piter.
Теги:
Хабы:
+33
Комментарии 19
Комментарии Комментарии 19

Публикации

Информация

Сайт
jugru.org
Дата регистрации
Дата основания
Численность
51–100 человек
Местоположение
Россия
Представитель
Алексей Федоров