Почему не лишено смысла писать код на C, а не на C++


Предыдущая | Следующая

Сегодня мы поговорим на тему «C против C++». Некоторые читатели данного блога уже знакомы с моей точкой зрения по этому поводу. Когда встает вопрос с формулировкой вроде «похоже, мы решаем задачу, где очень важна скорость выполнения кода, и нужно выбрать между языками C и C++», в последнее время я склонен рекомендовать C. Многие программисты при этом недоумевают, мол «как же так, ведь С++ новее и имеет больше фичей, и вообще C входит в него как подмножество». Поэтому я хотел бы подробно объяснить свою точку зрения один раз в данном посте, так как каждый раз объяснять ее заново занимает ощутимое количество времени.

Хотелось бы начать с небольшого дисклеймера. Всегда найдутся люди, которые пишут на C++ последние лет 20, и потому (1) они искренне считают язык простым и понятным, (2) им не хочется учить что-то новое, ведь их и здесь неплохо кормят. Это, собственно, и есть так называемый C++ головного мозга. Далее я предполагаю, что читатель не страдает от этого недуга и не утратил трезвость восприятия и открытость мышления по каким-либо иным причинам. Также стоит отметить, что в вопросах «какой язык лучше» нет правых и неправых. Одни и те же объективные преимущества и недостатки воспринимаются разными людьми с разными весовыми коэффициентами, поэтому на выходе получаются разные значения функции fitness. В этой заметке мне хотелось бы пояснить причины, по которым у меня весовые коэффициенты выставлены так, как они выставлены, а не доказать, что у кого-то они выставленные неверно.

Сразу отмечу, что я не считаю, что C++ умер, что это ужасный и совершенно ни на что не годный язык или что-то в этом роде. Трудно ругать язык, на котором написаны Chromium, Skype, Sublime Text и множество других программ, которые я использую каждый день. Не говоря уже о великом множестве хороших библиотек на С++. Тут мне сразу вспоминаются, например, Assimp и wxWidgets. Более того, вы можете помнить, что в заметке Критика языка Rust и почему C/C++ никогда не умрет я отстаивал C++ и говорил, что в обозримом будущем он никуда не денется. Нельзя исключать и культурный фактор. Так игры AAA класса принято писать на C++, потому что в индустрии уже много лет все так делают. Если вы работаете в этой индустрии, то особого выбора у вас может и не быть.

Несмотря на все это, я считаю, что в третьем тысячелетии лучше не писать нового кода на C++, если, конечно, (!) у вас есть такая возможность. И далее я постараюсь более подробно объяснить эту точку зрения.

Примечание: Специально для читателей, считающих, что на C давно никто не пишет, спешу сообщить, что это не так. В первую очередь на C, конечно же, ведется разработка всех современных операционных систем, драйверов, и подобных вещей, например, систем виртуализации. Многие десктоп приложения все так же пишутся на C, например, Claws Mail, Liferea, XChat, Transmission, Gimp, Pidgin, Tox, а также оконные менеджеры и десктоп окружения — Xfce, Lxde, Awesome, i3, и другие. Серверные приложения также часто разрабатываются на C. Тут вспоминается HAProxy, lighttpd, NginxNagiosMemcachedRedis и PostgreSQL. Еще можно вспомнить, например, виртуальную машину языка Erlang и интерпретатор языка Python. Все эти приложения объединяет то, что они должны использовать доступные им ресурсы максимально эффективно — десктоп приложения должны хорошо работать на бюджетных компьютерах, даже таких, как Raspberry Pi, серверные приложения должны обрабатывать как можно больше запросов в секунду, и так далее.

Итак, основная идея, пожалуй, состоит в следующем. Если вы решаете задачу, где действительно очень важна скорость (определение см далее), вы все равно не сможете использовать C++. Вы, вероятно, сможете писать на так называемом «C с классами» или «C с шаблонами». Эти диалекты языка C, бесспорно, имеют право на жизнь. И если вы называете «языком C++» эти диалекты, то я, пожалуй, с вами даже соглашусь — для задачи надо брать «язык C++», срочно! Только нужно при этом быть очень уверенным, что через год вы не выйдите за рамки «C с шаблонами». Эта действительно большая проблема на практике и она более детально описана далее.

Однако большинство людей под С++ понимают так называемый «современный C++», со счетчиками ссылок, классами, исключениями, шаблонами, лямбдами, STL, Boost, и так далее. То есть, тот C++, на котором вы пишите, почти как на Java, в котором никогда не встречаются обычные указатели, и вот это все. Если вам очень важна скорость, то писать на таком C++ вы не сможете. Если же он вам подходит, то лучше взять Java, Go или любой другой высокоуровневый язык по вкусу. В них все те же возможности реализованы намного лучше. А узкие места при необходимости, которой, впрочем, может и не возникнуть, вы всегда сможете переписать на C.

Позвольте пояснить, что я имею ввиду под задачами, где очень важна скорость. Вы едете на машине. Вдруг в нескольких метрах впереди выбегает человек. На принятие решения водителю в среднем требуется около одной секунды. Нога мелено перемещается с педали газа на педаль тормоза. Затем медленно вдавливает тормоз в пол. Расстояние между автомобилем и человеком в это время сокращается. Наконец, сигнал от педали тормоза летит в бортовой компьютер автомобиля. И вот тут ни в коем случае программа не может сказать «о, счетчик ссылок обнулился, пойду-ка я собирать мусор по всему дереву» или даже «секундочку, я только схожу в vtable… как, ее нет в L1? ой…». Программа должна обработать сигнал как можно быстрее, тут же ударив по тормозным дискам. Ни о каких смартпоинтерах и прочих видах автоматического управления памятью, ровно как и о развесистых иерархиях классов, в таких задачах и речи быть не может.

Из менее драматичных примеров можно привести любую систему, где не работает правило «90% времени выполняется 10% кода, эти 10% и будем оптимизировать». Если код, который выполняется всего лишь 10% времени, ускорить на 1/20, суммарная производительность вырастит на жалкие 0.5%. Но в масштабах компании вроде Google или широко используемого приложения вроде PostgreSQL или Nginx эти 0.5% ускорения могут означать миллионы долларов экономии. То есть, несколько месяцев работы небольшой группы программистов в этом направлении окупаются с лихвой. А раз так, почему бы, например, не отказаться от STL совсем и сразу не использовать алгоритмы и структуры данных, заточенные под конкретный случай (примеры есть далее по тексту)?

Я могу привести еще много примеров такого рода. Но идея, надеюсь, ясна. Если решаемая вами задача такова, что написать 90% кода на высокоуровневом языке и 10% на C никак нельзя, то ни на каком «C++, который почти как Java, только компилируемый в машинный код» вы писать не сможете. Если же в вашей задаче можно не париться по поводу 0.5% производительности, то скорость вам нужна не так сильно, как вы думали.

Серьезно, возьмите лучше Go, Java, или любой другой язык по вкусу. Сборка мусора там сделана намного лучше (напомню, что счетчики ссылок плохо работают для большого количества быстро умирающих объектов), прочее управление ресурсами, управление зависимостями, инструменты разработки (IDE и прочие), генерики, лямбды, неймспейсы, исключения, автоматический вывод типов, классы и интерфейсы там сделаны намного лучше. В качестве бонуса вы получите существенно большую скорость разработки, куда более читаемые сообщения об ошибках, куда более лучшую кроссплатформенность, простую отладку, высокую скорость компиляции и так далее, за что там обычно вполне заслуженно ругают С++. Да и найти достойных программистов на этом другом языке станет на порядок проще.

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

Не удивительно, что и сегодня даже для новых проектов многие программисты (Линус Торвальдс, пожалуй, является самым известным примером) выбирают язык C, а не C++. Стремление писать код на C — это стремление к максимально простому и понятному коду, стремление использовать ресурсы как можно более эффективным образом, и не в последнюю очередь это стремление к красоте. Технологии появляются и исчезают. Подходы, которые еще вчера считались общепринятой практикой, сегодня уже причисляют к антипаттернам. И только C прекрасен и вечен. На чем еще писать, если с 1972 года люди так и не придумали ничего лучше?