Как добиться успеха в системном программировании, что нужно знать и понимать, особенно если ты работаешь уже третий десяток лет? C# и перформанс — cтоит ли переписывать на C# всё что видишь? Какое будущее в смысле низкоуровневых компиляторных технологий нас ждёт?
Сегодня в нашей виртуальной студии на вопросы отвечает Alexandre Mutel.
Alexandre Mutel работает на должности Lead Software Architect в Unity Technologies. Кроме того, он известный в опенсорсе разработчик, контрибутящий в SharpDX, Markdig, Zio и другие проекты, а с 2014 года — MVP в категории «Visual Studio and Development Technologies».
Alexandre работает над разными низкоуровневыми и высокоуровневыми вопросами в областях рендеринга графики в реальном времени, GPGPU, синтеза звука, эффективного использования и архитектуры управляемых языков, кодогенерации и документации.
Как всегда, интервью ведут Евгений Трифонов (phillennium) и Олег Чирухин (olegchir) из JUG.ru Group.
В конце поста есть сюрприз от Дилана Битти (другого известного дотнетчика) — мы и сами не ожидали.
E.: У вас длинная карьера, три десятилетия — для начала можете вкратце рассказать о ней?
Всё началось в детстве — у меня появился Amstrad PC 464. Когда я начал программировать на этом компьютере, мне было то ли 11, то ли 12, точно не помню. Я быстро освоил программирование на BASIC и накупил книжек по разработке игр: тогда это казалось очень интересным. Я совсем мало играл в игры, куда интересней было разрабатывать, писать код. Потом я продолжил писать код на ассемблере для Amstrad.
В 16 лет у меня появилась Amiga 500. Встретил ребят, которые занимались написанием демок — это было совсем не то, что сейчас. Сейчас это WebGL, и это уже совсем другая демосцена. Я начал писать много демок, которые я не всегда показывал, но мне просто нравилось писать на ассемблере. И это было так просто.
Потом пошёл в технологический колледж, где изучал компьютерную инженерию. Это уже было нечто совсем другое по сравнению с играми и ассемблером. Я обожал изучать вещи, про существование которых раньше даже не знал: операционные системы, UNIX, работа с языком C (до этого я пользовался только BASIC или ассемблером, потому что у меня не было денег, чтобы купить компилятор C/C++).
Когда окончил колледж, начал работать в индустрии валютного рынка. Эта была работа на французскую компанию в Нью-Йорке. Через два года я вернулся и попал в банк. Вообще, я не хотел работать в банке, я хотел работать в геймдеве. Но в результате залип там — новая область, много чему можно научиться. Так и провёл там лет 8-9, в основном имея дело с Java и немного с C++. Множество распределённых серверов и SQL БД, копирование баз данных… Совсем не то, чем я занимаюсь сейчас.
Потом я взял некий творческий отпуск и поехал в туристическую поездку по миру: был в Африке, в Южной Америке, целый год в Азии год. Путешествие меня поменяло и встряхнуло. Когда я вернулся, то не мог уже работать с компьютерами, в IT-сфере, не мог работать на банк. Я уволился и провёл 4 года на курсах социальных работников, чтобы работать с детьми, с бездомными, инвалидами, пожилыми людьми. Три года я учился этому, и это было очень интересно, потому что большую часть жизни я работал в области точных наук: математики, проектов, абстракций. А потом вдруг взял и перешёл в очень гуманитарную область. Пытался даже работать в этой области после обучения, но как раз в этот период один друг, с которым я делал демки в детстве, намекнул, что можно снова этим заняться.
Я начал заниматься демками всё своё свободное время, и очень быстро это стало занимать больше, чем работа с детьми на улице. Это было плохо. Люди говорили: «Нужно попробовать найти работу в геймдеве, почему нет? Ты же можешь». Но я думал, что это невозможно, ведь я давно не работал с компьютерами, и с моим резюме сложно было отыскать работу в IT.
Я начал работать над опенсорсными приложениями и выпустил пару проектов, которыми стали пользоваться компании. Однажды со мной вышла на связь одна из этих компаний, они пользовалась одним из последних проектов, который называется SharpDX. Я поехал в Японию вместе с семьёй — ведь у меня уже было двое детей. Мы прожили 5 лет в Японии. В это время я работал над созданием игрового движка с нуля на C#.
Года два назад я вернулся во Францию и начал работать в Unity. Это мешало тому, чем я занимался раньше, но они предложили работу над очень сложной и интересной задачей, настоящим испытанием: сделать нативный компилятор, генерирующий нативный код из IL .NET-кода. Это было именно то, чем я всегда хотел заниматься, но не мог, потому что мне бы за такое не заплатили. И вот появился шанс, прекрасная возможность. Я проработал над этим проектом 2 года.
Да, похоже, рассказ получился не очень коротким.
E.: Ничего, такая карьера стоит долгого рассказа. Из-за вашего опыта хочется спросить вот что. Сейчас одни люди говорят «Закон Мура больше не работает, компьютеры не становятся быстрее, мы все обречены». Другие отвечают: «Да ладно, хотя они не ускоряются прежними темпами, рост по-прежнему есть, поэтому никаких поводов для паники нет». Поскольку вам близка тема производительности, и при этом вы следите за индустрией давно, какая позиция у вас?
Я придерживаюсь в этом вопросе золотой середины. (смеется) Я верю, что многие, если не большинство приложений, которые мы разрабатываем, адаптируются к требованиям производительности с самого начала, в результате обеспечивая наилучшее качество.
Посмотрите, что происходило в IT-индустрии на наших глазах. Например, когда Windows на протяжении нескольких лет становилась немного медленнее — Windows Vista и т.п. По сути велась естественная работа по улучшению перформанса, ведь годами об этом особо не беспокоились. Когда вышла Windows 8, она была уже немного лучше. Потом вышла Windows 10 и она стала ещё немного лучше. В результате, у нас есть система, которая работает весьма неплохо по сравнению с тем, что было раньше. Действительно важно для них было сделать эти оптимизации, ведь однажды люди обязательно стали бы «жить не по средствам» и начали бы говорить: «О! Этот софт больше не работает, мы переходим на Linux, потому, что он быстрее и меньше тупит».
То же самое можно сказать про весь софт, который мы разрабатываем. И вот что удивительно: всегда была тенденция работать с нативным кодом, в какой-то момент даже в Windows решили вернуться к C++, они говорили: «C++ — это решение, .NET очень медленный, тут Garbage Collector и бла-бла-бла…». И вот снова стали актуальны нативные языки.
В то же самое время V8 в Chrome вернул в обиход JavaScript благодаря JIT. JS — скриптовый язык, не супербыстрый, но иногда он отрабатывает в два раза быстрее C++. Этого было достаточно, чтобы он выжил и чтобы мы использовали его прямо сейчас для вещей вроде написания кода в Visual Studio Code. Но если присмотреться, это всё потому, что требования по производительности закладывались туда с самого начала. Даже в VSCode, даже несмотря на то, как много там JavaScript и скриптового кода вообще, всё остальное — V8, стек рендеринга, JIT — всё это написано на языке, предназначенном для максимальной производительности, то есть на C++. Всё могло быть написано на другом языке, не обязательно C++, но факт в том, что весь этот софт развивался с учётом перформанса с самого начала.
Так что да, мы можем использовать менее эффетивные и производительные языки, но только потому, что всё нижележащие технологии разрабатываются с целью получить фантастический user experience. Например, Visual Studio Code — изумительное программное обеспечение, очень хорошо работающее для разработчиков, решающее их задачи. Многие говорят: «Хотя нам нравится пользоваться более нативными редакторами кода, прямо сейчас мы переходим на Visual Studio Code» — потому что они считают его вполне эффективным. Перформанс повсюду, но иногда мы не видим его, потому что он уже заложен во всё, что мы используем.
Мы думаем: это написано на JavaScript, потому что он достаточно быстрый. Но JavaScript такой быстрый только потому, что сотни сотен инженеров-разработчиков годами работали, чтобы оптимизировать JIT. Сейчас мы можем пользоваться скриптовыми языками даже для написания очень сложных приложений. Скриптовыми языками, которые без всей этой подготовительной работы оказались бы гораздо медленнее. Мы живём в странное время. У нас есть выбор, но всё равно есть эта история с перформансом, которая повторяется для каждого языка снова и снова.
Так что .NET — типичный пример. За последние три-четыре года там проведена громадная работа. Если вы посмотрите когда-нибудь на ASP.NET Core, если вы посмотрите на всю работу, проделанную с CoreCLR… Перформанс — это хорошо продаваемая штука, он стоит денег и позволяет достигать большего. Пытаясь уложиться в жесткие требования, можно попробовать стать более производительным, можно сэкономить мощности, сэкономить немного денег в конце месяца — перформанс влияет на всё. Когда я слышу, как люди говорят: «Всё в порядке, я разрабатываю своё приложение, у него средняя производительность, сойдёт…», о чём они думают? Нужно уделять немного времени, чтобы проверять, можешь ли ты сделать своё приложение чуть более производительным. Если ты сможешь сэкономить ресурсы или время работы приложения хотя бы на десятую долю, это уже хорошо.
E.: Есть отчасти философский вопрос. Вы считаете Slack не лучшим местом для технических решений, а на своём сайте предлагаете подписываться на олдскульный RSS. Считаете ли вы, что наступившая эпоха мгновенного обмена сообщениями делает разработчиков менее продуктивными?
Нет, я так не думаю. Сейчас я работаю удалённо. На работе, в Unity, мы можем работать удалённо, поэтому я постоянно использую Slack для общения с коллегами. Это лучший способ для меня и быть на связи, и оставаться продуктивным. Это отнимает много времени от работы, потому что нужно проверять каналы и так далее, но я могу временно выключить Slack и сфокусироваться на работе. Пока я работал в компании в опенспейсе, у меня не было выбора: если кто-то хочет задать вопрос, приходится сразу отвечать, это куда сложней.
Что касается Twitter и электронной почты, я не проверяю их так часто. Один-два раза в день я читаю Twitter, это зависит от разных факторов: участвую ли я в каких-то обсуждениях и что обсуждаю. Если используешь что-то типа Slack, можно присоединяться к разным каналам в компании, можно следить за многими темами, за которыми ты бы не мог следить, если бы работал один. Нужно найти золотую середину: все мы беспокоимся о множестве вещей, которые происходят в компании, но нужно быть избирательным, ведь нельзя участвовать во всех обсуждениях одновременно. Некоторые люди могут читать так много каналов, что я просто поражаюсь их способностям, сам я не такой. На сегодняшний день я читаю около 30 каналов, это не так много.
E.: Спасибо, время для вопросов Олега!
О.: Моя карьера чем-то похожа на вашу: я работал в банке, сейчас вообще в другой сфере — в организации конференций, и параллельно пытаюсь разобраться в построении компиляторов. Что вы можете посоветовать тем, кто из простых enterprise web-разработчиков пытается перейти в системное программирование, есть ли какие-то советы по такому переходу? Уверен, что нас таких тут если не много, до достаточно.
Не уверен, что существует заготовленный путь для такого перехода. Если ты интересуешься такими технологиями, то делаешь некую обычную домашку. Дома ты пишешь парсеры и штуки, связанные с компиляторами. Не обязательно писать полностью компилятор от начала до конца, до самой генерации машинного кода. Ты начинаешь интересоваться написанием компиляторной инфраструктуры. Это то, что я делаю последние годы, работая в Unity. Если ты увлечён низкоуровневыми вещами, то это одно из тех мест, где можно понять, как это всё устроено на самом деле. Как можно улучшить работу, где стоит улучшить перформанс, и где этого ещё не сделали. Если ты беспокоишься о перформансе, то очень важно быть в курсе, на чём будет выполняться приложение в конце концов.
Перформанс — это моя тема, и всё это стало для меня отличной возможностью. Хочется подходить к решению проблемы в её основе, то есть на уровне компилятора. Именно здесь мы можем в десятки раз увеличивать производительность в тех местах, где это нужно для наших пользователей. Если мы запускаем игры, приложения, фильмы или что-то в этом роде, иногда можно сравнительно легко достичь таких результатов.
Увлечённость низкоуровневыми штуками и составными частями компилятора привели меня к текущей работе. Но это не было чем-то, чем я специально хотел заниматься. Иногда, когда ты получаешь много опыта с разными языками, пишешь приложения — появляется желание даже придумать свой собственный язык. Я начинал этим заниматься, но прекратил, потому что это слишком много работы, а у меня очень мало свободного времени. Но у тебя появляется подсознательное желание вернуться «к корням», попытаться сделать что-то самому, чтобы всё понять. Конечно, я понимал, как работают компиляторы и всё такое, но не понимал сложности требований. Сложных трейдоффов, с которыми придется разбираться, например, в области управления памятью. Очень сложно выбрать то, что одновременно даст и большую продуктивность работы прикладного разработчика и будет эффективным. Эта проблема всё ещё не решена до конца. Rust или .NET никогда не решат этого. Rust — прекрасный, изумительный, но он сложен в работе, особенно если ты переходишь на него с чего-то типа JavaScript. Тем не менее, имеются примеры разработчиков на Python или JavaScript, которые переходят на Rust, хотя это и несколько удивительно.
О.: Вы упомянули, что программировали на C# последние лет 10. Так чего хорошего в C#? Почему не C++, например? C++ кажется более системным языком.
Если честно, я ненавижу C++, ненавижу C, но работаю с ними. Я искренне верю, что они приводят к куче багов, к огромной неэффективности разработки. Многие думают, что раз ты программируешь на C, то ты уже де-факто пишешь быстрый код, что твоя программа будет перформанс-ориентированной. Это неправда. Если ты лепишь кучи malloc и всякого такого, оно будет медленным даже по сравнению с тем, что написано на .NET. Хорошие разработчики на C/C++ вынуждены использовать ухищрения вроде region memory allocator. Ты должен зарываться в кучу странных вещей, о которых никто не слышал. Хотя вот разработчики игр обычно о таких вещах знают. Как минимум, разработчики AAA или люди, которые делают игры на C/C++-фреймворках. Часть проблем происходит от сложности самого языка. Раньше я вообще не читал книг по C++ и только три-четыре года назад начал читать книги только по C++, чтобы просто прочувствовать язык. Я программировал на нём, но у меня не было систематического, формального подхода, и меня поразила его сложность, количество вещей, которые ты можешь испортить, если не напишешь всё правильно.
Не далее, как пару месяцев назад в Unity был баг, кто-то допустил ошибку в куске кода на C++, это было в конструкторе, что-то передавалось по значению и в итоге мы брали от этого значения адрес и искали в кэше. Фактически мы ссылались на значение, которого уже не было в памяти. И всё это потому, что там перепутали указатели с неуказателями, и тот, кто сделал этот рефакторинг, не проверил все места использования. Совершенно другой код, который отлично работал, внезапно работать перестал. Вроде бы, ошибка маленькая, но она поломала всё целиком. По сути, это ошибка в работе с памятью. Так что да, когда я вижу подобные вещи, то убеждаюсь, что мы должны ограничить доступ к работе на C и C++, максимально сократить их использование. В .NET-части я действительно ограничил их применение только платформозависимыми вещами. Но писать всё на C# довольно муторно. Для того, чтобы получать доступ к API, нужно делать кучу dlopen. Хотя, например, можно попробовать инкапсулировать всё это в обёртку на C и организовать доступ через всего одну функцию. Такие вещи я предпочёл бы изолировать и разрабатывать их дальше на C и C++. Но это такая узкая тема про интероп, а дальше ты остаёшься с нормальным управляемым языком, используешь его большую часть времени, наслаждаешься более быстрой компиляцией.
Я ненавижу ошибки компилятора C++ и линкера, ненавижу необходимость работать с разными платформами, всё это очень-очень сложно. Ты начинаешь компилировать на MSVC, потом должен переключиться на Clang, потом на GCC. На Linux, на Mac, на Windows, на Android, на iOS и так далее и тому подобное. Работа с C++ — это кошмар!
Я ненавижу разделение между файлами в редакторе, .h-файлами и cpp.-файлами. Люди окончательно запутываются в языке и начинают программировать на макросах. Я люблю метапрограммирование, но в современном C++ мы можем делать просто полное безумие. Сами по себе эти штуки изумляют, но вообще-то это уже слишком.
Подводя итог: да, думаю, мы можем разрабатывать эффективный софт на C#. Может быть, не такой быстрый, как на C++, но можем. Именно это мы пытаемся делать в Unity — например, мы делаем bust compiler, чтобы компилировать особое подмножество C#, добиваясь максимального перформанса, местами даже большего, чем это получилось бы на C++ — но оставаясь в C#. Это вполне безопасно. Для указателей нужно объявлять unsafe, не генерить исключений, делать всё эксплицитно. И это горький опыт. Но всё-таки ты можешь написать код, который будет так же быстр, как на C++. Думаю, это как раз то направление, в которое стоит идти .NET и куда должны идти мы.
О.: Если говорить об опенсорсном коде, например, у нас есть сборщик мусора в .NET Core, который представляет из себя очень большой и страшный C-файл. Два мегабайта мусора, скорее всего, сгенерированного из какого-нибудь лиспа (вряд ли столько букв стоило писать вручную). Возможно, имеет смысл переписать здесь всё на C#?
Да! Я общаюсь с людьми, которые работают над JIT и в Microsoft, и в сообществе. Есть кое-что, во что я искренне верю. Я верю, что существует момент, когда твой язык становится более зрелым и основополагающим, и тогда ты должен бросить вызов, проверить его на прочность. Тебе нужно уметь использовать его в качестве фундамента. Доказать, что можешь применить его даже для создания чего-то очень требовательного к перформансу. И это история про Garbage Collector и JIT. Очень-очень большой процент подсистем рантайма .NET, в том числе JIT и GC, может быть сделан на C#. Если взять за правило, что на C++ можно описывать только абстракции базовой платформы, это сделает большую часть рантайма платформонезависимой. Я бы очень порадовался, если бы это произошло. Но это огромная работа.
Есть одна причина, почему мне эта идея особенно нравится. Я уже говорил об этом, рефакторинг и улучшение кодобазы на C/C++ настолько сложны, что в какой-то момент ты перестаёшь этим рефакторингом заниматься. Это настолько больно, что ты просто к этому больше не притронешься. Придётся переносить и менять какие-то файлы руками, ибо рефакторинги в IDE будут работать плохо, например, потому что слишком много шаблонов — и так далее и тому подобное. Разрабатывая на C#, можно было бы быть амбициознее насчёт своих желаний. Добавлять новые оптимизации оказалось бы возможным гораздо быстрее и проще, просто потому, что время компиляции уменьшилось. Уменьшилось бы время итераций для тестирования и так далее. Приятно, что, например, в CoreRT стараются максимально использовать C# вместо C++. Ситуация меняется.
Но мы всё ещё на полпути от того, чтобы переписать .NET GC на C#. Но мы могли бы. Например, я знаю, что могу переписать .NET GC, причём переписать по-другому. Несколько лет назад я сильно заинтересовался GC, читал книжки про это, написал что-то вроде прототипа реализации GC. Я был поражён работой Java-комьюнити над Jikes RVM — они проделали эту работу на Java. Позже я обнаружил, что в Golang компилятор вначале был написан на C, а потом на Golang. Когда стал читать исходный код Golang-компилятора, был удивлён организацией кодобазы и тем, как структурированы оптимизации. Например, есть огромный файл, где описываются разрешённые оптимизации, которые можно применять к некоторым инструкциям. Инструкции могут мапиться в более быстрые нативные инструкции, и это всё описано в огромном текстовом файле. Я не видел такого ни в LLVM, ни в .NET JIT.
Подводя итоги, да, использование управляемого языка должно дать нам больше возможностей для написания более качественного рантайма.
О.: Вы рассказали о сложности кода, опасности ошибок и так далее. Для меня наиболее сложная часть — читать исходники компилятора и понимать их. Например, вот есть очень большой файл с десятками тысяч строк AST-трансформаций и промежуточных представлений. Вот этот файл из Golang с ловерингом, например. Что вы думаете о том, чтобы писать хороший понятный код для системных частей? Как его написать?
Ха, вы говорите, как будто бы для любой вещи есть заранее заготовленные правила! Компиляторы более требовательны к коду, чем обычные приложения. Первое: нужно быть предельно внимательным при проектировании базовой архитектуры. Нужно внимательно следить за переиспользованием и изоляцией: если ты изменяешь что-то, то в других местах ничего не должно посыпаться и испортить весь остальной код.
Второе: я убеждён, что мы обязаны сопровождать код большим количеством комментариев, чтобы объяснять внутренние зависимости, которые не очевидны, когда просто смотришь на код. Мне очень не нравится, когда говорят: «Код должен сам описывать себя! Он должен быть очевиден!». Это совершенно неверно и вводит в заблуждение. Инженеры любят рассказывать такие сказки молодым разработчикам, и так делать не стоит.
Когда я вижу кодобазу вроде LLVM, например — она может быть несовершенна, но там полно комментариев. Например, когда есть структура в заголовочном файле, комментарии могут объяснить, где эта структура объявляется, почему она так объявляется, почему здесь такие поля. Для всех этих вещей есть причина. Если ты не объяснишь эту причину, никто и не поймёт, почему ты сделал это именно так. Если ты просто прочитаешь код, ничего понятно не станет. Например, выравнивая данные для эффективного кэширования, если ты не объяснишь это в комментарии, как другие поймут, что всё это делалось ради эффективности? Возможно, впоследствии даже тебе самому придётся перечитать весь код, восстановить всю структуру приложения и раскладку данных в памяти, и тогда придёт озарение. Но если ты не добавишь комментарии в код и не объяснишь этого, то кто-то другой посмотрит на всё это и скажет: «Вау, тут прибитое гвоздями поле, это не очень реюзабельно, поставлю-ка я здесь другой класс и добавлю указатель» — и тем самым поломает вообще всё, потому, что не понял, почему здесь было сделано именно так.
И я говорю так не только о хорошей, но и о плохой работе. Не раз мне приходилось вставлять комментарий в код, чтобы сказать, что вот эту часть я сделал не очень хорошо, не очень горжусь этим, это место нужно исправлять, пока что мы его оставим в текущем виде, но когда-нибудь нужно будет к нему вернуться и починить. Объяснял причину, почему это сделано так: например, не хватило времени, чтобы сделать хорошо. Ты задокументировал, что сделал лишь небольшую часть необходимого, и это нормально. Ты применил вот такой костыль, и если бы попытался сделать более правильно, то сломал бы всё. Но ты должен это объяснить даже для тех участков кода, которые работают не очень хорошо. Итак, комментарии — это второе.
И третье для меня — тестирование. Нужно делать больше юнит-тестов. Не больших интеграционных тестов, а именно мелких юнит-тестов, проверяя части своего API, начиная с нижних уровней. Так твое приложение будет совершенствоваться со временем. Например, я написал SharpDX вообще без тестов. И в момент написания он был неплох. Но дело в том, что я создавал его не для людей, а для себя, в своё свободное время. Я хотел иметь доступ к DirectX C++ API, которое уже было доступно на C++. На протяжении многих лет я проверял, что всё работает. Но каждый раз, когда я вносил какие-либо изменения, приходилось проверять функциональность заново. Последние несколько лет я переключился на другие проекты: времени нет, да и не пользуюсь я им больше. А потом пришел один разработчик из опенсорсного сообщества, выделил компилятор в отдельный пакет и попробовал на этом сделать нечто отдельное от SharpDX. Штука в том, что я полностью не проверил этот PR, поскольку мы не сделали ни одного теста. Просто замерджил его pull request (он казался идеальным). Он начал делать мини-тесты на своём репозитории на своём отдельном приложении. Но мы кое-чего не учли, и сам SharpDX оказался совершенно сломан в какой-то из версий. Тут же нарисовались люди с проблемами вида: «А чего это вот этот метод вызывает исключение?» На последующих проектах (и опенсорсных, и рабочих) я старался быть очень осознанным в плане тестирования, периодически даже пытаясь увеличить покрытие. Но попытка достичь 90-процентного покрытия — это очень муторно. Иногда реальные тесты получаются слишком странными, ты написал их просто для отладки и не можешь внести их под покрытие, не хочешь тащить такое в свой код.
В общем, да, я считаю, что это вот эти три вещи, к которым нужно подходить очень внимательно: архитектура, комментарии, тестирование.
О.: Компиляторы — это вполне старая сфера, да? История начинается в середине прошлого века. Как ты думаешь, какие самые главные челленджи сейчас есть для современных разработчиков и компаний, создающих компиляторы?
Я думаю, что сегодня главный челлендж — создать компилятор, который будет эффективен в работе с SIMD. И это действительно вызов, ответить на который компиляторы немного затрудняются, потому что SIMD и подобные оптимизации обычно добавляются позже, не обязательно с самого начала создания компилятора. (В случае с LLVM, наверное, они были с самого начала). Дело в том, что это приносит множество новых проблем оптимизатору, он должен уметь выполнять их корректно. Как можно заметить, компиляторы сейчас затрудняются, например, автоматически векторизовывать код. В каком-то виде они могут это делать, но иногда это проще сделать вручную. Компиляторы неспособны определять все места для оптимизации, и в результате генерится неэффективный код. Это сложная тема. Кое-кто из Intel работает над добавлением технологии векторизации в LLVM. Проект разрабатывается уже несколько лет, и в целом это только подготовка, чтобы внести эти изменения в апстрим LLVM. Их ещё пока там нет, это очень сложно, и потребуются годы. LLVM — это очень хорошая система, хотя бы потому, в ней есть вещи, отсутствующие в .NET. Использование преимуществ SIMD, CPU и разных ядер — вот это будет наибольшим современным челленджем. Вот почему, например, в .NET языки добавляют векторные интринсики — можно использовать векторизатор и SIMD-инструкции эксплицитно. Но во многих случаях, вроде циклов, можно было бы ожидать и автовекторизации.
Раньше я бы сказал: «Нам нужны компиляторы как сервисы и компиляторы как библиотеки». Сегодня это больше не проблема. Люди убедились в полезности этих идей. Поэтому LLVM и стала такой известной штукой: она разрабатывалась с самого начала как компилятор-библиотека, в которой можно пользоваться любым языком, каким захочется. То же самое, например, с Roslyn на C#, там тоже есть компилятор. Конечно, это компилятор не того же типа, но тем не менее его можно использовать в собственном коде. Думаю, сейчас все уже в курсе этих штук, люди осознают, что компилятор приносит пользу. Так что для меня совместимый с SIMD код — более важная штука. Программировать с прицелом на GPU, CPU, на количество используемых ядер — это только начало. У нас есть 6, 8, 16, 42 ядра. В какой-то момент начнётся их увеличение, мы должны быть готовы к этому, чтобы использовать всю эту мощь максимально эффективно.
О.: У вас есть своя собственная реализация Markdown под названием Markdig, да? Не могли бы вы совсем немного объяснить, каков статус Markdown? Есть ли у него формальная определённая грамматика, как у него дела?
Да, для меня Markdown — это такой способ написания документации, который нравится разработчикам. Скорее всего, это не для простых обывателей, которым всё ещё проще использовать Word или что-то такое. Но для разработчиков он хорош. Разработчики на протяжении многих лет пользовались чем? Текстовыми файлами, разными способами размечали в них заголовки, и так далее. И это было нормально. Ты знаешь, это как читать RFC в интернете — есть куча текстовых форматов файлов, очень ограниченных, очень хорошо сделанных, но не формализованных. Их не всегда было просто читать, там не было картинок, и это было сумасшествие. Но у нас не было выбора, и приходилось использовать даже ASCII-art. А потом появился Word, PDF и тому подобное. В двухтысячном году куча документации была в Word. Разработчиков было не очень-то просто уговорить работать с такими документами — это было не модно. Было неудобно смотреть изменения в документации, когда вносишь правки в связанный код и наоборот. Когда появился Markdown, это было изумительно, стало возможным производить из него что-то очень приятное — например, HTML. И это всё ещё текстовый формат, который легко читать и добавлять в свою кодобазу. Даже если у тебя не сгенерировано HTML, Markdown можно удобно читать в текстовом редакторе. Сейчас, когда ты открываешь проект на Github, он определяет Markdown-файлы и самостоятельно красиво их отображает. И всё это лежит рядом с твоим кодом. Документация среди кода — лучшая форма технической документации. Например, в Microsoft перевели всю техническую документацию на Markdown, и теперь документацию можно читать прямо на GitHub, они принимают PR — вот такой прекрасный способ дать доступ к документации и организовать процесс внесения правок, воспользоваться которым может каждый.
В плане нормализации Markdown, есть проект под названием CommonMark, который запущен несколько лет назад, целью которого является стандартизация Markdown. Думаю, что сегодня CommonMark — это и есть стандарт. Люди начинают уважать его, следовать ему, но есть и множество других реализаций. Например, Github перешел на CommonMark в прошлом году. Никто этого не заметил — и это хорошо. Но они смогли перейти на него. И Microsoft тоже перевёл документацию на CommonMark, потому что они использовали мой Markdig. Они начали работать над тем, чтобы использовать Markdig много лет назад, а до этого они взяли самописный Markdown-движок, использующий внутри регулярки. Это неплохо, но, насколько знаю, они не очень следовали в этом движке спецификации CommonMark, поэтому в результате перешли на Markdig. Они связались со мной, и это было круто. Я написал Markdig для того, чтобы им пользовались, и хотел использовать в своём собственном проекте, но потом надолго отложил его в сторону. Очень рад, что такая компания как Microsoft решила его подхватить.
О.: Как думаете, как быть хорошим программистом сегодня? Что хороший современный программист должен знать в отличие от программистов прошлого века?
Это применимо не только к программированию, но и к любой сфере. Главное — делать свою домашку, изучать вещи самостоятельно. Важно задавать вопросы. Не обязательно принимать язык программирования таким, какой он есть. Не то чтобы надо его менять, но важно понимать, почему разные вещи работают так, как они работают. Ты должен быть любознателен и открыт к изучению причин, должен задавать хорошие вопросы. «Я не понимаю, как это устроено, но я хочу понять это». Что бы ты сделал, чтобы улучшить язык? Чем больше вопросов ты задаёшь языку, которым пользуешься, тем больше у тебя будет возможностей помогать, улучшать его, узнавать новые вещи и открывать, почему что-то в программном обеспечении сделано определенным образом. Так ты сможешь узнать гораздо больше, чем просто вбивая в Google «я хочу сделать X» и находить на гитхабе готовые проекты из одной функции «сделать хорошо». Когда мы пишем реальное приложение, слишком сложно было бы вдаваться в детали каждой использованной зависимости — это была бы огромная работа по ковырянию в деталях. Но в той части, которая тебя заботит, которая интересна для тебя лично, стоит глубже погружаться и задавать вопросы. Как это работает, можно ли это сделать быстрее, почему какая-то вещь работает не очень хорошо — ты обязательно должен понять, почему. «Может быть, я могу написать что-то лучшее?»
Многие проекты, с которыми я работал, по большей части были законченными. Иногда хозяева проекта не хотели его менять. Это грустно, ведь ожидаешь, что в опенсорсе все друг с другом будут сотрудничать. Реальность опенсорса такова, что иногда тебе нужно заняться чем-то другим. Иногда то, что ты предлагаешь, хорошо, но ведёт к слишком большим изменениям. Людям бывает тяжело принимать такие предложения — возможно, это принесёт только больше багов и больше поддержки, и кто будет всё это поддерживать? Тот, кто пришёл с этим предложением? Наверное, нет.
Иногда я тоже забываю о том, что нужно задавать разные вопросы. Простые вопросы. Можем ли бы сделать это быстрее? Да ли нет? Нужно действовать вместо того, чтобы просто пользоваться и ругаться на медленно работающие вещи, и ничего не делать по этому поводу. Нужно копать, исправлять… Делать что-то дома, справляться с болью. С каждым разом ты становишься всё более готов понимать вызовы и разрабатывать более сложные программы. И, может быть, в конце концов ты будешь чуть менее разочарован реакцией других людей, потому что начнёшь лучше понимать их ограничения.
И ещё одна важная вещь. Не надо скромничать. Пробуй! Пробуй делать то, тебе хочется. Если у тебя появилась идея — попытайся воплотить её. Даже если эта идея безумная, и иногда ты можешь получить немного безумные результаты — в этом тоже что-то есть. Пока ты занимаешься такими вещами, можно встретить множество странных и сумасшедших людей, от которых можно получить крутые инсайты о том, как можно заниматься разработкой. Тебе нужно быть очень внимательным к тому, что они говорят, как они работают, что они думают. Не копируй их, потому что у тебя есть свой собственный путь и своё будущее. Это круто, когда ты можешь встретить таких людей на своем карьерном пути. У меня было несколько таких людей. Я не всегда это осознавал, но спустя какое-то время понимал: «Да ведь этот парень был крутым!». Я научился у них чему-то, но, может быть, мог бы научиться большему. Это золотая середина между тем, чтобы пытаться думать самостоятельно и брать лучшее от лучших умов вокруг себя. Они могут повлиять на тебя в хорошем смысле и могут помочь разработать более крутые вещи.
Это история про то, чтобы быть развиваться самостоятельно, задавать как можно больше вопросов, пытаться быть открытым людям, которых встречаешь, к событиям, которые можно использовать, и принимать участие в чём-то большем, чем мы сами.
Уже в следующую пятницу Александре выступит с докладом «Behind the burst compiler, converting .NET IL to highly optimized native code by using LLVM» на DotNext 2018 Moscow. Конференция пройдёт 22-23 ноября в Москве и, кажется, это будет самый большой DotNext из всех. Мы бы рассказали вам, чего ещё ждать от конференции, но за нас это лучше сделал другой её спикер, Дилан Битти — он записал целую песню:
Автор: olegchir