Support us

Непостижимая эффективность языка C

Оставить комментарий
Непостижимая эффективность языка C

Не так давно издательство O’Reilly выпустило книгу «21st Century C: C Tips from the New School», которая собрала на Amazon.com довольно противоречивые отзывы. Поэтому я решил написать исключительно оптимистичный материал о «древнем языке» C и предлагаю обсудить его настоящее (а если желаете — прошлое и будущее).
Слово Дамьену Катцу, автору CouchDB. В настоящее время он является главным техническим директором (CTO) в Couchbase.

Много лет я всеми правдами и неправдами пытался расстаться с языком C. Слишком прост по составу, требуется управлять множеством деталей, слишком старый и неработоспособный, на первый взгляд — слишком низкоуровневый. Я крутил бурные и страстные интрижки с Java, C++ и Erlang. На всех этих языках у меня есть выполненные проекты, которыми я горжусь, но все эти языки в свое время разбили мне сердце. Они сулили что-то и не сдерживали обещаний, создавали культуры, в которых ты сосредотачиваешься не на том, на чем следует, а также требовали идти на катастрофические компромиссы, которые не оборачивались ничем, кроме горестных разочарований. И я на коленях приползал обратно к C.

Язык C универсален. Это единственный язык, который может одновременно похвастаться и высокой продуктивностью и исключительной скоростью, превосходным инструментарием для любых областей, огромным сообществом разработчиков, высокопрофессиональной культурой. Да, не без компромиссов — но этот язык их не стыдится.

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

Простота и выразительность

С — поразительно высокоуровневый язык. Да, я не оговорился, и повторю еще раз: C — фантастически высокоуровневый язык. Конечно, не такой высокоуровневый, как Java или C#, и далеко не такой, как Erlang, Python или JavaScript. Но он не менее высокоуровневый, чем C++, зато гораздо, гораздо проще. Разумеется, C++ предлагает больше абстракций, но эти абстракции недалеко ушли от C. Работая с C++, вы все равно должны в совершенстве знать C, а также еще кое-какую смешную дребедень.


Если кто-то вам скажет: «Мне нужен язык программирования, на котором мне всего лишь достаточно сказать, чего я хочу — и все будет готово», дайте ему карамельку».
Алан Дж. Перлис

Сложно вспомнить какие-нибудь низкоуровневые языки, которые могли бы заменить C на практике. Это объясняется тем, что C так чертовски успешно абстрагирует свойства той машины, на которой работает, и делает это на таком высоком уровне, что остальные низкоуровневые языки просто не нужны, если есть C. Язык C действительно хорош в том, для чего предназначается.

Синтаксис и семантика C удивительно сильны и выразительны. На этом языке удобно судить и о высокоуровневых алгоритмах, и о низкоуровневом оборудовании. Семантика C настолько проста, а синтаксис так удобен, что значительно упрощается чисто когнитивная сторона работы. Программист может сосредоточиться на том, что действительно важно.

Язык C задал планку для по-настоящему хорошего низкоуровневого языка и избавился от всего, что этой планке не соответствует. Таким образом, С полностью перевернул представление о том, что такое «качественный низкоуровневый язык». Черт возьми, это впечатляет!

Более простой код, более простые типы

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

«Даром» вызывающая сторона не получает почти никакой информации.

«Вы хотели банан, но оказались наедине с гориллой, которая держит этот банан. А вокруг вас — джунгли».
Джо Армстронг


Иногда кажущаяся слабость оказывается преимуществом. Именно такова ситуация с API языка C: их «поверхностная область» кажется очень маленькой и простой. Вместо массивных фреймворков здесь делается ставка на хорошо прослеживаемые тенденции и культуру создания компактных библиотек, которые представляют собой легковесные абстракции простых типов.  
Сравните это с объектно-ориентированными языками, где в базах кода зачастую развиваются огромные взаимозависимые интерфейсы из сложных типов, где аргументы и возвращаемые типы оказываются сложнее. Такая сложность становится фрактальной: каждый тип является классом, определенным в контексте методов с аргументами и возвращаемыми типами, которые, в свою очередь, также могут быть сложными возвращаемыми типами.

Конечно, объектно-ориентированные системы типов не провоцируют такую фрактальную сложность сами по себе, но они повышают вероятность ее возникновения. В объектно-ориентированной среде проще пойти по неверному пути. C также не исключает возможности свернуть на кривую дорожку, но сделать это сложнее. В C обычно создаются более простые и неглубокие типы с меньшим количеством зависимостей. Они проще для понимания и для отладки.

Самый быстрый

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

«Если вы пытаетесь перехитрить компилятор, то практически сводите на нет его пользу».
Керниган и Плоджер, The Elements of Programming Style

Всякий раз, когда вам рассказывают о том, что на высокоуровневом языке, например, Java или Haskell, удалось достичь производительности «почти как на C» — изучите детали. В лучшем случае это окажется плоской шуткой. Таким «обгонятелям» приходится вытворять неуклюжие обратные сальто, пользоваться специальными знаниями об «умных» компиляторах и внутренних механизмах виртуальной машины, чтобы достичь сравнимой скорости. Обычно такие эксперименты приводят к тому, что простая и выразительная природа языка тонет в мудреных оптимизациях, специфичных для конкретных версий. Выигрыш в скорости обычно получается минимальным.

Если вы пишете на C программу, которая должна работать быстро, вы знаете, почему она получится быстрой. Такая программа не будет существенно замедляться при использовании различных компиляторов и сред исполнения, как это происходит при использовании виртуальной машины, экспериментах с настройками сборщика мусора (они могут радикально влиять на производительность и длительность пауз). Не случится и такого, что срабатывание какого-то фрагмента кода в приложении коренным образом изменит профиль сборки мусора во всей остальной программе.

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

Ускоренные циклы сборки-запуска-отладки

Эффективность и продуктивность труда разработчика исключительно сильно зависят от цикла «сборка-запуск-отладка». Чем быстрее этот цикл, тем более интерактивна сама разработка и тем дольше вы остаетесь в движении, тратя время именно на решение задачи. C обладает наивысшей интерактивностью разработки среди всех широко распространенных статически типизированных языков.

«Оптимизм — это профессиональная болезнь всего программирования. Обратная связь — это лекарство от этой болезни».
Кент Бек

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

Вездесущие отладчики и удобные аварийные дампы

Практически в любой системе, куда вам может потребоваться портировать программу, найдутся готовые отладчики C и инструменты для аварийных дампов. Это бесценные вещи, если нужно быстро найти источник проблем. А проблемы будут.


«Ошибка, отсутствует клавиатура — для продолжения нажмите F1».

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

Если же код написан только на C, то вы сможете вызывать стеки, переменные, аргументы, локальные поточные переменные и глобальные переменные — практически все, что встречается в памяти. Это поразительно полезно, особенно если приходится решать проблему, которая усугублялась уже несколько дней в долгоживущем серверном процессе, а как-либо иначе воспроизвести ее невозможно. Если вы потеряете такой контекст в высокоуровневом языке — я вам не завидую.

Вызывается отовсюду

В языке C есть стандартизированный бинарный интерфейс приложений (ABI), поддерживаемый всеми существующими операционными системами, языками и платформами. Он не требует ни среды времени исполнения, ни каких-либо иных неизбежных издержек. Таким образом, код, написанный вами на C, полезен не только для вызывающих элементов из C, но и для любой мыслимой библиотеки, языка или среды, какие только существуют в природе.

«Ключ к портируемости — немногочисленные концепции и полное описание».
Дж. Пальме

Код на C можно использовать в автономных исполняемых файлах, скриптовых языках, коде ядра, коде прошивок, в качестве DLL и даже вызывать из SQL. Это лингва франка системного программирования и подключаемых библиотек. Если вы хотите написать какой-то код только однажды и обеспечить его используемость в большинстве сред и ситуаций, то C — единственный разумный выбор.

Да, у него есть и недостатки

В C много «недостатков». В нем нет контроля границ, в памяти легко что-нибудь повредить, встречаются висящие указатели и утечки памяти/ресурсов, поддержка параллелизма прикручена кое-как, нет модулей и пространств имен. Обработка ошибок может быть страшно затруднительной и пространной. Легко допустить целый класс ошибок, о которые разбивается стек вызовов, а ваш процесс захватывается зловредным вводом. Замыкания? Смешно! 

«Если ничего не получается, прочтите инструкцию».
Л. Ласельо

Недостатки C очень хорошо известны, и это радует. Во всех языках и реализациях есть свои сбои и проблемы. Язык C просто их не скрывает. Существует масса статических и динамических инструментов, позволяющих справляться с наиболее распространенными и опасными ошибками в этом языке. Тот факт, что некоторые наиболее востребованные и надежные программы в мире написаны именно на C, доказывает, что недостатки языка преувеличены, ошибки легко отслеживать и устранять.

Как-то раз в Couchbase нам довелось потратить более двух человеко-месяцев работы, чтобы справиться со сбоем в виртуальной машине Erlang. У нас ушла уйма времени на то, чтобы отследить одну проблему, которая коренилась в самом ядре реализации Erlang. Сначала мы вообще не понимали, что происходит и почему, думали, что ошибка в нашем подключаемом коде на C, безуспешно пытались ее найти и исправить. А оказалось, что в ядре Erlang есть баг, вызывающий условия гонки. Такая фундаментальная проблема возможна в любом языке.

Сначала, из соображений производительности, мы стали все активнее переписывать код Couchbase на C, именно на этом языке реализовали несколько новых фич. Но когда мы столкнулись с проблемами, оказалось, что их совсем несложно отлаживать и исправлять. Итак, в долгосрочной перспективе язык C более продуктивен.

Где-то в подсознании я желаю сделать язык C немного лучше. Просто сгладить некоторые острые края и исправить кое-какие вопиющие проблемы. Но, наверное, не удастся исправить все сверху донизу — синтаксис, семантику, инструментарий. Возможно, это и не стоит усилий. На сегодняшний день язык C непостижимо эффективен, и ситуация вряд ли изменится в обозримом будущем.

Источник

16 лет dev.by — «дефолтный» источник информации о беларусском ИТ

Вы можете...

Читайте также
7 отличных курсов по финансам. Уплыть «с галеры» и основать свой стартап
7 отличных курсов по финансам. Уплыть «с галеры» и основать свой стартап
7 отличных курсов по финансам. Уплыть «с галеры» и основать свой стартап
Если вы посмотрели «Волк с Уолл-стрит» и хотите, как Леонардо ди Каприо прогуливаться по яхте с бокалом вина в руках, но не знаете, с чего начать, подборка курсов Digitaldefynd станет для вас отличным стартом. Здесь представлены как платные, так и бесплатные программы, которые помогут вам освоить финансовое моделирование. Они подойдут не только для начинающих слушателей, но и для экспертов.
Не Paint-ом единым. 13 курсов по UX/UI-дизайну для продвинутых и не только
Не Paint-ом единым. 13 курсов по UX/UI-дизайну для продвинутых и не только
Не Paint-ом единым. 13 курсов по UX/UI-дизайну для продвинутых и не только
Если вам нравится думать о том, как с минимальными затратами получить максимум эффективности, то проектирование пользовательских интерфейсов определенно вас заинтересует. DigitalDefynd сделал подборку курсов по UX/UI-дизайну как для новичков, так и для продвинутых специалистов. 
Компания в 200+ человек ждёт зарплату две недели. Завис перевод в Цептер Банк?
Компания в 200+ человек ждёт зарплату две недели. Завис перевод в Цептер Банк?
Компания в 200+ человек ждёт зарплату две недели. Завис перевод в Цептер Банк?
26 комментариев
Как удалёнка портит карьеру в ИТ
Как удалёнка портит карьеру в ИТ
Как удалёнка портит карьеру в ИТ
Строить карьеру в ИТ было непросто до пандемии и глобальной миграции на удалёнку. Спланировать путь к заветной должности, мониторить кучу информации от средних зарплат до списков востребованных скиллов, а потом добиваться заслуженного повышения или прибавки у боссов. Сделать это теперь, когда все работают из дома, ещё сложнее, но тем не менее возможно, рассуждает Dice Insights.

Хотите сообщить важную новость? Пишите в Telegram-бот

Главные события и полезные ссылки в нашем Telegram-канале

Обсуждение
Комментируйте без ограничений

Релоцировались? Теперь вы можете комментировать без верификации аккаунта.

Комментариев пока нет.