10/7/2006 3:28:00 AM

Этот постинг - своего рода фантастика. Которая, наверное, сейчас начинает воплощаться в реальность.

Давайте посмотрим на вектор развития современной компьютерной техники.
Времена лозунгов "покупатель платит за частоту" прошли вместе с гонкой за этой самой частотой, а проблема повышения производительности осталась.
И решение этой проблемы тоже уже ни для кого не секрет. Вот, например, Intel продемонстрировали 80-ти ядерный процессор. Конечно, "серийный" вариант этого дела увидит мир не в этом году, и даже не в следующем, но вот однодерных процессоров уже, кажется, никто не производит... Уже совсем-совсем скоро выйдет десктопный вариант 4х-ядерного процессора.. Там и 8 не за горами... Ну, а про планируемые (и уже продемонстрированные) 80 я уже сказал.

Что дает нам такая многоядерность?
Можно запустить параллельно две задачи, выделив каждой по процессору, скажете вы. А если задач больше - то все равно, два процессора справятся быстрее. Все верно. Но подумайте вот о чем: а что, если процессора не два, а четыре? А восемь? А нужно ли вам четыре или восемь одновременно загруженных задач, каждая из которых жрет по процессору? Ну хорошо, а восемьдесят?? Сможете ли вы, обладая 4-8-80ти-процессорной машиной эффективно использовать ее мощь? Куда девать все это хозяйство, если ваша задача ресурсоемкая, но одна??

И вот к чему я клоню: использовать возможности систем будущего настоящего можно только при наличии возможности распараллелить задачу. Это когда, например, летящая пуля обсчитывается на одном процессоре, бегущий монстр - на другом, льющаяся кровища - на третьем.. Все классно. Но.

Что это означает для нас, разработчиков?
А означает это одну простую вещь. Код придется писать так, чтобы он был масштабируемым. Чтобы запустив его на машине с двумя ядрами мы получили одно быстродействие, а на машине с 80-ю ядрами - в 40 раз большее. Это означает, что наш код обязан будет поддерживать параллельные вычисления. Обязан просто потому, что он нафиг никому будет не нужен, если не будет использовать все преимущества уже сегодняшних многоядерных технологий.
Это, кстати, еще реалии. Теперь будет почти фантастика :) А именно: требования к языку программирования, к компилятору, к платформе, которая будет эффективна в сложившихся обстоятельствах.

Понятно, что с господствующим ныне подходом мы практически не в состоянии написать программу, способную оптимально масштабироваться.
Да, с C++ мы имеем сейчас технологию, где используя прагма-директиву, которой можем сказать компилятору: "я гарантирую, что вот этот вот цикл можно распараллелить, так как его итерации не зависят друг от друга". Бах - каждая итерация цикла выполняется параллельно, супер!
НО: много ли вы найдете мест в вашей программе, где итерации независимы? Да, найдете. Ну и что? Есть еще 99% кода, который вы не сможете поменять.

Отсюда первый вывод:
Каждая операция должна считаться независимой и распараллеливаемой, если не указано обратное. То есть, подход, обратный описанному мною выше.

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

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

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

Отсюда третий вывод:
Состояние должно быть минимизировано. В идеале - искоренено совсем. Вот так.

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

И языки такие есть. Называются функциональными. С их помощью легко соблюсти все эти правила просто потому, что такова их природа.
Функциональными они называются потому,что функция принимает в качестве параметра функцию, которая своими параметрами имеет другие функции и т.д. Уравнения. Получается этакое дерево функций, параллельные ветки которых абсолютно независимы друг от друга.
Так, как ветки независимы, то они могут выполняться в любом порядке, на любом процессоре. Просто не нужно никакой синхронизации.
Да и поведение функций, можно сказать, "примитивно". Они принимают функции-значения в качестве параметров и возвращают функцию в качестве результата. Никакого тебе состояния. Легко мыть, лекго хранить. Легко дебагить, легко тестировать. Одинаковые входные данные => одинаковый результат. Никакого состояния.

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

Еще один немаловажный аспект: в данном функциональном подходе возможно математически предсказать результат той или иной операции. Предсказуемость поведения - великая вещь. Легко дебагить, легко тестировать, я уже говорил.

Мы часто обсуждаем на работе эту проблему. Правда, в еще более "фантастических" величинах. :)
Например, есть мнение, что в свете указанного подхода, такое понятие, как "поток" (thread) должно вообще исчезнуть для разработчиков. Это работа операционной системы - выполнить параллельную задачу наиболее эффективно. Если ей нужен поток - пусть сделает поток. Это не мое дело. Я со своей стороны гарантирую, что этот кусок работы можно выполнить параллельно. Этого достаточно.
Или, есть мнение, что один, отдельный, поток гда GUI тоже становится пережитком. В самом деле, что мешает рисовать кнопку в одном потоке, чекбокс в другом, а дропдаун - в третьем? Опять же, совершенно прозрачно для программиста, который и понятия не имеет, что там за потоки и сколько их. Это тоже проблема операционной системы.

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

Так получается, что функциональные языки - это наше, девелоперское, будущее.

Заключение.
Так что же, пора бросаться писать с использованием функциональных языков? Я думаю, все же нет. Спрос на параллельные решения будет повышаться, да. Функциональные языки наиболее подходят для таких решений - да. Но я думаю, что у нас, у разработчиков еще есть добрый пяток (?) лет на раскачку. Разработчику проще. Будет спрос - будет предложение. А пока эта область - особая. Пока необходимость параллельных вычислений не назрела. Пока еще очень узок круг продуктов, построенных на этом подходе. Пока еще недостаточен спрос, я хочу сказать.

Гораздо сложнее ситуация у менеджеров :) У тех, кто сейчас или в ближайшем будущем должен создавать, проектировать, вкладывать в системы; кому предстоит еще годы поддерживать эти системы; кто не может игнорировать тенденции "многоядерности", кто хочет (и должен, иначе где смысл?) использовать любую возможность для повышения производительности..
У них более сложная задача. Им нужно решить, как быть в подобной ситуации.
Потому, что игнорировать ее уже нельзя.

P.S. пока писал эту статью, мне прислали вот эту заметку. Коротко: протестирован четырехъядерный процессор Intel. Сегодня. Тот самый, о котором я сказал "вот-вот выйдет" в начале постинга. Понеслось.
Что будет, если приложение, которое вы пишете сейчас, запустить на такой машине? Не придется ли кусать локти от того, что оно лишь способно работать в 3 раза медленнее, чем могло бы? ;)

[UPDATE] А ведь процессор может поддерживать "аппаратно" не один поток на ядро, а два. То есть, 4 ядра = 8 эффективных потоков.

Tags:

Comments (2) -

10/9/2006 11:03:43 PM

void

Интересная мысль. Но вот честно говоря не представляю как какую-нибудь мало-мальски серьезную задачу реализовать на функциональном языке?

void

10/10/2006 6:24:54 AM

Не представляешь? SmileПочему? Уже не один год разрабатывают.
Кстати знаешь, что члены комитета стандартизации С++ засуетились? Фьючерсы вводить в стандарт хотят. Потому как проблема паралеллизма пока решена только в С++, и то кое-как (те же фьючерсы в бусте, например). А фьючерсы - они, можно сказать, нативны в функциональных языках.
Тот же Herb Sutter уже который раз выступает по этому поводу. Это член коммитета стандартизации и человек, отвечающий за С++ в Майкрософт. Так вот он же заявляет, что нормальное распараллеливание невозможно без неизменяемых "переменных", например. Что тоже норма для функционального языка (оно так по умолчанию)...
Они сейчас думают, как бы не потерять возможность поддержки уже существующего императивного кода. Оттого и вводят фьючерсы всякие.
Но такая смесь - они сами говорят - эффективней, чем ничего. Но писать станет сложнее - нужно думать много о распараллеливании.

А вот для шарпов, например, нет ничего Smile То есть, все еще сложнее, чем кажется Smile Более того, есть наметки грешить на сборщик мусора при многоядерной системе... Что-то такое промелькивало...

Alexey Raga

Comments are closed

Powered by BlogEngine.NET 2.5.0.6

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Twitter


Recent posts

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012

Sign in