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

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

З новими можливостями нас зустрічають нові виклики:
  • Як буде працювати один і той же код .NET і Mono Core, 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;
  • Microsoft MVP, к. ф.-м. н., срібний призер ACM ICPC, постдок в Вейцмановском інституті науки і розробник JetBrains Андрій Акіньшин;
  • CTO Promarket і експерт в області Mono і Linux Микита Цуканов.

Що змінилося .NET з переходом на Roslyn?
Саша Гольдштейн
Для Microsoft Roslyn – проект величезної важливості. Майже всі в C#-команді працювали над ним років сім, напевно, і дуже довго не могли випустити. І зараз він на github в open-source і весь процес змін мови перед очима. З одного боку, з'явилося багато людей з дивними ідеями додати в C# конструкції з інших мов, з іншого – весь процес відбувається відкрито і за дві години на будь-яке питання тобі можуть відповісти три людини, які пишуть компілятор. Для community – це дуже здорово.

Андрій Акіньшин
В цілому Roslyn мені дуже подобається. Як компілятор він краще, ніж старий: багато ділянок коду він компілює більш грамотно і більш вдало з точки зору продуктивності. Що стосується RyJIT, однозначно відповісти не можна. Однією з основних завдань RyuJIT було зниження час JIT-компіляції. Ми даємо користувачеві дуже швидкий старт, але з-за цього не можемо зробити кльові оптимізації, тому сам код працює повільніше, ніж міг би, а іноді навіть повільніше, ніж старий JIT. При цьому RyuJIT вміє використовувати SSE, AVX-інструкції, використовуються більш сучасні операції над числами з плаваючою точкою.

У плані продуктивності, мені здається, 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. Плюс Microsoft постійно щось перейменовує, кожен день все змінюється, одні баги чиняться, інші додаються.

Mono розробляється понад 15 років. Це дорослий, солідний рантайм, на якому працює багато production-коду. Я в JetBrains працюю над проектом Rider (платформна C# IDE). Backend Rider'а (це в чистому вигляді ReSharper) працює під Mono, а під .NET Core поки не дуже виходить запустити. І відмінно працює: величезна додаток із сотень проектів і дуже нетривіальною логікою, з усіма фічами .NET, і все це під *nix нормально заводиться. А під .NET Core не заводиться, так що, потрібно чекати, поки .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% випадків, потрібно багато знати про те, як все влаштовано.

Микита Цуканов
Проблема абстракцій в тому, що вони мають властивість протікати, тому в команді потрібен один чоловік, який розуміє, як усередині все працює. Приблизно також з людиною, яка добре знає математику. Традиційні низькорівневі проблеми – це витоку пам'яті, виключення в native-коді, heap corruption.

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

Що робити, якщо «гальмує»? Які перші 2-3 кроки?
Саша Гольдштейн
Не треба гадати. Потрібно запустити tool і отримати інформацію про те, що відбувається в процесі. Подивіться пам'ять, CPU, скільки потоків, що роблять. Зараз багато безкоштовних утиліт. Після цього є купа методів. Мій улюблений – USE (utilization, saturation, errors). Його придумав Брендон Грег (Brendan Gregg) — експерт по продуктивності в Linux. Utilization означає, що ми повинні для кожного ресурсу (ПРОЦЕСОР, пам'ять, диск ...) визначити, на скільки він використовується. Saturation – означає, що ми повинні для всіх цих ресурсів визначити чи over subscription, наприклад, читаємо ми 30 файлів з одного диска одночасно. Errors – кількість помилок в програмі. Після цього можна щось оптимізувати: будемо додавати процесори або код покращувати. Це в загальному.

Андрій Акіньшин
Перше, що потрібно зробити – профілювати і зрозуміти, що саме гальмує. Помилка №1: оптимізація без профілювання. Після того як ми знайшли вузьке місце, другий крок – поставити собі питання: «Чому цю ділянку коду гальмує»? Тут потрібно мати дійсно багато знань: потрібно здогадатися, які фактори дійсно важливі і перевіряти тільки їх, в іншому випадку доведеться витратити дуже багато часу. І третій крок – виправити проблему і акуратно перевірити, що нічого не зламалося, і програма дійсно тепер працює швидше.

Дмитро Іванов
Класики рекомендують взяти профайлер, знайти вузькі місця і пофіксити їх. Але коли ви це кілька разів вже робили, відверто вузьких місць уже не залишилося й не можна прискорити програму в кілька разів, пофиксив пару рядків. Іноді в цих випадках потрібно переглянути підхід, наприклад, вставити за кодом Assert'и, які переконуються, що операції завершуються за відведений час. І змусити кожного члена команди виправляти падаючі в його зоні відповідальності перфоманс-ассерти. Ще корисно подивитися на memory traffic і почати з ним боротися. Можливо навіть піти в unsafe.

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

Андрій Акіньшин
Я є мейнтейнером утиліти BenchmarkDotNet. Останні версії вже цілком юзабельны, з великою кількістю можливостей. Тим не менше я постійно знаходжу історії, як люди беруть Stopwatch і починають робити свої самопальні бенчмарки. Проблема кожної другої такої історії: люди запускають бенчмарки в debug'е, тому що це конфігурація обрана за замовчуванням в студії. BenchmarkDotNet не дасть запустити бенчмарк в дебаге, дотримується методологію, запускає код в різних процесах, кожен процес запускається кілька разів, робить прогрів і т.д. Зараз навколо проекту зібралося community та інструмент стає зручним для інженерної роботи.

Як профілювання я можу порекомендувати dotMemory і dotTrace. Я ними часто користуюсь, і вони мені дуже подобаються.

Дмитро Іванов
Всі наші інструменти: dotTrace, dotMemory. Майже для всього вистачає. Іноді потрібно подивитися на sys internals-утиліти.

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

Андрій Акіньшин
Я б йшов зверху вниз і не став би йти на рівень CPU, якщо немає базових знань про алгоритми, структури даних і про платформу, з якою працюєш. З .NET можна почитати того ж Ріхтера, розібратися, як працюють базові класи і для чого гарні.

Далі, добре б розбиратися, як працює сучасно залізо, зокрема процесори компанії Intel. В цьому плані є багато ресурсів в інтернеті, але це не просте чтиво: не можна прочитати за вечір книжку і розібратися, як працює процесор.

Людині, яка не спеціалізується на 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.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.