Pull to refresh

C# для системного программирования

Reading time 9 min
Views 33K
Original author: Joe Duffy
От переводчика. Недавно на Хабре была опубликована статья «Будущее C#», описывающая новые фичи, которые, скорее всего, попадут в C# 6.0. Мне, как программисту .NET, эта статья очень понравилась, и я решил поискать дополнительную информацию о том, куда идёт C#/.NET. И вот, как будто прислушиваясь к моим новогодним пожеланиям, 27 декабря Джо Даффи (Joe Duffy) опубликовал в своём блоге статью «C# for Systems Programming», рассказывающую об исследовательском проекте под его руководством, направленном на создание нового языка и платформы на основе C#/.NET. Приятно впечатлённый статьей, я решил опубликовать её несколько вольный перевод на Хабре.

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


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

Пожалуйста, отнеситесь к этому рисунку с долей понимания и снисхождения. Я понимаю, что безопасность (Safety) не равна продуктивности (Productivity), что безопасность можно понимать и толковать во многих разных смыслах и т.д. Тем не менее, безопасность и продуктивность часто идут рука об руку — вспомните, сколько времени и усилий разработчики, как правило, тратят на ошибки безопасности, наборы инструментов, так или иначе помогающие улучшать код, и т.д.

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

В верхнем левом квадранте мы имеем языки со сборкой мусора, которые доминируют в плане продуктивности разработчика, их использующих. За последние несколько лет производительность JavaScript существенно подросла, в чём спасибо компании Google. Недавно подобное случилось и с PHP. Совершенно ясно, что появилась целая плеяда динамически типизированных языков, которые активно соперничают с C# и Java. Таким образом, сейчас выбор сместился с вопроса производительности на вопрос предпочтения динамической или статической системы типов.

А это значит, что языки типа C# всё более и более «страдают» от Закона исключающего среднего (Law of the Excluded Middle). Нахождение посередине добром не закончится.

В нижнем правом квадранте расположились языки, которые выжимают из себя последние капли производительности. Будем честными: большинство программистов не поместит C# и Java в этот квадрант, и я с ними согласен. Я видел множество людей, которые с кислым послевкусием во рту убегали от языков со сборкой мусора обратно к C++. (Буду честен и скажу, что сама сборка мусора виновата лишь отчасти; подобное «бегство» в значительной степени обусловлено плохими шаблонами проектирования, фреймворками и упущенными возможностями сделать язык ещё лучше.) Java находится ближе к «квадранту производительности», чем C#, в первую очередь благодаря превосходной работе HotSpot-подобных виртуальных машин, которые используют травление (вытравливание) кода (code pitching) и выделение стека (stack allocation). И, тем не менее, большинство хардкорных системных программистов выбирает C++ вместо C# и Java исключительно из-за преимущества первого в производительности. И хотя C++11 немного приблизился к языкам наподобие C# и Java в плане продуктивности и безопасности, он всё ещё показывает явное «нежелание» добавить гарантированную безопасность типов к C++. Да, сейчас у вас уже намного меньше точек пересечения с небезопасностью, но я твёрдо верю, что, как и с беременностью, нельзя быть «наполовину безопасным». Присутствие в языке небезопасности означает, что вы должны всегда рассчитывать на худший из возможных сценариев и использовать инструменты для восстановления безопасности уже по факту её нарушения, вместо того, чтобы положиться на безопасность системы типов.

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

Результат должен быть выражен в наборе дополнений (extensions) к языку C#, затрагивающих сам язык в минимальной степени, а не в совершенно новом языке.

Следующий вопрос: «Зачем базироваться на C#?» В том языке, который мы хотим разработать, безопасность типов является «железобетонным» аспектом, а C# предоставляет чертовски хорошее полотно в стиле «современный типобезопасный C++», на котором мы начнём рисовать свою картину. C# ближе к тому, чего мы хотим, по сравнению с, скажем, Java, так как содержит такие современные вещи, как делегаты и лямбда-выражения. Теперь есть и другие кандидаты, такие как D, Rust и Go. Но когда мы начинали свою работу, эти языки ещё не появились или не стали достаточно полноценными для использования. К тому же, моя команда работает в Microsoft, где куча талантливых C#-разработчиков доступна на расстоянии вытянутой руки. Я готов сотрудничать с экспертами в других рассматриваемых языках, перечисленных выше, и даже поделился идеями с несколькими ключевыми людьми из сообществ этих языков. Хорошей новостью является то, что все рассматриваемые нами языки имеют общие корни, заложенные в C, C++, Хаскелле и т.д.

Наконец, вы можете поинтересоваться, «Почему бы не основываться на C++?». Должен признать, что в процессе работы я часто задавался вопросом, а не должны ли мы начать с C++ и вырезать из него «безопасное подмножество» функциональности. Мы часто обнаруживали, что занимаемся «тасованием C# и C++ в блендере, пытаясь увидеть, что же получится», и я признаю, что порой C# тянул нас назад. В частности, когда вы начинаете думать о RAII, детерминистических деструкторах, ссылках и т.д. Тонкости между обобщениями (generics) и шаблонами (templates) заслуживают отдельного поста. Я действительно ожидаю рано или поздно рассмотреть переход на C++ по двум причинам: (1) такой шаг приведёт к увеличению количества пользователей языка (в мире живёт намного больше программистов, знающих C++, нежели знающих C#) и (2) я мечтаю о стандартизации, чтобы open source-сообществу также не требовался выбор между «безопасность и продуктивность» и «производительность». Но в рамках изначальных целей проекта я рад использовать C#, и не по последней причине благодаря богатой функциональности .NET Framework.

На протяжении последний нескольких лет я уже несколько раз давал намёки на наш проект (например, здесь и здесь). В ближайшие месяцы я начну делиться ещё более подробной информацией. Моей конечной целью является передача результатов нашего проекта в open source, но до этого нам необходимо привести в порядок некоторые аспекты языка, а также, что более важно, перейти на использование кодовой базы проекта Roslyn. Надеюсь, что эта цель будет достигнута в 2014 году.

На высоком уровне я классифицирую функционал создаваемого языка по шести основным категориям.

  1. Понимание жизненного цикла (Lifetime understanding). C++ содержит RAII, детерминистические деструкторы и эффективную аллокацию объектов. C# и Java стимулируют разработчиков во всём полагаться на управляемую сборщиком мусора кучу, и поддерживают только нечёткую/слабую (loose) поддержку детерминистического уничтожения объектов через IDisposable. Часть моей команды постоянно конвертирует программы, написанные на C#, на наш новый язык, и для нас не редкость, что 30-50% времени выполнения программы «отъедает» сборщик мусора. Для серверных приложений этот факт «убивает» пропускную способность, для клиентов — приводит к деградации ощущений использования, так как снижает отзывчивость интерфейса во время его использования. Можно сказать, что мы «украли» часть возможностей C++, а именно rvalue-ссылки (rvalue references), семантику переноса (move semantics), деструкторы, ссылки и заимствования (references / borrowing). Наряду с этим, мы сохранили безопасность языка, а также соединили идеи, взятые от C++, с идеями функционального программирования. Всё это позволяет нам применять агрессивную аллокацию объектов на стеке, детерминированно уничтожать их и многое другое.
  2. Понимание побочных эффектов (Side-effects understanding). Этот пункт является эволюцией тех идей, которые мы опубликовали на OOPSLA 2012. Он подразумевает введение в язык части функционала оператора const из C++ (снова-таки, безопасным образом), а также неизменяемость (immutability) и изолированность первого класса.
  3. Асинхронное программирование в масштабе (Async programming at scale). Сообщество постоянно вращается вокруг этого вопроса, определяясь, использовать ли передачу продолжений (continuation-passing) или облегчённые блокирующие сопрограммы (lightweight blocking coroutines). Этот вопрос затрагивает не только C#, но и в значительной степени все существующие языки. Ключевой инновацией здесь является компонуемая система типов, которая «агностична» (безразлична) по отношению к модели выполнения, и может эффективно взаимодействовать с любой из них. Было бы самонадеянно утверждать, что мы имеем только один подход к решению данной проблемы, но, опробовав множество других подходов, могу сказать, что мне нравится тот, что мы выбрали.
  4. Программирование типобезопасных систем (Type-safe systems programming). Часто можно услышать утверждения, что система типов и потеря производительности идут рука об руку. Несомненно, что проверка границ требует времени, а также то, что мы предпочитаем, чтобы проверка переполнений была включена по умолчанию. Но удивительно, как много можно достичь при помощи хорошего оптимизирующего компилятора в противовес JIT-компиляции. Мы также позволим вам делать больше вещей без аллокаций, типа основанные на лямбдах API, вызов которых не сопровождается никакими аллокациями (сейчас, как правило, требуются две аллокации: одна для делегата и вторая для отображения). Ещё одна «фича» — возможность «вырезать» подмассивы и подстроки без аллокаций.
  5. Современная модель ошибок (Modern error model). Этот пункт — ещё один, с которым сообщество несогласно. Мы остановились на том, что я рассматриваю как лучший из возможных вариантов: всевозможное и полное задействование контрактов (предусловия, постусловия, инварианты, утверждения и т.д.), быстрая обработка сбоев как базовая установка среды, исключения только для редких динамических сбоев (парсинг, ввод-вывод, прочее), и типизированные исключения только при абсолютной необходимости в расширенной информации об исключении. Всё это интегрировано в систему типов как объекты первого класса, так что вы получите всё необходимое в целостности и сохранности.
  6. Современные фреймворки (Modern frameworks). Сюда входит целая куча вещей, включая асинхронные LINQ-выражения и улучшенная поддержка перечислителей (enumerator), которые будут конкурировать с итераторами из C++ в плане производительности и не будут требовать двойную диспетчеризацию интерфейсов (double-interface dispatch) для извлечения элементов. Если быть честным, то в этой области мы имеем наибольший список вещей со статусом «спроектировано, не ещё не реализовано». Сюда также попадают: тип void как объект первого класса, non-null-типы, типажи (traits), 1st class effect typing и прочее. Я ожидаю, что мы успеем к середине 2014-го реализовать небольшую часть из этого списка.

Я полагаю, что наш проект вас заинтересует, и я хочу услышать, что вы думаете по этому поводу, каковы ваши мнения как касательно проекта в общем, так и относительно его составляющих, а также хочу выяснить, какие его аспекты вас заинтересовали больше всего. Я крайне рад делиться с вами информацией, однако реалии таковы, что в меня нет кучи времени для ведения блога, да и работы в нас выше крыши (кстати, we’re hiring). Но я обязательно учту ваше мнение относительно того, о чём мне рассказывать и в каком порядке. В конце концов, я с нетерпением жду того дня, когда смогу поделиться с вами реальным кодом, а не текстами. А пока что желаю вам Happy Hacking!

Обновление поста


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

Как, надеюсь, ясно из моей биографии, язык, который описан в этом посте, является исследовательским проектом, ни больше, ни меньше. Считайте меня парнем из Microsoft Research, который опубликовал свой доклад в блоге, а не на PLDI. Я просто не настолько талантлив, чтобы выступать на PLDI.

Я очень надеюсь, что в ближайшие месяцы я буду писать что-то новое о проекте, но только в духе открытого сотрудничества с сообществом, а не ради копания в «глубоком смысле» или в «высоких материях». Не нужно столько домыслов!

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


Послесловие от переводчика


Несмотря на относительно небольшой размер, данная статья была довольно сложной для перевода. Многие термины просто не имеют адекватного перевода, многие технические вещи не до конца понятны. Я попытался снабдить гиперссылками все термины, пояснение которых смог найти (или полагаю, что правильно нашел). Тем не менее, я уверен, что в статье содержится много неточностей/ошибок/искажений перевода, как «классического», так и технического характера. Поэтому буду крайне благодарен любым вашим замечаниям/комментариям/предложениям, выраженным где вам удобно и как вам удобно.

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

Также рекомендую ознакомиться с переводом интервью с Джо Даффи «10 вопросов о параллельном программировании и потоках в .NET», опубликованным на RSDN. Хотя интервью и датируется 2007-м годом, полагаю, что ответы Даффи нисколько не устарели.

Также существует ещё одно, не менее интересное интервью с Даффи «Joe Duffy on Uniqueness and Reference Immutability for Safe Parallelism», датируемое апрелем 2013 года и опубликованное ресурсом InfoQ. Помимо вопросов параллелизма, Даффи также кратко упоминает проект, описанный в данном посте. Я могу выполнить его перевод, однако, исходя из размера и сложности этого интервью, мне не хочется делать перевод, не зная, заинтересовано ли в этом сообщество Хабра. Мне не хочется повторения ситуации с моими последними переводами статей Джона Скита, которые были интересны лишь нескольким людям. Поэтому жду вашего мнения.
Tags:
Hubs:
+23
Comments 19
Comments Comments 19

Articles