Введение
Языки программирования Cilk, Cilk++, Cilk Plus и OpenCilk – это языки программирования общего назначения, предназначенные для многопоточных параллельных вычислений. Они основаны на языках программирования C и C++, расширяя их конструкциями для выражения параллельных циклов и модели "ветвление-соединение". Изначально разработанный в 1990-х годах в Массачусетском технологическом институте (MIT) группой Чарльза Э. Лейзерсона, Cilk впоследствии был коммерциализирован компанией Cilk Arts под названием Cilk++. Эта компания была затем приобретена Intel, которая повысила совместимость с существующим кодом на C и C++, назвав результат Cilk Plus. После прекращения Intel поддержки Cilk Plus в 2017 году, MIT вновь разрабатывает Cilk в виде OpenCilk.
Cilk, Cilk++, Cilk Plus and OpenCilk are general purpose programming languages designed for multithreaded parallel computing. They are based on the C and C++ programming languages, which they extend with constructs to express parallel loops and the fork–join idiom. Originally developed in the 1990s at the Massachusetts Institute of Technology (MIT) in the group of Charles E. Leiserson, Cilk was later commercialized as Cilk++ by a spinoff company, Cilk Arts. That company was subsequently acquired by Intel, which increased compatibility with existing C and C++ code, calling the result Cilk Plus. After Intel stopped supporting Cilk Plus in 2017, MIT is again developing Cilk in the form of OpenCilk.
"Цильк" и "Цильк++"
До 2006 года рынок Cilk был ограничен областью высокопроизводительных вычислений. Появление многоядерных процессоров в массовых вычислительных системах означало, что ежегодно поставлялись сотни миллионов новых параллельных компьютеров. Компания Cilk Arts была основана для использования этой возможности: в 2006 году Лейзерсон запустил Cilk Arts с целью создания и вывода на рынок современной версии Cilk, отвечающей коммерческим потребностям нового поколения программистов. В октябре 2007 года компания завершила раунд венчурного финансирования серии А, а ее продукт, Cilk++ 1.0, был выпущен в декабре 2008 года. Cilk++ отличался от Cilk несколькими аспектами: поддержкой C++, поддержкой циклов и гиперобъектами – новой конструкцией, разработанной для решения проблем гонок данных, возникающих при параллельном доступе к глобальным переменным. Cilk++ являлся проприетарным программным обеспечением. Как и его предшественник, он был реализован в виде компилятора, преобразующего Cilk в C++. Он поддерживал компиляторы Microsoft и GNU.
Интел Чилк Плюс
31 июля 2009 года компания Cilk Arts объявила на своем веб-сайте, что ее продукты и инженерная команда теперь являются частью Intel Corp. В начале 2010 года веб-сайт Cilk по адресу www.cilk.com начал перенаправлять на веб-сайт Intel (по состоянию на начало 2017 года оригинальный веб-сайт Cilk больше не доступен). Intel и Cilk Arts объединили и развили технологию, что привело к выпуску Intel Cilk Plus в сентябре 2010 года. Cilk Plus использует упрощения, предложенные Cilk Arts в Cilk++, чтобы устранить необходимость в некоторых из исходных ключевых слов Cilk, добавляя возможность порождать функции и работать с переменными, участвующими в операциях сведения. Cilk Plus отличается от Cilk и Cilk++ добавлением расширений для массивов, включением в коммерческий компилятор (от Intel) и совместимостью с существующими отладчиками. Cilk Plus был впервые реализован в компиляторе Intel C++ с выпуском компилятора Intel в Intel Composer XE 2010. Реализация с открытым исходным кодом (под лицензией BSD) была предоставлена Intel в GNU Compiler Collection (GCC), которая включила поддержку Cilk Plus в версии 4.9, за исключением ключевого слова, которое было добавлено в GCC 5.0. В феврале 2013 года Intel объявила о создании форка Clang с поддержкой Cilk Plus. Компилятор Intel, но не реализации с открытым исходным кодом, поставляется с детектором гонок данных и анализатором производительности. Позже Intel прекратила поддержку Cilk Plus, рекомендуя своим пользователям вместо этого использовать OpenMP или собственную библиотеку TBB Intel для задач параллельного программирования.
Различия между версиями
В оригинальной реализации MIT Cilk первое ключевое слово Cilk – это `cilk`, которое определяет функцию, написанную на Cilk. Поскольку процедуры Cilk могут напрямую вызывать процедуры C, но процедуры C не могут напрямую вызывать или порождать процедуры Cilk, это ключевое слово необходимо для различения кода Cilk и кода C. Cilk Plus снимает это ограничение, а также упраздняет ключевое слово `cilk`, позволяя функциям C и C++ вызывать код Cilk Plus и наоборот.
Осуждение Cilk Plus
В мае 2017 года был выпущен GCC 7.1, в котором поддержка Cilk Plus была помечена как устаревшая. В сентябре 2017 года компания Intel объявила о прекращении поддержки Cilk Plus с выходом Intel Software Development Tools в 2018 году.
Открытый шелк
После прекращения поддержки Cilk Plus компанией Intel, MIT продолжил разработку Cilk в реализации OpenCilk, сосредоточившись на ответвлении LLVM/Clang, теперь известном как "Tapir". OpenCilk остаётся в основном совместимым с Intel Cilk Plus. Первая стабильная версия была выпущена в марте 2021 года.
Языковые особенности
Принцип, положенный в основу разработки языка Cilk, состоит в том, что программист должен отвечать за выявление возможностей для параллельного выполнения, определяя элементы, которые можно безопасно выполнять параллельно. Затем решение о том, как фактически распределить работу между процессорами, должно быть оставлено среде выполнения, в частности, планировщику, во время исполнения. Благодаря такому разделению ответственности программа Cilk может выполняться без изменений на любом количестве процессоров, включая один.
Входные
Два оставшихся ключевых слова Cilk немного сложнее и касаются использования входов (inlets). Обычно, когда процедура Cilk запускается, она может вернуть свои результаты родительской процедуре только путем сохранения этих результатов в переменную в области видимости родительской процедуры, как мы присваивали результаты вызовов процедур в примере переменным x и y. Альтернативой является использование входа. Вход – это функция, определенная внутри процедуры Cilk, которая обрабатывает результаты возвращаемого вызова процедуры. Одна из основных причин использования входов заключается в том, что все входы процедуры гарантированно выполняются атомарно по отношению друг к другу и к родительской процедуре, что позволяет избежать ошибок, которые могли бы возникнуть, если бы несколько возвращающихся процедур одновременно пытались изменить одни и те же переменные в области видимости родительской процедуры. Ключевое слово "inlet" определяет функцию, объявленную внутри процедуры, как вход. Ключевое слово "abort" может использоваться только внутри входа; оно сообщает планировщику, что любые другие процедуры, запущенные родительской процедурой, могут быть безопасно прерваны. Входы были удалены при переходе от Cilk к Cilk++, и отсутствуют в Cilk Plus.
Элементарные функции
В Cilk Plus элементарная функция — это обычная функция, которую можно вызывать как со скалярными аргументами, так и с элементами массива параллельно. Они аналогичны ядрам OpenCL.
#pragma simd (программа для сигнала)
Эта прагма предоставляет компилятору возможность векторизовать цикл, даже если автоматическая векторизация может не сработать. Это самый простой способ принудительно включить векторизацию.
Кража работ
Планировщик Cilk использует политику, называемую "кражей работы", для эффективного распределения выполнения процедур между несколькими процессорами. Опять же, проще всего понять это, если сначала рассмотреть, как код Cilk выполняется на однопроцессорной машине. Процессор поддерживает стек, в который он помещает каждый кадр, который необходимо приостановить для обработки вызова процедуры. Если он выполняет fib(2) и сталкивается с рекурсивным вызовом fib(1), он сохраняет состояние fib(2), включая его переменные и точку, где код приостановил выполнение, и помещает это состояние в стек. Он не снимет приостановленное состояние со стека и не возобновит выполнение, пока вызов процедуры, вызвавший приостановку, и любые процедуры, вызванные этой процедурой в свою очередь, не будут полностью выполнены. При использовании нескольких процессоров ситуация, конечно, меняется. Каждый процессор по-прежнему имеет стек для хранения кадров, выполнение которых было приостановлено; однако эти стеки больше похожи на двусторонние очереди (деки), поскольку приостановленные состояния могут быть удалены с любого конца. Процессор может удалять состояния из своего стека только с того конца, с которого он их помещает; однако любой процессор, который в данный момент не работает (завершил свою работу или еще не получил задание), случайным образом выберет другой процессор через планировщик и попытается "украсть" работу с противоположного конца его стека, которую ворующий процессор затем сможет начать выполнять. Состояния, которые подвергаются краже, – это те, которые процессор, у которого они были украдены, выполнил бы в последнюю очередь.