Выражаю благодарность уважаемому Rulexec, указавшему на вторую часть статьи о языке C (первая находится здесь), вернее — ответы господина Катца на реакцию читателей. Считаю, что эта статья также достойна перевода и вашего внимания. Простим автору небольшой автопанегирик в конце статьи
и послушаем его разъяснения. Возможно, дискуссия получит новый импульс
Мой пост «Непостижимая эффективность языка C» породил оживленные дискуссии на Reddit и Hacker News, собрал почти 1200 комментариев, в которых читатели вступают во всевозможные жаркие споры. Кроме того, я получил ряд личных писем, касающихся этого поста. Поэтому я решил ответить на некоторые распространенные вопросы, прокомментировать полученные отзывы и устранить возникшие недоразумения.
Является ли C оптимальным языком для решения любых задач?
Да нет же! Более высокоуровневые языки, например Python и Ruby, исключительно полезны и, несомненно, должны активно применяться в определенных областях. У Java есть множество достоинств, у C++ — также. Erlang очень интересен. Практически для любого популярного языка можно найти область, в которой этот язык наиболее удобен.
Но когда нас интересует в первую очередь производительность и надежность, именно язык C оказывается практически идеальным вариантом. В Couchbase нам требуется надежность «в промышленных масштабах», но без снижения производительности.
Мне нравится возиться с Erlang. Это очень надежный и предсказуемый язык, весь его дизайн заточен под стабильное функционирование, даже при отказах аппаратного обеспечения. Тот факт, что мы столкнулись с критической проблемой прямо в ядре Erlang, ничуть не умаляет в целом превосходную репутацию этого языка.
Но Erlang недостаточно быстр, чтобы удовлетворять нужды наших клиентов. Это основная проблема. Мы приложили огромные усилия, чтобы сделать наш код на C максимально эффективным и быстрым, и теперь он оправдывает себя на тысячах серверных установок Couchbase по всему миру, экономя нам массу времени и ресурсов. Наш код — это вложение, которое уже многократно окупилось.
Но в большинстве проектов излишние затраты на разработку обычно не нужны. Если вы пишете приложение, которое будет использоваться только в вашей организации либо небольшим количеством клиентов, то лучше потратить деньги на покупку более быстрого или просто дополнительного оборудования, чем на очень дорогостоящих программистов, которые станут писать, тестировать или отлаживать код на C. Возможно, вы сможете достичь примерно такой же экономии, какой достигли мы в Couchbase. Просто наши расходы распределены на огромное количество потребителей.
Не пользуйтесь C вслепую, понимайте, на какие компромиссы при этом придется пойти и подумайте, целесообразно ли это в вашей ситуации. Erlang нам вполне подходит, но, чтобы выдерживать конкуренцию, нам требуется более быстрый и масштабный язык, ориентированный на максимальную производительность кода. В конце концов, сам Erlang написан на C.
Если код C в составе Erlang доставил вам большие проблемы, почему вы считаете, что много C — это хорошо?
Потому что такой код проще отлаживать, если не терять контекст между уровнем приложения и низкоуровневым кодом. Большая проблема, с которой мы сталкиваемся, такова: если код C вызывается из более высокоуровневого кода в том же процессе, то мы теряем отладочный контекст между высокоуровневым кодом и базовым кодом на C.
Когда мы сталкивались с этими сбоями, у нас не было ни необходимого опыта, ни инструментов, чтобы определить, какие именно операции код Erlang выполняет в момент сбоя. Erlang отличается высоким параллелизмом, в каждый момент времени выполняется множество разных операций. Мы знали, что что-то неладное связано с настройками асинхронного ввода-вывода, которые мы используем на виртуальной машине при открытии и закрытии файлов, но суть проблемы от нас ускользала.
Кроме того, мы не могли смоделировать сбой в тестовом коде, хотя и пытались. Поэтому было очень сложно описать проблему команде поддержки Erlang. Нам приходилось прогонять под высокой нагрузкой весь стек Couchbase, чтобы спровоцировать сбой, на это зачастую уходило 6 и более часов. Отладка сильно усложнялась не только из-за этого, но и потому, что в нашем собственном внутрипроцессном коде на C также имелись факторы, которые теоретически могли вызывать сбои.
В итоге после скрупулезного анализа кода мы обнаружили, что проблема коренится в компоненте Erlang, отвечающем за дисковую сортировку, используемых опциях сжатия и во взаимодействии с эрланговским механизмом закрытия файлов. Когда Erlang закрывает файлы, применяя при этом конкретную опцию сжатия, иногда в недрах виртуальной машины возникают условия гонки. В результате появляется висящий указатель и ошибка двойного высвобождения памяти (double-free bug). Если бы мы не теряли весь контекст между пользовательским кодом Erlang и базовым кодом C, то могли бы отследить эту проблему гораздо раньше. У нас был бы полный стековый след C, демонстрирующий, что делает наш код при сбое библиотечного кода. Так мы могли бы без труда найти дефектный код или модули C.
Почему C++ не подходит в качестве замены для C?
Зачастую подходит, но при работе с C++ требуется работать очень дисциплинированно и не усложнять ваш код без необходимости (в том числе намеренно). Кроме того, C++ хуже портируется во многие среды, особенно — в прошивки, а также характеризуется значительно более длительным временем компиляции и сборки. Это снижает продуктивность труда разработчика.
Необходимо также отметить, что C++ — сложная штука. Если вы предпочитаете работать с C++ благодаря богатой библиотечной поддержке и широкому кругу специалистов, то приходится принимать весь язык — и достоинства, и недостатки, и странности. Относительно того, что же в C++ благо, а что — зло, до сих пор ведутся дискуссии. Ваше понимание «разумного подмножества» языка вполне может не совпадать с тем, как это подмножество видят коллеги. В C эта проблема выражена гораздо, гораздо слабее.
Сможет ли Go заменить C?
Не исключено, что когда-нибудь сможет. Пока Go гораздо медленнее, чем C. Кроме того, Go не обеспечивает такого же качественного управления памятью, это связано со сборкой мусора. Go гораздо хуже портируется, кроме того, вы не сможете эксплуатировать Go в других экосистемах или языковых виртуальных машинах. Таким образом, возможности вашего кода ограничиваются.
Но Go обладает достаточным динамизмом и оптимистичным будущим, его разработчики очень красиво и прагматично спроектировали свое творение. Если процветание Go продолжится, то все проблемы, на которые я указал (кроме сборки мусора), полагаю, будут устранены.
Сможет ли D заменить C?
Нет, по тем же причинам, что и Go. Возможно, когда-то D сможет решать самые разные задачи, но я не слишком оптимистичен на этот счет, в первую очередь из-за недостаточного динамизма. У D нет такого надежного тыла, как Google, этот язык растет не слишком быстро. Но, возможно, все эти проблемы будут преодолены.
Сможет ли какой-нибудь язык заменить C?
Я не слишком хорошо представляю, что происходит там на переднем крае, но некоторые попытки создать новый C мне известны. Но если говорить о совершенно новом языке, способном заменить C, то наиболее интересен Rust от Mozilla. Он разрабатывался с акцентом на быстроту и портируемость, может вызываться практически из любого языка/окружения — в этом он похож на C. В Rust отсутствует сборка мусора, но в нем нет и переполнений буфера, утечек и условий гонки. Кроме того, в Rust реализован параллелизм в стиле Erlang.
Но этот язык очень молод, хотя и развивается он довольно бурно. По производительности он пока и близко не может сравниться с C. Синтаксис слишком странен для среднего разработчика, поэтому Rust очень сложно соперничать с мейнстримовыми языками. Поэтому он может, как и Erlang, со временем превратиться в нишевой язык.
Но если Rust достигнет тех целей, которые ставит перед собой, — обеспечить производительность, как у C, но параллелизм и надежность, как в Erlang, то это будет язык моей мечты. Я собираюсь очень внимательно следить за развитием Rust.
Дружище, это всего лишь твое личное мнение
Да, это было мое личное мнение.
Но я не новичок в программировании. Я профессионально им занимаюсь с 1995 года.
Мне доводилось писать графические пользовательские интерфейсы как на очень высокоуровневых языках — HTML/JavaScript, Visual Basic, так и на более низкоуровневых — Java, C, C++.
Я написал массу кода для машинных интерфейсов на C, C++ и Erlang. Мне доводилось писать фрагменты более 100 000 строк на C и C++. Я свободно могу прочитать, строка за строкой, до 300 000 строк на C.
На C++ я написал байт-кодовую виртуальную машину, которая развернута более чем на 100 000 000 ПК и сотнях тысяч серверов. В C++ я использовал наследование, шаблоны, исключения, собственное выделение памяти и ряд других функций, которые на тот момент считал довольно крутыми. Теперь я не завидую людям, которым приходится это поддерживать.
Я исправил множество багов в MySQL и ее базе кода на C++. Написал механизм по организации пула потоков для предприятий, изобрел функцию сетевого ввода-вывода, позволяющую на порядок увеличить клиентскую масштабируемость.
Короче говоря, я обладаю значительным практическим опытом в боевых проектах, которыми ежедневно пользуются миллионы людей. И я знаю, о чем говорю.
При этом вся статья, о которой мы говорим, — действительно просто мое мнение, его сложно подкрепить фактическими данными. Я решился написать ее лишь после того, как неоднократно обжегся на более новых и, казалось бы, классных языках. С годами мое видение языка C изменилось: я когда-то думал, что старички, влюбленные в C, просто морально устарели. Теперь я понимаю многих из них. Они хорошо понимали, на какие компромиссы можно пойти ради простого и эффективного кода.
Вспомните о наиболее широко распространенных проектах, связанных с базами данных, подумайте, как в них удалось достичь такой надежности и производительности. Возможно, они всего лишь написаны на чистом C, и это не просто совпадение.
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.