08 February 2012

GoingNative 2012 + Why C++?

Потихоньку начал отсматривать выступления с конференции, организованной Microsoft, и посвященной C++ -- GoingNative 2012.



Why C++?

Маленькое лирическое отступление перед тем, как перейти непосредственно к обсуждению самих докладов.

Герб Саттер, самый главный человек по плюсам в Microsoft и, по совместительству, председатель комитета по стандартизации этого языка, последние несколько лет постоянно рассказывает о "ренессансе C++" как минимум в рамках немаленькой корпорации, где он имеет счастье работать. Оглядываясь на историю IT, Саттер говорит о том, что 2000-е годы -- годы потерянные для С++, так как повсюду царила managed лихорадка, которая явно перешагнула за границы элементарных рассудительности и здравомыслия. Ударила эта истерия и по Microsoft, где сверх успешный и действительно очень удачный .NET решили возвести в рамки абсолюта, и прийти к состоянию "одно сплошное телевидение" (кстати, отсутствие поддержки native кода в Windows Phone 7 -- одно из последних значительных решений той эпохи). Во времена Windows Vista первые люди в Microsoft на полном серьезе подумывали о том, чтобы все новые API в этой системе были исключительно managed.

Однако время расставило все на свои места. Почему сегодня мы наблюдаем картину под названием "C++ strikes back"?

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

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

Второе -- бурный рост мобильного направления, у которого своя специфика.
Там, с одной стороны, крепко борются за энергопотребление, с другой -- хотят дать пользователю интерфейс, который работает плавно, без всяких лагов, со скоростью 60 кадров в секунду. C++ снова в деле!

Третье -- софт, который работает на серверах. Конечно, если ваш сервер обслуживает пару тысяч запросов в день, то писать свой код вы можете хоть на квик бейсике. А вот Facebook, который обслуживает миллионы пользователей ежедневно, серьезно задумался над тем, куда их ведет существующий код, практически полностью написанный на PHP. Потому что если этот код ускорить хотя бы в два раза, это будет давать миллионы долларов в год экономии на серверном железе, которое надо покупать, резервировать, где-то размещать и платить за электроэнергию, которое оно потребляет.

***

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


В свое время я рассказывал о том, как мы решаем вопрос с загрузкой нашей железки, построенной на процессорах Blackfin. В решении этой проблемы участвует одно маленькое приложение, которое грузится сразу в RAM и вступает в качестве "флешера", зашивая поступающие по UART данные во flash память. Часть кода этого приложения я привел в качестве иллюстрации к материалу, и некоторые люди восхищенно удивлялись "вау! никогда бы не думал, что embedded приложения можно кодировать на C++ в таком стиле!".

На самом деле, это лишь маленький кусочек айсберга -- много гораздо более интересных вещей можно найти в самом приложении, которое написано под легковесную проприетарную RTOS ("ОС" это сильно громко сказано, скорее речь идет о миниатюрном ядре с поддержкой многопоточности). Основная задача приложения -- работа в режиме "моста", вывод в реальном времени в TCP/IP сеть по RTP протоколу до 30 голосовых каналов, передающихся в синхронном цифровом потоке. В приложении полно низкоуровнего кода, включая работу с этим самым синхронным цифровым потоком через прерывания и DMA, DSP алгоритмы для обработки звука, пул очень хитрых буфров, который используется для передачи блоков информации из этого потока в сеть и обратно и т.д. и т.п. При этом весь код проекта есть высококачественный и надежный C++ код -- очень строго типизированный, с использованием STL, с автоматическим управлением памятью, с исключениями, с блекджеком и шлюхами... Ну а если говорить про весь проект в целом, в котором железка с Blackfin всего лишь его маленькая часть, то значительный объем его исходного кода используется для компиляции под три операционные системы (Windows, Linux, VDK) для работы на трех разных процессорных архитектурах (x86, ARM, Blackfin) и при этом очень активно используется эмуляция нюансов аппаратной части, которая позволяет вести разработку очень эффективно (подробнее об эмуляции для другой нашей железки на ARM из этого проекта).

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


Threads and Shared Variables in C++11

Довольно нудный доклад от какого-то хрена из HP о появившихся в новом стандарте таких капитанских вещах как треды и мютексы, а также о том, какие гарантии теперь должен давать компилятор в ситуациях, которые потенциально приводят к race conditions.

Лично я для себя вынес из доклада вот какую сомнительную историю.

Дядя показал вот такой вот код, сказав, что это пример ленивой инициализации, когда кто-то желает сэкономить на лишнем локе мютекса, для чего заводит отдельный флаг типа bool:

  1. mutex m;
  2. bool initd = false;
  3. int x;
  4. // ...
  5. if (!initd)
  6. {
  7.         // point #1                            
  8.         lock_guard<mutex> _(m);
  9.         x = 42;
  10.         initd = true;
  11. }
  12. // read x
  13. // ...

Код содержит в себе race condition, и для того, чтобы от него избавиться, достаточно поменять тип флага initd с bool на atomic<bool>. Лично я сидел и смотрел на эту "правильную" версию с отвисшей до пола челюстью, потому что меня в бурсе учили немножко по другому. Во-первых, в секции ленивой инициализации редко кто станет инициализировать целочисленную переменную, и обычно речь идет о создании какого-то объекта через new. Во-вторых, абсолютно естественным требованием к ленивой инициализации является требование ее строго единоразового выполнения.
Проблема вышеприведенного кода в том, что даже если поменять тип флага с bool на atomic<bool>, ленивая инициализация в таком виде может быть выполнена многократно. (Внимание, спойлер!) Проблема в том, что при исходных условиях, в точку #1 после проверки флага, но до залочки мютекса, может попасть сколько угодно большое число потоков, каждый из которых потом спокойно возьмет мютекс и вызовет код инициализации для x.

Вот один из возможных вариантов корректного кода ленивой инициализации:

  1. mutex m;
  2. volatile T *x;  // atomic<T*> in C++11
  3. // ...
  4. if (== 0)
  5. {
  6.         lock_guard<mutex> _(m);
  7.         if (== 0) x = new T();
  8. }
  9. // read x
  10. // ...

Для C++98 переменную x надо сделать volatile (ибо компилятору повторная проверка на нуль может показаться несколько избыточной), в новом стандарте надо использовать шаблон atomic. Подразумевается, что на вашей платформе запись указателя происходит атомарно на уровне инструкций процессора (новый стандарт даст на это гарантию через использование atomic)...

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



Bjarne Stroustrup: C++11 Style

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


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

Началось все с легендарного qsort() из сишной библиотеки, который, разумеется, есть ад и лютый пиздец, и который, помимо всего прочего, еще и работает в разы медленнее алгоритма из STL. 
Потом нам рассказывали о том, почему в океан падают спутники. Не, не, виноват не Путин, главная проблема -- плохо типизированный код, когда константа в американских футах и галлонах используется в коде, рассчитывающем все в СИ. 
Потом нам рассказали о самой главной и любимой технике абсолютно любого уважающего себя C++ программиста -- RAII. 
О стандартных алгоритмах и о том, что не надо изобретать велосипеды (справедливости ради, юзабельность стандартных алгоритмов до появления лямбда функции в языке была ниже плинтуса).
О move семантике, и о том, что не надо злоупотреблять наследованием. 
Короче, вы поняли. 

Что я могу сказать?
Лично я послушал все это с удовольствием, хотя чего-то нового в этом всем для меня, очевидно, не было. Однако проблемы -- очень странный выбор языка приложения в пользу Си или гавнокодинг на плюсах -- увы, и по сей день сверхактуальны, поэтому упрекнуть Страуса в том, что он в очередной раз рассказывал нам все эти вещи -- сложно.

Надо продолжать рассказывать, на примерах типа того же qsort(), о том, что нет ни одного повода выбрать Си в качестве языка для своего приложения, а ведь на наших глазах такую ошибку совершали граждане, создавшие, к примеру, git или nginx. В мире по прежнему полно мудачья, которое думает, что пишет на C++, при этом используя printf() или тыкая в каждую функцию delete (см. истории вроде этой). Скажу больше, в роли таких тотально безграмотных товарищей часто выступают гуру, вроде Джона Кармака (тыц)... Поэтому все, что остается делать понимающим людям -- денно и нощно учить, пропагандировать, просвещать, заражать личным примером... 

Кстати, возвращаясь к теме умных указателей, отец C++ учит нас -- всегда используйте умные указатели, а именно -- unique_ptr и иногда shared_ptr. 



STL11: Magic && Secrets

Докладчик -- Stephan T. Lavavej (можно просто STL, мыло у чувака stl@microsoft.com), главный в корпорации добра по стандартной библиотеке плюсов. 
Парень молодой, на внешность совсем не русский, и в придачу одноглазый (какая-то очень редкая патология, которую он приобрел еще при рождении). На мой взгляд, из трех отсмотренных докладов этот самый интересный и зажигательный. Наверное, не последнюю роль в этом играет тот факт, что Стефан прямо таки фонтанирует энергией, он полон искреннего энтузиазма по поводу того, чем он занимается в жизни, ему это интересно, ему это очень нравится. 

Доклад можно разделить условно на две части.

Первая часть -- о некоторых трюках при реализации STL. К примеру о том, что shared_ptr лучше создавать через make_shared, потому что в этом случае shared_ptr использует хитрую оптимизацию, когда и сам объект, хранимый в указателе, и служебная часть указателя (содержащая вещи, типа счетчика ссылок) хранятся в едином блоке памяти, что позволяет сэкономить одно лишнее обращение к куче. (Дело в том, что оптимизация shared_ptr очень важна для Microsoft, так как это один из ключевых элементов нового API для Windows -- WinRT, который максимально плотно использует указатели с подсчетом ссылок, прозрачно встроенные в нестандартное расширение C++ используемое для работы с этим API.)
Также было рассказано о целой части нововведений, которые были сделаны для того, чтобы выжать максимум выгоды из появления в языке move семантики. 


Вторая часть доклада -- о том, как один из коллег Стефана запилил в выходные дни поддержку for range в компилятор VC11 (очевидно, чувак точно такой же помешанный энтузиаст, как и сам докладчик; это правильно, именно такие люди, для которых хобби не отделимо от работы, есть самые лучшие сотрудники в любой компании). 

Проблема заключается в том, что Microsoft серьезно потеряла темп в вопросе поддержки C++11 в своем компиляторе, и это имея в своих рядах председателя комитета по стандартизации. Я с ноября месяца собираюсь, блин, написать большой пост по этой теме, которая очень плотно соприкасается с темой выхода Windows 8, да все руки не доходят... Об унылости VC11 в плане поддержки новых фич языка можно почитать, например, тут, в блоге разработчиков Visual C++. 
Так вот, Стефан рассказал о том, какими героическими усилиями команда пыталась пропихнуть эту фичу в бета релиз (сильно сопротивлялось QA, заявляя, что у них просто нет ресурсов на тестирование такого нововведения), о том, что пока что не будет ее поддержки в IntelliSense (т.к. его разработкой занимается совсем другая команда), а также о некоторых интересных нюансах реализации этого аспекта языка. 
Итого -- for range в VC11 все-таки будет. 

На этом все, собираюсь посмотреть и остальные доклады. Если вам интересно знать мое мнение по их поводу -- чиркните пару строчек в комментариях, мне будет приятно (смайлик). 

No comments:

Post a Comment