Современный C++: что это и как он появился?
В течение последнего десятилетия с приходом стандарта C++11, а перед этим — предваряющих его спецификации TR1 и библиотеки Boost, — в сообществе C++-разработчиков наблюдался массовый переход на новый стиль программирования, так называемый современный C++. Этот переход подразумевал введение в оборот таких приемов как ключевое слово auto, замыкания (лямбда-выражения), вариативные шаблоны и многое другое. C++ оказался благодатной почвой для экспериментов, и на свет появилось несколько библиотек, написанных в новом стиле. Те, кто смог разобраться в новых идиомах вроде SFINAE, диспетчеризации тегов, CRTP, генератора типов, безопасного bool и т.д., или хотя бы научился их воспроизводить, были награждены званием гуру.
С появлением в начале 1970 -х гг. интуиционистской теории типов Мартин-Лёфа, которая представляет собой результат скрещивания чистой математики и информатики, начался период активных исследований в области языков нового типа, таких как Agda и Epigram. Так был заложен фундамент парадигмы функционального программирования. Все эти теории сейчас преподаются в вузах и провозглашаются в качестве «прорыва», а в их развитие и продвижение вкладываются огромные средства. Появилось даже целое сообщество ораторов, зарабатывающих на жизнь пропагандой этого «прорыва» среди представителей деловой Америки. Поэтому неудивительно, что на текущие решения Комитета по стандартизации C++ активно влияют новые члены — вчерашние студенты, чье мнение было сформировано под воздействием этого окружения.
Переориентация с производительности на функциональность
Со временем C++ превратился из «быстрого» языка в «функциональный», и о производительности забыли. Сейчас уже ни для кого не секрет, что некоторые компоненты C++, например, библиотека iostream и строки, в силу своей природы не могут работать быстро; кроме того, отсутствуют некоторые основные возможности вроде управления передачей данных по сети и совсем уж базовые функции, например, простейшая процедура разбиения строк. Если спросить любого члена Комитета, почему эти недостатки так и не устранили за прошедшие почти двадцать лет, ответ неизменно будет один и тот же: потому что никто не хотел возиться с подготовкой соответствующего доклада или предложения.
Комитет рассчитывает использовать исследовательскую группу SG14 — подразделение Рабочей Группы ISO 21 (WG21), посвященное разработке игр и высокочастотному трейдингу (HFT) — в качестве платформы для обсуждения дальнейших улучшений языка между специалистами из индустрии ПО с низкими задержками. Однако, судя по уже состоявшимся обсуждениям в дискуссионных группах и Стандарту Майкла Уонга (его содержание такое же забавное, как и название), никто не горит желанием проводить радикальные реформы, которые так необходимы C++, чтобы успешно конкурировать в этой области с «C++ с классами».
А почему это, собственно, проблема?
Я провожу экспертный анализ C++-кода для большого количества компаний-разработчиков в качестве консультанта как по стратегическим инвестициям, так и по вопросам оптимизации и увеличения производительности приложений, и на собственном опыте убедился, что производительность программ, написанных на современном C++, оставляет желать лучшего. Причина не в том, что новый стиль не позволяет создавать высокопроизводительные системы с низкими задержками, просто в процессе разработки возникает множество препятствий, которые парализуют работу программистов или порождают чрезвычайно труднооптимизируемые графы, создающие сложности компиляторам. Я несказанно рад, что есть Чендлер Карут с его серией видеолекций, благодаря которым мы можем восстановить утраченную связь (а заодно и толику здравомыслия) между языком C++ и реальностью.
Потеря производительности
Первый камень преткновения, как уже говорилось, — это потенциальная потеря производительности по причине более сложного устройства структур промежуточного представления кода и проходов компилятора. С этой точки зрения Fortran намного лучше, поскольку он проще и компиляторы могут гораздо эффективнее оптимизировать его по сравнению с аналогичным кодом на C/C++. Мой собственный опыт работы в сфере HFT и разработке игр в течение многих десятилетий, а также данные авторитетных рецензируемых изданий, да и вообще наблюдения любого, кто немного разбирается в оптимизациях компилятора, показывают, что распространенное убеждение, будто обычные и вариативные шаблоны позволяют получать на выходе намного более быстрый ассемблерный код благодаря автоматическому «растворению» C++ кода на этапе компиляции, не подтверждается на практике. Это не более чем заблуждение, при этом весьма устойчивое. Фактически имеет место обратное: небольшие фрагменты кода гораздо лучше компилируются, отлаживаются и оптимизируются, когда работаешь со сгенерированным ассемблерным кодом, а не с шаблонами.
Время компиляции
Следующая проблема связана со временем компиляции. Обычные и вариативные шаблоны «славятся» своей способностью увеличивать время компиляции в десятки раз из-за пересборки намного большего числа вовлеченных в процесс файлов по сравнению с традиционным стилем. В то время как простой, традиционный класс в C++ перекомпилируется только в том случае, если были изменены непосредственно включаемые им заголовочные файлы, в современном C++ одно небольшое изменение зачастую влечет за собой полную пересборку проекта, на что нередко уходит до десяти минут. Для классического C++ это время, при такой же небольшой правке, составляет несколько секунд.
Как скоро программист сможет приступить к тестированию сгенерированного ассемблерного кода, закончив вносить необходимые правки в программу, напрямую зависит от качества генерируемого кода. Если приложение можно быстро собрать и проверить, то все проблемные места будут поправлены с большей вероятностью. А если на компиляцию уходит десять минут, то эта вероятность сильно снижается.
Сопровождаемость
Третье, и последнее, препятствие связано со сложностью кода. Как однажды сказал Эдсгер Дейкстра,
Простота — залог надежности.
Если для того чтобы разобраться в коде, приходится нанимать гуру программирования, то либо что-то не так с кодом, либо выбранный язык не подходит для решаемых задач.
Если внимательно прочитать, пожалуй, самую известную статью Дейкстры, «О вреде оператора goto», нетрудно заметить, что самые главные ее положения распространяются и на шаблоны (обычные и вариативные): дело не в том, что плох сам механизм, а в том, что изначально присущая ему структура неизбежно порождает сложный код, что в конечном счете нивелирует самое главное достоинство качественного кода — его понятность.
При создании торговых систем, когда по сети пересылается до 100 000 заявок в секунду, а торговые стратегии разрабатываются и реализуются каждые два дня, простота кода не просто желательна — она необходима. Отсюда мое «правило одной минуты» при разработке подобных систем:
Если за одну минуту нельзя понять, что делает данный C++-файл, считайте, что код неверен.
Истинная причина
Однако истинная причина, по которой я почти перестал иметь дело с современным C++, несмотря на то что весьма преуспел в нем, заключается в том, что в IT-индустрии появляется все больше разработок, которым действительно стоит уделять внимание.
C++ стал похож на Fortran: он достиг своих пределов. Сейчас уже существуют настолько быстрые интерфейсы, что само понятие «тактовый цикл» становится неактуальным для определенных узлов системы. Скорости достигли таких значений, что два бита информации, посланные строго одновременно по паре смежных проводов, наверняка рассинхронизируются, не пройдя и метра.
C++ уже не годится для управления такими скоростями, которые реализуются в современных процессорах, массово выпускаемых на рынок, поскольку он изначально ориентирован на последовательный способ передачи данных — даже в максимально распараллеленных системах вроде GPU.
Современный разработчик, если он действительно хочет идти в ногу со временем, вынужден обращаться к новым языкам — Verilog и VHDL; он должен уметь проектировать собственные процессоры и виртуальные модели материнских плат, иначе он не совладает с лавиной технологических достижений ближайших лет. И дело не в том, что ППВМ (Программируемая пользователем вентильная матрица, Field-Programmable Gate Array, FPGA) отличаются сверхвысокими скоростями — это совсем не так. На самом деле они на порядок медленнее, чем топовые процессоры.
Просто сейчас появляется все больше всевозможных реконфигурируемых вычислительных систем. Например, Intel поставляет процессоры Xeon со встроенными ППВМ, а «Интернет вещей» (Internet of Things, IOT) в ближайшие пять лет превратится в рынок с оборотом в миллиарды долларов, и двигателем этого процесса в значительной степени выступают маленькие десятидолларовые ППВМ, для программирования которых придется нанимать целую армию квалифицированных технологов. И поверьте мне, программировать на уровне RTL (register transfer level, уровень регистровых передач ) в сотни раз сложнее, чем писать код на C++. И если изучение C++ дается нелегко, представьте, каково осваивать ППВМ-программирование на профессиональном уровне (документация к одному только инструменту Transceiver Toolkit от Altera занимает 700 страниц, к среде разработки Quartus — еще 1000, и это не говоря уже о продукции Xilinx).
И все же оно того стоит. Когда вы, наконец, овладеете этими новыми языками и приемами, перед вами откроются величайшие возможности для воплощения собственных идей. Именно так и рождаются «единороги» (стартапы, чья оценочная стоимость за короткий срок возрастает до миллиарда долларов и выше — прим. перев.) — благодаря людям, которые способны увидеть всю картину целиком.
Заключение
Как бы там ни было, я считаю, что язык C++ постепенно уходит в прошлое. Как и в случае с Fortran, его возраст дает о себе знать. А потому вкладывать силы и время в совершенствование своих навыков программирования на современном C++ — все равно что вкладываться в Cobol или тот же Fortran. Программист нового времени должен осваивать новые инструменты, чтобы иметь возможность управляться с передовыми технологиями, которые появятся в ближайшие десятилетия. А времени на изучение всего этого слишком мало.
Примечание переводчика
Я не во всём согласен с мнением автора, однако, считаю, что С++ программистам следует ознакомиться с этой статьёй. У меня тоже есть ощущение, что с современным C++, что-то не в порядке. «Навороченный» код на шаблонах становится крайне сложно понимать и вдобавок, он не даёт обещанной эффективности. Я вообще все больше склоняюсь в сторону написания кода в стиле C++ с классами.
Ах, да, чуть не забыл. Используйте статические анализаторы кода, чтобы уменьшить хотя бы количество глупых ошибок — и без них головной боли с C++ хватает.
Автор: PVS-Studio