В течение многих лет вопросы производительности приложений .NET были горячей темой. Одна из самых ранних статей датирована 2001 годом и дала много полезного для разработчиков.

Тема была актуальна десять лет спустя, и люди до сих пор задаются вопросом о лучшем инструменте профилировщика, когда-либо существовавшем на Stack Overflow. Что все это означает для современной разработки .NET? Мы решили спросить Марко Чеккони.

Марко Чеккони, инженер-программист Stack Overflow в Лондоне. Марко пишет о разработке программного обеспечения, кодировании, архитектуре и руководстве командой. Он также выступает на конференциях по всему миру.

В. Вы работаете в Stack Overflow. Каковы основные «болевые точки» вашего решения с точки зрения производительности?

Есть две основные болевые точки: с одной стороны, мы должны быть очень осторожны с количеством экземпляров объектов, которые мы создаем, и их влиянием на сборку мусора, а с другой стороны, мы должны быть очень осторожны с тем, как мы используем SQL Server, напишите SQL-запросы, способ построения таблиц и так далее.

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

Ваше решение построено на технологии .NET или в нем есть компоненты на иностранных языках, таких как C++, Java, Python и т. д.?

Я бы сказал, что мы на 99% C#, хотя у нас есть некоторые биты на C++ или C, но они действительно незначительны с точки зрения количества строк кода. Конечно, у нас есть TypeScript и JavaScript. Мы используем Javascript на сервере, чтобы скомпилировать Javascript в пакеты и минимизировать его и т. д. И мы используем SQL, конечно, это другой язык. вот об этом.

В Почему вы решили начать проект на C#, а не на каких-то других языках, других технологиях?

Хорошо, могу я немного перевернуть вопрос? Я думаю, что наиболее интересным моментом в этом вопросе является то, почему мы до сих пор используем эти технологии. Stack Overflow существует уже 8 лет и перешел от сотен тысяч просмотров страниц в день в начале к миллиардам просмотров страниц в месяц прямо сейчас, и мы все еще используем C#, так почему же мы не отказались от него? И ответ не из-за какой-то извращенной лояльности к Microsoft, а из-за того, что это язык с очень хорошей средой выполнения, который удовлетворяет наши потребности. И мы действительно не видим смысла вкладывать время, чтобы переключиться на что-то другое. Это работает более чем достаточно хорошо для нас прямо сейчас.

В Значит, у вас есть дополнительные ресурсы, чтобы обслуживать всплески посетителей, которые когда-нибудь придут на Stack Overflow?

На данный момент у нас загруженность 5%. Мы можем справиться с нагрузкой в ​​20 раз больше. В этот момент мы бы пострадали, мы действительно не хотим быть на 100% все время, но сейчас нагрузка колеблется между 5 и 10%, поэтому мы можем легко справляться с пиками трафика.

В Действительно ли мы хотим продолжить это интервью, потому что вы, ребята, похоже, вообще не сталкиваетесь с какими-либо проблемами производительности (смеется)?

О нет, у нас много проблем с производительностью. Но вы знаете, оптимизация производительности — это не то, что вы делаете только тогда, когда умираете, потому что ваш процессор весь день загружен на 80%. Есть причина, по которой у нас 5% [мощности] — потому что мы продолжаем оптимизировать, это не то, что происходит само собой, это результат непрерывной работы над производительностью.

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

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

Есть события, для которых мы действительно можем увидеть всплески в журналах. Поэтому, когда вышел Pokemon-Go, на всех графиках в течение нескольких дней наблюдался огромный скачок. Или на выборах в США была долина, на следующий день после выборов на сайт пришло значительно меньше людей.

Мы видим такие вещи, но мы говорим о менее чем 100% ударах.

Q Давайте перейдем к разделу инструментов. Какой ваш любимый инструмент, который вы используете для выявления узких мест в коде?

У нас есть собственный инструмент под названием MiniProfiler. Это инструмент с открытым исходным кодом, вы можете найти его на GitHub. Он работает на .NET и Ruby. В .NET это работает с операторами using. Мы используем эти использующие операторы, чтобы идентифицировать раздел кода, который мы хотим рассчитать по времени. Итак, если вы хотите зафиксировать время вызова, вы можете обернуть его в оператор using, и тем самым мы сможем создать профиль выполнения запроса. Например, у нас есть таймер, который запускается, когда мы начинаем обрабатывать запрос, и тот же таймер заканчивается, когда мы заканчиваем этот запрос. Каждый вызов базы данных, каждый вызов Redis, каждый вызов Elastic search обрабатываются таймерами. В разных разделах каждой страницы есть таймеры, поэтому, например, при рендеринге на главной странице — список вопросов, а не при рендеринге нижнего колонтитула или заголовка.

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

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

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

В Как вы отслеживаете свое решение? Помогает ли это выявить узкие места?

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

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

У нас есть другие инструменты мониторинга, которые всегда контролируют основные параметры сервера, такие как память и ЦП. У нас есть инструмент для мониторинга всех исключений, происходящих на всех серверах, во всех проектах.

В Это такой инструмент, как Telegraf?

Мы используем инструмент под названием OpServer, инструмент, который мы создали для этой цели, и он с открытым исходным кодом.

В Следующий вопрос касается вашего подхода к оптимизации производительности. Что вы делаете с кодом, который «пахнет»?

Выделения обычно плохо пахнут, но их не всегда легко найти. Иногда единственный способ выяснить это — взять дампы памяти IIS и посмотреть, что в них и почему занимает так много памяти, ведь выделения могут происходить где угодно. Это может произойти в вашем собственном коде, но это может произойти и в библиотеке, которую вы вызвали. Так что они могут происходить в какой-то библиотеке, которую мы добавили, это может происходить в библиотеках .NET — например, StringBuilders начали добавлять одну аллокацию в свой конструктор, и мы, конечно, заметили это, потому что наша память была заполнена частичными строками.

"Компоненты StringBuilders начали добавлять одно выделение в свой конструктор, и мы это заметили".

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

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

В Вы упомянули специальный инструмент, который используете в StackOverflow. Итак, не могли бы вы сказать пару слов о сравнительном анализе.

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

«Производство — наш ориентир»

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

В Иногда оптимизированный код уродлив и полон хаков. Как вы работаете с оптимизированным кодом?

Иногда оптимизация — это хаки или уродливые вещи, такие как развертывание циклов, но в большинстве случаев на самом деле оптимизация — это не то, что делает код уродливее. На самом деле, я бы сказал, что во многих случаях выполнение кода намного красивее.

«… во многих случаях выполнение кода гораздо красивее».

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

В Но более компактный код обычно менее удобочитаем и понятен, не так ли?

Что ж, более компактный код не обязательно более понятен или удобен в сопровождении, но если ваш код полагается только на себя и не имеет зависимостей, то он на самом деле более удобен для чтения и сопровождения. Я думаю, что это одна из главных вещей — наш код очень самостоятельный. Подумайте об этом — скажем, вы пишете фиктивный проект, очень-очень маленькую вещь, просто тестовый пример или что-то очень компактное, порядка пары сотен строк кода. Может быть, вы запустите LinqPad и поместите туда что-нибудь — знаете, очень компактное, и все там. Этот код будет для вас очень ясен. Там сто строк кода, вы можете прочитать их все, вы можете держать их в голове, у вас нет сомнений в том, как они работают — они все есть. И это то, к чему мы стремимся, поэтому каждая функция очень компактна и автономна.

«Поэтому каждая функция [Переполнения стека — редактор] очень компактна и автономна»

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

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

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

"да, мы знаем о проблеме [существующих ошибках производительности в сторонних библиотеках], поэтому мы пишем так много библиотек с открытым исходным кодом"

Что касается сторонних библиотек — да, мы знаем об этой проблеме, поэтому мы пишем так много библиотек с открытым исходным кодом — потому что во многих случаях наши потребности сильно отличаются от потребностей широкой публики. Мы постоянно переписываем библиотеки. Например, у нас есть собственные библиотеки кэширования, собственный клиент Redis, собственная реализация protobuf, собственный сериализатор Json и так далее.

В Кстати, у вас есть собственная реализация C#?

У нас есть версия компилятора на основе Roslyn, в основном для компиляции шаблонов Razor, но мы используем ее только для локализации, мы используем ее только для выполнения определенных действий, которые нам нужны, но не для расширения языка. Мы используем сам язык Vanilla. Причина в том, что если вы измените его, то это будет проблемой, поскольку это сломает Visual Studio, это сломает все остальное, поэтому мы действительно не хотим этого делать.

Q Звучит очень круто. Имея в виду все проблемы, связанные с оптимизацией производительности, есть ли какие-то сигналы для разработчика, чтобы начать оптимизацию кода или не начинать вообще?

Всегда начинайте сразу, постоянно оптимизируйте код. Производительность — это фича, отсутствие производительности — это баг.

«Всегда начинайте сразу, постоянно оптимизируйте код. Производительность — это функция, а низкая производительность — это ошибка».

В Это философский вопрос — все дело в деньгах в ИТ. И какой-нибудь парень мог бы сказать вам — хорошо, сколько времени вы потратите на оптимизацию производительности? И вы могли бы дать ему какую-то оценку — например, 40 часов. За эти 40 часов можно было либо внедрить какую-то фичу для конечного пользователя, либо потратить на оптимизацию, которая, видимо, может стать не очень нужной. Это своего рода компромисс.

Я не согласен с этим. Если у вас есть ошибка в продакшене, разве вы не исправите ее?

Интервьюер: Да, исправлю.

Правильно, потому что это одно и то же. Ну, отсутствие производительности - это ошибка. Так что вам нужно исправить это, и это очень просто.

Интервьюер:А как определить, что в рабочей системе есть ошибка?

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

«…когда страница переходит с 20 миллисекунд на 2 секунды — вам нужно это исправить, вы не можете заставить пользователя ждать страницы 2 секунды. Или, может быть, некоторые другие компании могут себе это позволить, но мы не собираемся этого делать».

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

Мы делаем. В одном конкретном случае мы используем CUDA для выполнения некоторых сильно распараллеленных вычислений. Мы не часто используем параллелизм, в основном для таких вещей, как сборка, поэтому, когда нам нужно обработать кучу файлов, мы стараемся использовать как можно более многопроцессорные или многопоточные процессы. Что касается фактического кода, мы используем асинхронный, чтобы пропустить все внешние вызовы, такие как вызовы базы данных, но я не знаю, можем ли мы действительно назвать это многопоточным. Дайте подумать… В большинстве случаев лучшая стратегия, которую мы используем для веб-серверов, — это быть максимально быстрыми, а не разбросанными по разным ядрам. Потому что мы всегда конкурируем с определенным количеством других запросов, поступающих от других клиентов. Использование ЦП, вероятно, лучше оставить IIS, который распределяет запросы по разным ядрам ЦП. С точки зрения того, чтобы вместо этого иметь, возможно, библиотеки или серверы приложений в бэкэнде, это имеет гораздо больше смысла, потому что запросов становится все меньше и меньше, поэтому становится лучше, если мы можем использовать больше ядер для каждого запроса. Фактически из этого вышла CUDA. Мы увидели, что увеличение уровня параллелизма увеличило производительность, поэтому мы сказали давайте попробуем это, на самом деле другой разговор, который я собираюсь дать, как раз об этом.

В Можете ли вы рассказать больше о значке «НЕ РОБОТ»?

StackOverflow — это сообщество разработчиков, и мы вознаграждаем разработчиков за помощь, присваивая им баллы, называемые репутацией, и выдавая им значки. И они предназначены для поощрения людей делать то, что мы считаем положительным для сообщества. Например, если вы зададите отличный вопрос, вы получите значок; если вы дадите отличный ответ, вы получите значок. В этих условиях мы добавили значок под названием «НЕ РОБОТ», который мы награждаем людей, которые приходят и взаимодействуют с некоторыми из наших спикеров или другими представителями, которые посещают конференции. Причина в том, что мы понимаем, что мы говорили в течение долгого времени, и мы были очень и очень активны в течение последних нескольких лет на конференциях, и мы заметили, что люди во многих случаях застенчивы, и потому что разработчики, возможно, интроверты, или, может быть, потому, что они не думают, что мы доступны, или потому, что они думают, что Stack Overflow — это какой-то инопланетянин, и они не хотят приходить и разговаривать с нами. Поэтому мы просто создали этот значок, чтобы поощрить людей. Если вы придете поговорить с нами, мы дадим вам код, вы можете использовать этот код, чтобы выкупить значок на сайте, и вы получите серебряный значок, который встречается относительно редко, и это единственный способ его получить.

Мы хотим раздать их в Хельсинки и Москве на конференциях DotNext.

В Что бы вы порекомендовали всем разработчикам .NET и C#?

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

Делать то, что делает их страстными. Очень важно, что разработка — это не просто работа. Разработка также является творческим начинанием, потому что, в отличие от создания автомобиля, быть разработчиком означает решать разные проблемы каждый день. Это означает делать что-то другое, постоянно исследовать то, чего вы не знаете. Важно не только делать свою работу профессионально, но и делать ее с душой. Важно развивать свои увлечения, они должны думать, что этого недостаточно, это не нормально, если кто-то просто делает свою работу, и им все равно. Они должны думать — что сделает их счастливыми, и делать это.

— — —

Как видите, тема производительности приложений активно развивается и остается актуальной для многих разработчиков .NET. Для справки есть онлайн-курс Саши Гольдштейн Pluralsight. Также Марко выступит с докладом о DotNext Helsinki всего через несколько дней.

Более подробная информация о DotNext Helsinki, его программе, спикерах и регистрации доступна на официальном сайте.

Благодарим Microsoft за предоставленное место.