Я хочу идти быстро

Я программировал недолго, но дошел до того, что мне стало немного скучно работать с Ruby, Python, Scala, PHP, C # и JavaScript (и это лишь некоторые из них). Новые языки кажутся старыми, переделанными. Проблемы более или менее похожи, и проблемы кажутся одинаковыми. Мне нужен новый вызов.

Но что это значит? Для меня это означало изучение C ++ и создание вещей, которые работают действительно быстро. Это означает изучение новой области и вывод мастерства программного обеспечения на новый уровень. Продолжайте читать, и я объясню.

Желание идти быстро (в C ++)

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

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

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

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

А потом жизнь повторилась.

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

Пришло время снова окунуться в страну неэргономичного и сложного кода… по крайней мере, я так думал.

Изучение C ++

Если вы не изучали C ++ или только прикоснулись к нему на уроках алгоритмов в колледже, то вы должны знать, что C ++ существует в двух формах. Есть pre-C ++ 11, который можно описать как «C с классами», и есть C ++ 11 и выше (11, 14, 17 и скоро 20).

Я был более чем приятно удивлен, увидев, что многие из современных функций, которые мне нравились в Rust, существуют и в C ++. Он имеет умные указатели, которые присваивают определенные свойства владения, такие как уникальность или общие (подсчитанные по ссылкам) объекты. Наряду с этим есть семантика RAII, которая позволяет легко избежать большей части кода, которым было так легко выстрелить себе в ногу.

В последней версии (17) были добавлены дополнительные обновления для 11 и 14, чтобы улучшить библиотеку и добавить несколько небольших функций (таких как структурированные привязки). Тем не менее, FileSystem TS (техническая спецификация) приземлилась и является серьезным улучшением языковой эргономики.

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

Я пытаюсь сказать, что современный C ++ на самом деле просто современный. Он содержит все (или, по крайней мере, большинство) функций, которые я привык использовать в таком языке, как Scala. И это намного более безопасный язык, чем раньше. И, что самое главное, эволюция не останавливается. К языку постоянно добавляются новые интересные функции.

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

P.S. - Если вас это волнует, посмотрите новый проект, над которым я работаю, на сайте cpp-vs.com, чтобы узнать о другом способе изучения C ++.

Мастерство (Performance Edition)

Мы все можем отстать от мастерства, потому что это, по сути, означает создание красивого кода. И красивый может означать, что его легко читать, легко расширять / изменять, легко тестировать или все вышеперечисленное (и многое другое). И это весело пробовать новые техники и основные шаблоны, которые позволяют нашему коду быть красивым.

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

Модели собственности

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

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

  • Гарантируется ли существование этой части данных, пока она у меня есть?
  • Могу ли я оставить себе копию этого указателя?
  • Могу ли я поделиться копиями этого указателя с другими частями приложения?

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

Абстракции с нулевой стоимостью

Представьте, что вы применяете функциональный код к списку данных - map, flatMap, filter и т. Д. Теперь представьте, что когда вы компилируете этот код, вы получаете замкнутый цикл с нулевыми ненужными копиями ваших данных со всеми этими аккуратно свернутыми действиями. По сути, это абстракция с нулевой стоимостью.

Это способность разрабатывать библиотеки и писать код, которые позволяют нам быть выразительными и писать читаемый код. Затем мы позволяем компилятору оптимизировать этот высокоуровневый код в очень эффективный низкоуровневый код.

При написании C ++ это основной принцип языка и стандартной библиотеки, к которому следует стремиться при разработке внутренних абстракций и библиотек.

Шаблоны параллелизма

Если вы хотите действовать быстро, вы должны быть осторожны и внимательны к своим шаблонам параллелизма. Ограничен ли ввод-вывод вашего приложения? Это связано с процессором? И вы смоделировали свое приложение, чтобы воспользоваться этим ограничением?

Если вы привязаны к вводу-выводу, используете ли вы систему событий, такую ​​как libevent или libev? Если вы привязаны к процессору, закрепляете ли вы свои потоки и используете NUMA? Правильно ли вы смоделировали параллелизм, чтобы максимизировать производительность приложений?

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

Cache-Lines

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

Канонический пример - узкие петли. Это означает выполнение цикла, который включает только локальные данные (например, повторяемый список). Другой появляющийся шаблон называется Data-Oriented Design, который является отличным способом максимизировать эффективность кеширования на уровне приложения - гораздо более широкая область применения примера с замкнутым циклом.

Вы хотите идти быстро?

Если вас это волнует, ознакомьтесь с этим замечательным списком ресурсов, чтобы быстро начать изучение C ++.

Статьи по теме Джона Мюррея