Бесплатного супа больше не будет

    Фундаментальный поворот к параллелизму в программировании

    Автор: Герб Саттер
    Перевод: Александр Качанов

    The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software
    (By Herb Sutter)

    Ссылка на оригинал статьи: www.gotw.ca/publications/concurrency-ddj.htm

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

    Данная статья достаточно старая. Ей уже 7 лет, если считать с момента ее первой публикации в начале 2005 года. Помните об этом, когда будете читать перевод, так как многие вещи, которые для вас уже стали привычными, для автора статьи в 2005 году были в новинку и только-только появлялись.


    В ваши двери стучится крупнейшая революция в области разработки программного обеспечения со времен революции ООП, и зовут ее Параллелизм.


    Данная статья впервые была опубликована в журнале «Dr. Dobb's Journal» в марте 2005 года. Более короткая версия этой статьи была опубликована в журнале «C/C++ Users Journal» в феврале 2005 года под названием «Революция параллелизма».

    Обновление: график тенденций роста процессоров был обновлен в августе 2009 года. К нему были добавлены новые данные, которые показывают, что все предсказания данной статьи сбываются. Остальной текст данной статьи остался без изменений в том виде, как он был опубликован в декабре 2004 года.

    Бесплатного супа скоро больше не будет. Что вы будете делать по этому поводу? Что вы сейчас делаете по этому поводу?

    У ведущих производителей процессоров и архитектур, начиная от Intel и AMD и кончая Sparc и PowerPC, большей частью исчерпаны традиционные возможности увеличения производительности. Вместо того, чтобы и дальше увеличивать частоту процессоров и их линейную пропускную способность, они массово обращаются к гиперпоточным и многоядерным архитектурам. Обе эти архитектуры уже присутствуют в сегодняшних процессорах. В частности современные процессоры PowerPC и Sparc IV являются многоядерными, а в 2005 году к течению присоединятся Intel и AMD. Кстати, крупной темой форума In-Stat/MDR Fall Processor Forum, прошедшего осенью 2004 года была как раз тема многоядерных устройств, так как именно там множество компаний представило свои как новые так и обновленные многоядерные процессоры. Не будет преувеличением сказать, что 2004 год стал годом многоядерности.

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

    Пожалуй, бесплатный суп закончился уже как год или два назад. Мы только сейчас стали это замечать.

    Бесплатный суп производительности


    Вы наверное слышали такую интересную поговорку: «Сколько бы Энди не выдал, Билл заберет все» («Andy giveth and Bill taketh away»)? (прим.переводчика — Имеется в виду Энди Гроув — руководитель компании Intel, и Билл Гейтс — руководитель компании Microsoft). Сколько бы процессоры не увеличивали скорость своей работы, программы всегда придумают, на что потратить эту скорость. Сделайте процессор в десять раз быстрее, и программа найдет в десять раз больше работы для него (либо, в некоторых случаях, позволит себе выполнять ту же самую работу в десять раз менее эффективно). Большинство приложений на протяжение нескольких десятилетий работали все быстрее и быстрее, не делая для этого абсолютно ничего, даже без выпуска новых версий или коренных изменений в коде. Просто изготовители процессоров (в первую очередь) и производители памяти и жестких дисков (во вторую очередь) всякий раз создавали все новые и новые, все более быстрые компьютерные системы. Тактовая частота работы процессора не единственный критерий оценки его производительности, и даже не самый правильный, но тем не менее он говорит о многом: мы видели, как на смену 500 МГц-овым процессорам пришли процессоры с тактовой частотой 1ГГц, а за ними – 2ГГц-овые процессоры и так далее. Так что сейчас мы переживаем этап, когда вполне рядовым является процессор с тактовой частотой 3 ГГц.

    Теперь зададимся вопросом: Когда же закончится эта гонка? Вроде бы Закон Мура (Moore’s Law) предсказывает экспоненциальный рост. Ясно, что такой рост не может продолжаться вечно, он неизбежно упрется в физические пределы: ведь с годами скорость света быстрее не становится. Значит рост рано или поздно замедлится и даже остановится. (Маленькое уточнение: Да, Закон Мура говорит главным образом о плотности транзисторов, но можно сказать, что экспоненциальный рост наблюдался и в такой области, как тактовая частота. А в других областях рост был даже еще большим, например, рост емкостей накопителей, но это тема отдельной статьи.)

    Если вы – разработчик программного обеспечения, скорей всего вы уже давно расслабленно едете на волне увеличения производительности настольных компьютеров. Ваша программа медленно работает при выполнении какой-то операции? «Чего волноваться?», скажете вы, — «завтра выйдут еще более быстрые процессоры, а вообще программы работают медленно не только из-за медленного процессора и медленной памяти (например, еще из-за медленных устройств ввода-вывода, из-за обращений к базам данных)». Верный ход мыслей?

    Вполне верный. Для вчерашнего дня. Но абсолютно неверный для обозримого будущего.

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

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

    Увеличение тактовой частоты означает увеличение скорости. Если увеличить скорость работы процессора, это более менее приведёт к тому, что он будет выполнять тот же самый код быстрее.

    Оптимизация исполнения кода программы означает выполнение большего объема работы за один такт. Сегодняшние процессоры оснащены очень мощными инструкциями, а еще они выполняют разные оптимизации, от тривиальных до экзотических, включая конвейерное исполнение кода, предугадывание ветвлений (branch predictions), исполнение нескольких инструкций за один и тот же такт и даже выполнение команд программы в ином порядке (instructions reordering). Все эти технологии были придуманы для того, чтобы код исполнялся как можно лучше и/или как можно быстрее, чтобы выжать как можно больше из каждого такта, сводя задержки к минимуму и выполняя больше операций за каждый такт.

    Небольшое отступление по поводу выполнения инструкций в ином порядке (instruction reordering) и моделях памяти (memory models): хочу отметить, что под словом «оптимизациями» я имел в виду нечто на самом деле большее. Эти «оптимизации» могут менять смысл программы и приводить к результатам, которые будут противоречить ожиданиям программиста. Это имеет огромное значение. Разработчики процессоров не безумцы, и в жизни они мухи не обидят, им и в голову не придет портить ваш код… в обычной ситуации. Но в течение последних лет они решились на агрессивные оптимизации с единственной целью: выжать еще больше из каждого такта процессора. При этом они прекрасно понимают, что эти агрессивные оптимизации ставят под угрозу семантику вашего кода. Что ж, это они из вредности так делают? Вовсе нет. Их стремление – это реакция на давление рынка, который требует все более и более быстрых процессоров. Это давление настолько велико, что такое увеличение скорости работы вашей программы ставит под угрозу её корректность и даже её способность вообще работать.

    Приведу два наиболее ярких примера: изменение порядка операций записи данных (write reordering) и порядка их чтения (read reordering). Изменение порядка операций записи данных приводит к таким удивительным последствиям и сбивает с толку стольких программистов, что эту функцию приходится обычно отключать, так как при ее включении становится слишком трудно правильно судить о том, как будет вести себя написанная программа. Перестановка операций чтения данных тоже может привести к удивительным результатам, но эту функцию обычно оставляют включенной, так как здесь особенных трудностей у программистов не возникает, а требования к производительности операционных систем и программных продуктов заставляют программистов идти хоть на какой-то компромис и скрепя сердце выбирать меньше из «зол» оптимизации.

    Наконец, увеличение размера встроенного кэша означает стремление как можно реже обращаться к оперативной памяти. ОЗУ компьютера работает намного медленнее, чем процессор, поэтому лучше всего размещать данные как можно ближе к процессору, чтобы не бегать за ними в ОЗУ. Самое близкое – это хранить их на том же кусочке кремния, где находится сам процессор. Рост размеров кэш-памяти за последние годы был просто ошеломительным. Сегодня уже никого нельзя удивить процессорами со встроенной кэш-памятью 2-го уровня (L2) в 2Мб и более. (Из трех исторических подходов по увеличению производительности процессоров, рост кэш-памяти будет единственным перспективным подходом на ближайшее будущее. Еще немного о важности кэш-памяти я поговорю несколько ниже.)

    Хорошо. К чему я это все?

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

    Разумеется и компиляторам прошлось поспевать за процессорами; вам время от времени приходилось перекомпилировать ваше приложение, выбирая определенную модель процессора как минимально приемлимую, чтобы извлечь пользу из новых инструкций (например, MMX, SSE), новых функций и новых характеристик. Но в целом даже старые программы всегда работали значительно быстрее на новых процессорах, чем на старых, даже без всякой перекомпилляции и использования новейших инструкций новейших процессоров.

    Как прекрасен был этот мир. К сожалению, этого мира больше не существует.

    Препятствия, или почему мы не видим 10ГГц-вых процессоров


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

    Похожий график можно создать и для других процессоров, но в данной статье я собираюсь воспользоваться данными по процессорам Intel. На рис.1 представлен график, отображающий, когда какой процессор Intel был представлен на рынке, его тактовая частота и количество транзисторов. Как видите, количество тразисторов продолжает расти. А вот с тактовой частотой у нас проблемы.

    image

    Обратите внимание, что в районе 2003 года график тактовой частоты резко и странно отклоняется от привычной тенденции непрерывного роста. Между точками я провел линии, чтобы была более понятна тенденция; вместо того, чтобы продолжать расти, график неожиданно становится горизонтальным. Рост тактовой частоты дается все большей и большей ценой, и физических препятствий на пути роста не одно, а множество, например нагрев процессоров (слишком много выделяется тепла и его слишком трудно рассеивать), потребление энергии (слишком высоко) и паразитная утечка тока.

    Краткое отступление: посмотрите, какова тактовая частота процессора на вашем компьютере? Может быть 10ГГц? Процессоры Intel достигли уровня 2ГГц давным давно (в августе 2001 года), а если бы тенденция роста тактовой частоты, существовавшая до 2003 года, продолжалась, то сейчас — в начале 2005 года — должны были бы появиться первые процессоры Pentium с частотой 10ГГц. Осмотритесь, видите ли вы их? Более того, процессоров с такой тактовой частотой ни у кого даже в планах нет, и мы даже не знаем, когда такие планы появятся.

    Ну, а что насчет 4ГГц-вого процессора? Уже сейчас есть процессоры с частотой 3.4ГГц, значит 4ГГц не за горами? Увы, мы не можем дотянуться даже до 4ГГц. В середние 2004 года вы наверное помните, как компания Intel перенесла выпуск 4ГГц-ового процессора на 2005 год, а потом осенью 2004 года официально объявила о полном отказе от этих планов. На момент написания данной статьи Intel планирует продвинуться чуть вперед, выпустив в начале 2005 года процессор с частотой 3.73ГГц (на рис.1 на графике роста частоты это самая верхняя точка), но можно сказать, что гонка за герцами закончена, по крайней мере на сегодняшний момент. В будущем компания Intel и большинство производителей процессоров будут добиваться роста другими способами: все они активно стали обращаться к многоядерным (multicore) решениям.

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

    БСНБ: Закон Мура и следующие поколения процессоров


    «Бесплатного супа не бывает (БСНБ)» — Р.А.Хайнлайн, «Луна — строгая хозяйка»

    Значит ли это, что Закон Мура больше не действителен? Самое интересное, что ответ будет «нет». Разумеется, как и всякая экспоненциальная прогрессия Закон Мура когда-то перестанет работать, но по всей видимости на ближайшие несколько лет Закону ничего не угрожает. Несмотря на то, что конструкторы процессоров больше не могут увеличивать тактовую частоту, число транзисторов в их процессорах продолжает расти взрывным темпом, и в ближайшие годы рост по Закону Мура будет продолжаться.

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

    В ближайшем будущем, точнее в ближайшие несколько лет, прирост производительности в новых процессорах будет достигаться тремя главными способами, лишь один из которых остался из прежнего списка. А именно:
    • гиперпоточность (hyperthreading)
    • многоядерность (multicore)
    • кэш (cache)

    Гиперпоточность (Hyperthreading) это технология исполнения двух и более потоков (threads) параллельно в одном и том же процессоре. Гиперпоточные процессоры уже сейчас имеются на рынке, и они действительно позволяют выполнять несколько инструкций параллельно. Однако, несмотря на то, что у гиперпоточного процессора для выполненяи этой задачи имеется в наличии дополнительные аппаратные срества, например, дополнительные регистры, у него по прежнему всего один кэш, один вычислительные блок для целочисленной математики, один блок для операций с плавающей запятой и вообще по одному всего, что есть в наличии в любом простом процессоре. Считается, что гиперпоточность позволяет увеличивать производительность сносно написанных много-поточных программ на 5-15 процентов, а производительность отлично написанных многопоточных программ при идеальных условиях увеличивается аж на 40%. Неплохо, но это далеко не удвоение производительности, а однопоточные программы здесь ничего выиграть не могут.

    Многоядерность (Multicore) это технология размещения на одном и том же кристалле двух или нескольких процессоров. Некоторые процессоры, например SPARC и PowerPC, уже выпускаются в многоядерных вариантах. Первые попытки компаний Intel и AMD, которые должны реализоваться в 2005 году, отличаются друг от друга по степени интеграции процессоров, но функционально они очень схожи. Процессор от AMD будет иметь несколько ядер на одном кристалле, что приведет к большему выигрышу в производительности, в то время как первый многоядерный процессор Intel представляет собой всего лишь два сопряженных процессора Xeon на одной большой подложке. Выигрыш от такого решения будет таким же как от наличия двух-процессорной системы (только дешевле, так как на материнской плате не понадобится два сокета для установки двух чипов и дополнительных микросхемы для их согласования). В идеальных условиях скорость исполнения программ почти удвоится, но только у достаточно хорошо написанных многопоточных приложений. Однопоточные приложения не получат никакого прироста.

    Наконец, ожидается рост объемов встроенного кэша (on-die cache), по крайней мере в ближайшем будущем. Из всех трех тенденций, только эта приведет к росту прозводительности большинства существующих приложений. Рост размера встроенного кэша для всех приложений важен просто потому, что размер значит скорость. Доступ к ОЗУ обходится слишком дорого, и по большому счету хочется обращаться к ОЗУ как можно реже. В случае промаха кэша (cache miss), на извлечение данных из ОЗУ уйдет в 10-50 раз больше времени, чем на их извлечение из кэша. До сих пор людей это удивляет, поскольку было принято считать, что ОЗУ работает очень быстро. Да, быстро по сравнению с дисками и сетью, но кэш-память работает еще быстрее. Если весь объем данных, с которым предстоит работать приложению, помещается в кэше, мы — в шоколаде, а если нет, то в чем-то другом. Вот поэтому растущие размеры кэш-памяти спасут некоторые сегодняшние программы и вдохнут в них еще немного жизни на несколько лет вперед без каких-либо значительных переделок с их стороны. Как говорили во времена Великой Депрессии: «Кэша мало не бывает». («Cache is king»)

    (Краткое отступление: вот вам история, случившаяся с нашим компилятором, в качестве демонстрации утверждения «размер значит скорость». 32-битная и 64-битная версии нашего компилятора создаются из одного и того же исходного кода, просто при компиляции мы указываем, какой процесс надо создать: 32-битный или 64-битный. Ожидалось, что 64-битный компилятор должен работать быстрее на 64-битном процессоре, хотя бы потому, что у 64-битного процессора было намного больше регистров, а также имелись оптимизирующие функции по более быстрому выполнению кода. Все просто прекрасно. А что насчет данных? Переход на 64 разряда не поменял размеры большинства структур данных в памяти, за исключением, конечно, указателей, размер которых стал в два раза больше. Оказалось, что наш компилятор использует указатели намного чаще, чем какое-либо другое приложение. Так как размер указателей теперь стал 8 байт, вместо 4 байт, общий размер данных, с которым надо было работать компилятору, увеличился. Увеличение объема данных ухудшило производительность ровно настолько, насколько она улучшилась из-за более быстрого процессора и наличия дополнительных регистров. На момент написания этой статьи наш 64-битный компилятор работает с такой же скоростью, что и его 32-битный собрат несмотря на то, что собраны оба компилятора из одного исходного кода, а 64-битный процессор — более мощный, чем 32-битный. Размер значит скорость!)

    Воистину, кэш будет править балом. Так как ни гиперпоточность, ни многоядерность не увеличат скорость работы большинства сегодняшних программ.

    Так что же эти изменения в аппартном обеспечении значат для нас – программистов? Вы уже наверное поняли, каков будет ответ, так что давайте обсудим его и сделаем выводы.

    Мифы и реалии: 2 x 3ГГц < 6ГГц


    Если двух-ядерный процессор состоит из двух 3ГГц-овых ядер, значит мы получает производительность 6ГГц-ового процессора. Верно?

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

    Почему же нет? Во-первых, мы имеем издержки на согласование содержимого кэшей (cache coherency) двух процессоров (непротиворечивое состояние кэшей и разделяемой памяти), а также издержки на прочие взаимодействия. Сегодня двух- или четырех-процессорные машины не обгоняют по скорости в два или четыре раза своих однопроцессорных собратьев даже при выполнении многопоточных приложений. Проблемы остаются по сути теми же и в тех вариантах, когда вместо нескольких раздельных процессоров мы имеем несколько ядер на одном кристалле.

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

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

    Если ваше приложение — однопоточное, вы используете только одно ядро процессора. Конечно некоторое ускорение будет, так как операционная система или фоновое приложение будут исполняться на других ядрах, но как правило операционные системы не нагружают процессоры на 100%, так что соседнее ядро большей частью будет простаивать. (Или опять-таки, на нем будет крутиться троян или вирус)

    Значение для ПО: Очередная революция


    В конце 90-ых мы научились работать с объектами. В программировании произошел переход от структурного программирования к объектно-ориентированному, который стал самой значительной революцией в программировании за последние 20, а может даже и 30 лет. Были и другие революции, включая недавнее появление веб-сервисов, но за всю нашу карьеру мы не видели переворота более фундаментального и значительного по последствиям, чем объектная революция.

    До сегодняшнего дня.

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

    Слышу крики возмущения: «Параллелизм? Какая ж это новость!? Люди уже давно пишут параллельные программы». Верно. Но это лишь ничтожная доля программистов.

    Вспомните, что и объектно-ориентированным программированием люди занимались аж с конца 60-ых годов, когда вышел язык Simula. В то время ООП не вызвало никакой революции и не доминировало среди программистов. До наступления 90-ых годов. Почему именно тогда? Революция произошла в основном потому, что возникла нужда в еще более сложных программах, которые решали еще более сложные задачи и использовали все больше и больше ресурсов процессора и памяти. Для экономичной, надежной и предсказуемой разработки крупных программ сильные стороны ООП – абстракции и модульность – оказались очень кстати.

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

    Кстати, насчет шумихи: сколько раз нам объявляли, что мы стоим «на пороге очередной революции в области разработки ПО». Как правило те, кто это говорил, просто рекламировали свой новый продукт. Не верьте им. Новые технологии всегда интересны и даже порой оказываются полезными, но самые крупные революции в программировании производят те технологии, которые уже несколько лет присутствуют на рынке, тихонько набирают силу, пока в один прекрасный момент не происходит взрывной рост. Это неизбежно: революция может основываться только на достаточно зрелой технологии (у которой уже есть поддержка со стороны многих компаний и инструментариев). Обычно проходит лет семь, прежде чем новая технология программирования становится достаточно надежной, чтобы ее можно было бы широко применять, не наступая на грабли и глюки. В результате, настоящие революции в программировании, такие как ООП, производят те технологии, которые оттачивались годами, если не десятилетиями. Даже в Голливуде всякий актер, ставший в одну ночь суперзведой, до этого оказывается уже несколько лет играл в кино.

    Параллелизм (Concurrency) — это следующая великая революция в программировании. Существуют разные мнения экспертов о том, сравнится ли она с революцией ООП, но оставим эти споры ученым мужам. Для нас, инженеров, важно то, что параллелизм сравним с ООП по масштабности (что ожидалось), а также по сложности и по трудности освоения этой новой технологии.

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


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

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

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

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

    Всякий, кто берется за изучение параллелизма, в какой-то момент считает, что разобрался в нем полностью. Потом, столкнувшись с необъяснимым состояниями гонки (race conditions), вдруг осознает, что рано еще говорить о полном понимании. Далее, по мере того, как программист обучается навыкам работы с параллельным кодом, он обнаруживет, что необычных состояний гонки можно избежать, если код тщательно оттестировать, и переходит на второй уровень мнимого знания и понимания. Но во время тестирования, обычно ускользают те ошибки параллельного программирования, которые проявляются только на реальных многопроцессорных системах, где потоки исполняются не просто переключением контекста на одном процессоре, а там, где они выполняются действительно одновременно, вызывая новый класс ошибок. Так программист, который считал что теперь-то уж он точно знает, как пишутся параллельные программы, получает новый удар. Мне встречалось множество команд, чьи приложения отлично работали во время долгого усиленного тестирования и превосходно работали у клиентов, пока в один прекрасный день один из клиентов не устанавливал программу на многопроцессорную машину, и тут же то там то здесь стали вылазить необъяснимые состояния гонки и повреждения данных.

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

    При переходе от структурного программирования к объектно-ориентированному у программистов были точно такие же трудности (что такое объект? что такое виртуальная функция? для чего нужно наследование? И помимо всех этих «что» и «почему», самое главное – почему правильные программные конструкции являются действительно правильными?), что и сейчас – при переходе от последовательного программирования к параллельному (что такое «гонка»? что такое «взаимная блокировка» (deadlock)? от чего она происходит и как мне ее избежать? какие программные конструкции делают мою параллельную программу последовательной? почему надо подружиться с очередью сообщений (message queue)? И помимо всех этих «что» и «почему», самое главное – почему правильные программные конструкции являются действительно правильными?)

    Большинство сегодняшних программистов не разбирается в параллелизме. Точно так же 15 лет назад большинство программистов не понимало ООП. Но модели параллельного программированяи можно обучиться, особенно если мы хорошо освоим понятия передачи сообщений и блокировки (message- and lock-based programming). После этого параллельное программирование будет не труднее, чем ООП, и, надеюсь, станет вполне привычным. Просто подготовьтесь, что вам и вашей команде придется потратить некоторое время на переобучение.

    (Я намеренно свел параллельное программирование к понятиям передачи сообщений и блокировки. Существует способ писать параллельные программы без блокировок (concurrent lock-free programming), и этот способ поддерживается на уровне яыка лучше всего в Java 5 и по крайней мере в одном из известных мне компиляторов С++. Но параллельное программирование без блокировок намного труднее в освоении, чем программирование с блокировками. Большей частью оно понадобится только разработчикам системного и библиотечного ПО, хотя каждому программисту придется понять, как работают такие системы и библиотеки, чтобы извлечь из них пользу для своих приложений. Честно говоря, и программировать с блокировками тоже не так уж легко и просто.)

    Что все это значит для нас?


    ОК. Вернемся к тому, что все это значит для нас – программистов.

    1. Первое главное последствие, которое мы уже осветили, это то, что приложения должны стать параллельными, если вы хотите на все 100% использовать растущую пропускную способность процессоров, которые уже начали появляться на рынке и будут править бал на нем в последующие несколько лет. Например, компания Intel заявляет, что в недалеком будушем она создаст процессор из 100 ядер; однопоточное приложение сможет использовать только 1/100 мощи данного процессора.

    Да, не все приложения (или, если говорить точнее, важные операции, выполняемые приложением) можно распараллелить. Да, для некоторых задач, например компиляция, параллелизм походит почти идеально. Но для других – нет. Обычно в качестве контр-примера вспоминают ходячую фразу о том, что если у одной женщины уходит 9 месяцев, чтобы родить ребенка, это вовсе не значит, что 9 женщин смогут родить ребенка за 1 месяц. Вы наверное часто встречались с этой аналогией. Но заметили ли вы обманчивость этой аналогии? Когда вам в очередной раз упомянут о ней, задайте в ответ простой вопрос: «можно ли из этой аналогии заключить, что Задача Рождения Ребенка не поддается параллелизации по определению?» Обычно в ответ люди задумываются, а потом быстро приходят к заключению, что «да, эту задачу невозможно распараллелить», но это не совсем так. Разумеется, ее невозможно распараллелить, если наша цель — родить одного единственного ребенка. Но она великолепно поддается параллелизации, если мы ставим себе цель родить как можно больше детей (по одному ребенку в месяц)! Вот так, знание реальной цели может перевернуть все с ног на голову. Помните об этом принципе цели, когда решаете, стоит ли менять свою программу и каким образом это надо делать.

    2. Пожалуй, менее очевидным последствием является то, что скорей всего приложения все больше и больше будут тормозить из-за процессоров (CPU-bound). Разумеется, это произойдет не со всеми приложениям, а те, с которыми это может произойти, не станут тормозить буквально завтра. Тем не менее мы, пожалуй, достигли границы, когда приложения тормозили из-за систем ввода-вывода, или из-за обращения к сети или базам данных. В этих областях скорости становятся все выше и выше (слышали про гигабитный Wi-Fi?). А все традиционные способы ускорения процессоров себя исчерапали. Только подумайте: мы сейчас прочно застряли на скорости 3ГГц. Следовательно, однопоточные программы не станут работать быстрее, ну разве только за счет увеличения размеров кеша процессоров (уж хоть какая-то хорошая новость). Другие продвижения в этом направлении будут уже не такими большими, как мы привыкли раньше. Например, вряд ли инженеры-схемотехники отыщут новый способ, как заполнять работой конвейер процессора и не допускать его простаивания. Здесь все очевидные решения уже давно были найдены и реализованы. Рынок безпристанно будет требовать от программ большей функциональности; кроме того новым приложениям придется обрабатывать все больше и больше данных. Чем больше функционала мы станем вводить в программы, тем скорее мы станем замечать, что программам не хватает мощности процессора, потому что они не параллельны.

    И здесь у вас будет два варианта. Первый, переделать свое приложение в параллельное, как уже было сказано выше. Либо, для самых ленивых, переписать код так, чтобы он стал более эффективным и менее расточительным. Что приводит нас к третьему выводу:

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

    4. И наконец, языки программирования и программные системы вынуждены будут хорошо поддерживать параллелизм. Язык Java, например, поддерживает параллелизм с самого своего рождения, хотя в этой поддержке и были сделаны ошибки, которые пришлось потом исправлять на протяжение нескольких релизов, чтобы многопоточные программы на Java работали правильно и эффективно. Язык С++ с давних пор использовался для написания мощных многопоточных приложений. Однако параллелизм в этом языке не приведен к стандартам (во всех ISO-стандартах языка С++ даже не упоминаются потоки, и сделано это намеренно), так что для его реализации приходится прибегать к различным платформенно-зависимым библиотекам. (Кроме того и поддержка параллелизма далеко не полна, например, статические переменные должны быть инициализированы только раз, для чего от компилятора требуется обозначить инициализацию блокировками, а многие компиляторы С++ этого не делают). Наконец, существует несколько стандартов параллелизма в С++, включая pthreads и OpenMP, и некоторые из них поддерживают даже два вида параллелизации: скрытую (implicit) и явную (explicit). Прекрасно, если такой компилятор, работая с вашим однопоточным кодом, сумеет превратить его в параллельный, отыскав в нем куски, которые могут быть распараллелены. Однако такой автоматизированный подход имеет свои пределы и не всегда дает хороший результат по сравнению с кодом, где параллелизм присутствует явно, заданный сами программистом. Главный секрет мастерства заключается в программировании с использованием блокировок, что освоить довольно трудно. Нам срочно требуется более продвинутая модель параллельного программирования чем та, что предлагают современные языки. Об этом подробнее я скажу в другой статье.

    В заключение


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

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

    Благодаря росту размеров кеша и еще немногим улучшениям в оптимизации исполнения кода бесплатный суп будет доступен еще некоторое время, но начиная с сегодняшего дня в его составе будет только одна вермишель и морковка. Все наваристые кусочки мяса будут в супе лишь за дополнительную плату – дополнительные усилия программиста, дополнительная сложность кода, дополнительное тестирование. Успокаивает то, что для большинства приложений эти усилия будут не напрасными, потому что они позволят на все 100% использовать экспоненциальный рост мощности современных процессоров.
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 81
      +7
      Я давно думал о том, что сколько может продолжаться такая вот гонка: процессоры мощнее — программы прожорливее. Но судя по статье не долго…
      +13
      Спасибо за перевод, крайне интересно.

      Увы сейчас есть следующая тенденция, был неоднократно свидетелем, время работы программиста стоит дороже машинного времени. Поэтому часто бывает такое что скорость разработки идет в ущерб качеству кода. Крайне печально, но во многих фирмах это практикуется (особенно на момент запуска продукта)
        +4
        Этой тенденции уже лет десять, как минимум.
          0
          Увы, десять лет назад мне было 12 и представление о состоянии программирования в то время у меня весьма смутные
            +2
            Ну мне по схожей причине тяжело судить о том, что было более 10 лет назад ;)
              0
              Спольски пишет, что уже в конце восьмидесятых Лотус проиграл Майкрософту именно потому, что пока первые оптимизировали, вторые сделали релиз, а к моменту релиза уже и железо подоспело.
              0
              Подозреваю, что гораздо больше, гораздо. Вспомните, сколько, к примеру, кода было написано на КОБОЛе. Хотя с точки зрения эффективности все эти вещи лучше было бы написать на более низкоуровневых языках.
              0
              Когда прога необычная или нишевая, приходится «прощупывать почву» силами внештатных бета-тестеров, и одновременно доводить ядро до более-менее оптимизированного состояния.
                +4
                Опытные программисты пишут качественный код даже если их ограничивают временными рамками, т.к. они осознают, что только качественный код можно написать и оттестировать быстро, но правда время таких программистов стоит намного дороже.
                  0
                  О чем и речь. Сейчас многим фирмам проще и дешевле взять говнокодеров чем профи.
                  +1
                  Нету тенденции. И нету печали.

                  Нужно понимать простую вещь — если ускорить программу выгодно (профита больше, чем стоит время программистов) — нужно ускорять. Если и так всё работает приемлимо и в перспективе не начнёт тормозить — то зачем заниматься лишней работой? Да и золотое правило инженерии «не трогай то, что работает» — никто не отменял))
                  +17
                  И ни слова про функциональное программирование.
                    +8
                    Кстати да, сидел и в подсознании крутилось: когда же заговорят о функциональном программировании
                      0
                      ФП — это круто, конечно. Но не мейнстрим. И боюсь, что никогда не станет: высокий порог входа, невозможность написать что-то большое и т.п. Например, создатели Котлина, рассказывая про Scala говорят, что под Scala невозможно сделать нормальную IDE.

                      Есть расхожее мнение, что «чисто функциональные программы не имеют side-эффектов, а поэтому хорошо параллелятся». Но это слова. На самом деле, хороший вопрос, что такое «чисто функциональная программа» и что такое «хорошо параллелятся». Хорошо — в смысле «корректно», это да. Но вот в смысле «эффективно» — не уверен.
                        +1
                        > И боюсь, что никогда не станет: высокий порог входа,

                          +2
                          > И боюсь, что никогда не станет: высокий порог входа,

                          Ничего высокого. Вон, в C# ввели толпу функциональщины, в Java введут.

                          > невозможность написать что-то большое и т.п

                          В этом месте можно смеяться в голос
                            0
                            для того, чтобы написать что-то большое с помощью ФП нужно где-то достать много Фпрограммистов, не? А их днём с огнём.
                              +1
                              Не обязательно много. В частности, из-за того, что количество кода сокращается :) А на тот же Erlang нормальные программисты переучиваются влет.

                              И да, «невозможно написать что-то большое» как минимум разбивается о существующую реальность
                                0
                                ок, а примеры приведите чего-нибудь большого, написанного в функциональной парадигме?
                                  +1
                                  erlanger.ru, в шапке — в описании языка ;)

                                  Наша внутренняя система — 350 тыс. строк на Erlang'е (эквивалент на каком-нить С++/Java сложно подсчитать). Достаточно большая?
                                    0
                                    большая, большая))
                        +6
                        Разве чисто функциональное программирование не является панацеей в данном случае? (Привет, Хаскель).
                          +9
                          Панацеей — нет. Предоставляет ли оно облегчённые абстракции для параллельного программирования — да.
                            +2
                            скорее erlang
                              +1
                              No Silver Bullet.

                              Сайд-эффектов не будет, это да. Но при этом всё равно придётся думать над тем, как эффективно распараллеливать задачи.

                              Плюс «чистое ФП» — сложно и дорого. Плюс под него нельзя сделать IDE. Знаете, сколько стоят программисты, которые умеют круто работать без IDE?

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

                              Язык — слабый помощник и там и там. Если нужно быстро считать или быстро гонять данные, то зависимость от платформы практически пропадает.
                                0
                                Согласен. Почему-то все считают и начинают говорить про разные языки программирования, но это ведь далеко не главное — какая разница на чём записать, главное что ты написал и что у тебя в голове.
                              +1
                              Статья больше подходит для мобильных разработчиков, особенно тех которые работают с нативным кодом. И кстати сейчас вроде на топовых шестиядерных Phenom можно до 6 ггц разогнать даже на воздушном охлаждении?
                                +1
                                Имелось в виду, что с распространением многоядерных процессоров, для мобильных разработчиков эта статья стала востребованой сейчас.
                                  0
                                  На воздухе до 4 ГГц разгоняется. 6 ГГц — это с жидким гелием и всем причитающимся.
                                  +1
                                  Спасибо, очень интересно!
                                    –8
                                    Когда я поставил первый двухядерный процессор, очень радовался тому, что можно запускать тяжёлую софтину, не подвешивая при этом систему.
                                    Теперь же быдлокодеры спешат осваивать параллелизм, чтобы их говнософтины смогли продолжать сжирать все ресурсы системы.
                                    И пусть 99,99% программ с пользой используют только 0,001% потребляемых ими ресурсов, почему-то никто не думает о том, как бы сделать их хоть немного полегче, главное — побольше абстракции в коде, побольше визуального мусора в интерфейсе вроде закруглённых уголков, прозрачных заголовков и прочих анимаций. Естественно, что всему этому уже не хватает одного процессора.

                                    Время быдлокодера стоит дороже машинного времени, ага. Но если бы быдлокодер писал качественный код, он бы и потреблял машинного времени ровно столько, сколько нужно, а не в 9000 раз больше.
                                      +5
                                      Сложность кода растёт с каждым годом. Писать корректный и эффективный multi-threaded код в разы сложнее, чем single-threaded. А как уже заметили выше, писать качественный код банально невыгодно в плане денег.
                                        –6
                                        Ещё бы. Сложность кода растёт, а задачи решаются те же (только хуже).

                                        корректный и эффективный multi-threaded код

                                        Не нужна многопоточность корректному и эффективному коду. Несколько потоков в одном процессе — дань быдлокодерам, которые не осилили асинхронную работу. В юниксах по началу не было многопоточности, хотя вычисления прекрасно распараллеливались и суперкомпьютеры работали. Несколько потоков выполнения в одном адресном пространстве — это низкоуровневый хак уровня ассемблера, крайне опасный и некрасивый.
                                          +2
                                          Есть задачи, которые хорошо параллелятся, например числодробление. Объясните, почему числодробилки не нужно многопоточить? Или вы предлагаете вместо тредов создавать отдельные процессы?
                                            –1
                                            это часто действительно хорошее решение, по-крайней мере в linux, где процессы не сильно дороже потоков и есть множество средств ipc. при этом код может получится проще и понятней
                                              +1
                                              Ну это просто поиск баланса между производительностью и безопасностью. Код будет проще и понятней, и даже безопасней, но менее производителен (насколько — нужно мерять). Тут уже зависит от задачи, где-то это решение применимо, где-то нет. Апач вон когда-то на каждый запрос по процессу рожал (сейчас не знаю как), потом перед ним стали ставить nginx'ы разнообразные, ибо накладно это было слишком.
                                                –1
                                                для многих числодробилок многопоточность-хорошее решение, если это не усложнит всё сильно и не приводит к ошибкам, гонкам и т.п. лучше более медленный код но корректрый и безопасный.
                                                несколько лет назад Линус был против появления многопоточности в линукс =)
                                                мы, ортодоксальные юникс-программисты, всегда предпочитали форкнуться, но времена меняются…
                                        +3
                                        Вместо того что бы выёбываться я бы советовал написать корректный многопоточный код для серьезной софтины (отличной от hello world) и выложить его на общее обозрение. Думается мне, вас бы говном быстро закидали.
                                          –3
                                            +2
                                            Не совсем. Это скорее «давайте обсуждать вкус устриц с теми, кто их ел».
                                        +3
                                        Программы всё больше и больше уходят в веб, то есть в браузерный яваскрипт. А значит будущее за новыми подходами, которые смогут, наконец, подружить браузер и скриптовую многопоточность.
                                        Первый шаг сделан: яваскрипт постепенно превращается из интерпретируемого языка в компилируемый.
                                        Дальше — вопрос логики и новых редакций стандартов.
                                        Вот где действительно развернётся будущая битва за производительность.

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

                                        С одной стороны, де-факто отсутствие разделяемой памяти упростит расширение на сколь угодно большое количество ядер. С другой стороны, появляется масса проблем, связанных как с самим подходом (задержки и прочее), так и увязкой этого с визуализацией DOM. Похоже, это дело браузеров будущего.
                                          +1
                                          Согласен, что все больше расчетов ведется на стороне клиента javascript — ом. Но серверная часть никуда не денется, и от нее тоже требуется производительность. Это я так, к тому, что на стороне клиента происходят довольно простые расчеты, как правило не требующие оперирования большими объемами данных, самое сложное обычно происходит на сервере, и сервер как правило нуждается в оптимизации больше чем клиент на javascript.
                                            0
                                            Мне кажется, что если сравнить количество строк кода веб-сервера с веб-браузером, то у последнего будет больше. Примерно так же и с объёмом данных, который обрабатывается, DOM — объёмная структура, да и если HTTP протокол без состояния, а вот состояние страницы поддерживать приходится. БД и визуальную среду в сравнение не включаем.
                                              0
                                              Количество строк на сервере или в браузере крайне сильно зависит от приложения. В туду листе будет много javascript-а, а в поисковике его может даже не быть. Тут не все однозначно, но есть однозначность такого плана, что сложные расчеты по обработке данных происходят обычно на сервере, т.к. передавать клиенту все данные может оказаться невозможным и недопустимым.
                                                0
                                                Имелось в виду количество строк исодников самого веб-сервера и веб-браузера, а не веб-приложений, что скорее всего коррелирует со сложностью расчётов.
                                            0
                                            Мне кажется, «отказ от классики» тут не поможет.Какой-нибудь MPI или Linda tuples уже не менее классика, чем семафоры. Там свои проблемы появляются, и общий вывод, как всегда, печален: В разных задачах разные подходы оказываются оптимальными.
                                            +2
                                            Нам срочно требуется более продвинутая модель параллельного программирования чем та, что предлагают современные языки. Об этом подробнее я скажу в другой статье.

                                            Будете ли вы переводить эту «другую статью»? было бы интересно её почитать. В крайнем случае, можете просто указать в тексте ссылку на неё?

                                            Мне кажется, что проблема описанная в статье в частности привела сегодня к развитию языков использующих акторы, которые позволяют писать очень многопоточные приложения достаточно легко по трудозатратам, только жалко что мейнстримом они таки не спешат становиться. Плюс мне очень бы хотелось что бы возродилось незаслуженно забытое на рубеже 90-х dataflow программирование, которое тоже позволяет достаточно легко писать очень многоптоточные приложения, вплоть до того что встаёт проблема слишком сильной параллельности (fine-grained).

                                            P.S.: На будущее, есть специальный тип поста, называется «перевод».
                                              +2
                                              честно говоря в оригинале статьи не было ссылки на «следующую статью». подозреваю, что статья была написана позднее а ссылка на нее не поставлена: скорей всего это вот эта статья:

                                              Software and the Concurrency Revolution,
                                              ACM Queue, September 2005
                                              queue.acm.org/detail.cfm?id=1095421
                                              0
                                              Прошу простить меня за такую точку зрения, но проблема в статье надуманная и никакой революции параллелизма не будет. Не так уж и много задач, требующих выжимания максимума производительности из одного процессорного устройства. Более того, серьёзный вычислительный процесс никогда не станет себя ограничивать рамками одного процессора/железа, пусть даже многоядерного. Может случиться так, что уже завтра задача потребует в 8 раз больше пиковых мощностей. Никто не будет ждать log2(8)*18 месяцев по закону Мура, что бы потом, наконец-таки, купить самую мощную топовую железку и получить 8-кратный рост производительности в пиках. Те, кому такие мощности действительно необходимы, уже давно параллелятся в облаках и прекрасно знакомы со всеми «прелестями» параллельного программирования.
                                                +4
                                                Не согласен. Главная задача параллелизма в бизнесе обрабатывать большие массивы данных. А количество данных очень быстро растет. И практически везде в бизнес-приложениях эта проблема стоит очень остро от Microsoft Excel до Sap. Так что то, что вчера работало на одном сервере, сегодня физически не может, из-за гораздо большего количества данных и эта проблема очень распространена.
                                                  0
                                                  Речь в статье про софт для простых смертных, а не про трудоёмкие научные вычисления, которые по возможности делают на суперкомпах и в облаках.

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

                                                  В общем и целом, от всё большего количества софта требуется способность эффективно использовать все ядра. Не знаю уж, «революция» это или нет, но факт остаётся фактом.
                                                    +1
                                                    Ха-ха, «не приближается к идеальной». Лучше бы сказать отдаляется от идеальной. У меня VS тормозит так, что я уже придумал, чем будут развлекать в аду черти программистов — они их будут заставлять использовать написанные ними программы на слабых компьютерах.
                                                  0
                                                  Спасибо, познавательная статья!
                                                  Многоядерные архитектуры, скорость света, параллелиз, обращаться к многоядерным (multicore) решениям… Как мне все это знакомо.
                                                  Жалко, что большинство мощных процессоров ожидает в лучшем случае участь кубика.
                                                    0
                                                    Гораздо интереснее было бы почитать какое-нибудь авторитетное руководство/учебник по современному параллельному программированию(ПП). С одной стороны вроде бы всем все понятно, но с другой — тут может обнаружиться множество не очевидных приемов.
                                                    Раз уж ПП это такая же революция как и ООП, то где аналоги книги банды четырех по ПП? Может уже есть?
                                                      0
                                                      Например, вот.
                                                        +1
                                                        увы, дружище, перевод книг подпадает уже под другую категорию перевода. бесплатного супа не быват — трясите издательства чтобы они не спали в шапку и не тратить силы на издания вской дряни типа html для чайников. а то такими темпами перевод книги изданной в 2008 году вы увидите дай бог в 2018. были уже такие прецеденты
                                                        +2
                                                        Количество транзисторов и количество ядер так же упрется в физические возможности, интересно что будет тогда? Возможно люди, наконец-то, возьмутся за оптимизацию своего кода? =)
                                                          0
                                                          к счастью количество ядер езе не скоро упрется в физические пределы. пока у индустрии есть большие возможноти пкаовать все больше и больше тразисторов на чипе. а значит увеличивать мощнось проессора не за счет повышения тактовой частоты, а за счет плотности логики на квадратный сантиметр. влоть до размеров атомов.
                                                          +2
                                                          Недавно читал книгу Программист прагматик, и там была глава про параллелизм.
                                                          Особенно понравился совет использовать UML диаграмму (activity diagram) для анализа возможных параллельных операций.
                                                          Глава 28. Временное связывание
                                                            +5
                                                            Я выбрал данную статью для перевода потому, что она на мой взгляд является очень известной и фундаментальной. На нее ссылаются многие другие статьи в интернете, а также она часто упоминается в литературе по параллельному программированию, как отправная точка, как начальный документ, с которым должен ознакомиться программист, чтобы быть в теме.

                                                            Список из нескольких книг, в которых упоминается данная статья:

                                                            2007 — Intel Threading Building Blocks: Outfitting C++ for Multi-Core Processor Parallelism
                                                            2009 — The Art of Concurrency: A Thread Monkey's Guide to Writing Parallel Applications
                                                            2010 — Professional Parallel Programming with C#: Master Parallel Extensions with .NET 4
                                                            2012 — C++ Concurrency in Action: Practical Multithreading

                                                            Судя по тому, какие процессоры мы видим вокруг себя сейчас в 2012 году, автор статьи, написанной в 2005 году, оказался совершенно прав. 10ГГцовых процессоров мы так и не увидели, и даже 4ГГцовых процессоров в продаже сейчас нет. Производители процессоров действительно сейчас во всю разрабатывают тему многоядерности (multi-core) и аппаратной многопоточности (hardware threads) процессоров: Intel, Oracle Sparc, Power, ARM.

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

                                                            Гонка за герцами сменилась на гонку за ядрами. 2, 4, 6, 8 ядер в процессоре уже есть сейчас. И каждое такое ядро поддерживает 2, 4, 16, 32 потока на ядро — уже сейчас. Спрятаться от этой гонки уже не удастся ни одному программисту. Если раньше многоядерность была головной болью системных программистов, писавших операционные системы, системы управления данными, научные вычислительные программы и системные утилиты, то теперь это касается тех, кто пишет веб-приложения, рядовые вычислительные программы, игры, прикладные программы, конвертеры фотографий и видео, архиваторы, почтовые программы, браузеры, программы моментальной передачи сообщений.

                                                            Раз предпосылки статьи оказались верными, значит и выводы из нее тоже верны: каждый программист должен подумать о том, как бы ему освоить параллельное программирование, чтобы не отстать от жизни и быть востребованным на рынке.
                                                              0
                                                              Не могу ответить за всех, но по-моему в любой достаточно сложной программе всегда использовалась многопоточность. У вас же в 3Ds Max не подвисает вся IDE во время рендеринга. И, например, к вам поступает звонок если вы тайпитесь в скайпе. А музыкальный плэер как был однопоточным, так и останется.
                                                                +1
                                                                при чем тут многопоточность?
                                                                  0
                                                                  что значит причём здесь многопоточность?
                                                                  +1
                                                                  тайпитесь
                                                                    +1
                                                                    Некоторым пиплам так больше лайкается.
                                                                    +1
                                                                    многопоточность существовала задолго до многоядерных процессоров. многопоточнеы программы выполнялись на одном процессере и это была просто имитация паралельного исполнения потоков. с появлением многоядерных машин потоки исполняются на параллельных ядрах, а значит это уже не имитация параллельного исполнения потоков, а реальное параллельное исполнение. вот именно при таком вот исполнении многие многопоточные программы начнут показывать ввсякие неожиданные глюки и ошибки. которые не выскакивали на однопроцессорных машинах. вот именно поэтому параллельное программирование теперь становится для программистов вторым хлебом. о чем и говорится в данной статье.

                                                                    в дополнение могу заметить что не все многопоточные программы написанные ранее масштабируются — например программасоздает 5 потоков. и эти 5 потоков поочередно выполнялись на одном процессоре. на 5-ядерной машине жти потоки попадут на разные ядра и прекрасно загрузят процессор… но вот если программу запустить на 10 ядерной машине — программа по прежнему будет использовать только 5 ядер для 5 потоков. и значит будет использовать машну лишь на 50%. видите как усложняется задача параллельного программирования на реальных параллельных машинахз? и мы говорим не о суперкомпьютерах, не о кластерах или сверх=супер-пуер серверх. а о реальных потребительских компьютерах: настольниках, лаптопах, планшетах, мобильных телефонах, игровых приставках и может леч через 5холодильник или какой-нить пылесос
                                                                      0
                                                                      Спасибо за столь подробное объяснение! Но я всё равно не до конца понял — а хочется разобраться.

                                                                      Я понимаю что многопоточность существовала, на моём старом 386 в Win 3.11 я мог открыть одновременно MS Paintbrush и играть в Miner в параллельном окне. Команды из процессов выполнялись по очереди, тем чаще чем выше приоритет.

                                                                      Теперь же MS Paintbrush будет идти на одном ядре, а Miner — на другом. НУ или если бы это был один процесс, разделённый на несколько потоков — теперь каджый поток будет выполняться отдельным ядром, и это гарантирует операционная система.

                                                                      Я к чему — и раньше приходилось писать программы, в которых создаётся много потоков, они между собой синхронизируются и т.п. Что принципиально нового в двух ядрах? Просто будет выполняться всё быстрее.

                                                                      А если программист из вашего примера разделил задачу на 5 потоков на однопоточной машине, то она скорее всего не делится на большее количество частей, или дальнейшее разделение неразумно — потоки будут много простаиваться и ждать друг друга. Не все ведь алгоритмы можно распараллелить.
                                                                        +1
                                                                        Принципиально новое — два потока могут обращаться к одному объекту в в действительно одно и то же время, т.е. одновременно, т.е. в один и тот же момент. Одновременная запись приводит ко всяким забавным потерям информации
                                                                          0
                                                                          Возможно я и правда отстал от жизни. И что, обычные семафоры, локи и т.п. в этом случае уже не будут работать? Ведь в регистрах обоих процессоров всё же разные наборы данных. Они могут одновременно обращаться разве что к чему-нибудь внешнему, к оперативке например.
                                                                            +1
                                                                            если язык и его компилятор хорошо и правильно их поддерживает — да. вот например Java 1.4 плохо это поддерживала и несмотря на все старания — синхронизации и volatile — программисты не могли добиться нужного результата. пришлось все переделывать в Java 5 и Java 6. и все равно правильно писать многопоточные на Java — очень трудно. мозг закипает

                                                                            а что уж говорить про С++, где нет на уровне языка многопоточности и приходится пользоваться стоонними библиотеками, в которых тоже свои глюки могут оказаться.
                                                                          +1
                                                                          >>> MS Paintbrush и играть в Miner в параллельном окне

                                                                          это не многопоточность. это мултипроцессность. MS Paintbrush в своем процессе, Miner — в своем. Ну сами посудите, какими такими данными MS Paintbrush ваш обменивался с Miner-ом?
                                                                            0
                                                                            Я просто для примера написал :) не помню просто по-настоящему многопоточных приложений для 3.11, да я тогда и не интересовался :)
                                                                      0
                                                                      > автор статьи, написанной в 2005 году, оказался совершенно прав. 10ГГцовых процессоров мы так и не увидели

                                                                      это было очевидно в нулевых, pentium4 оказался тупиковой веткой и вернулись к архитектуре схожей с pentium pro — pentium m, потом core и т.д.
                                                                        0
                                                                        Кстати, если бы кукурузную ветку Tejas со сверхдлинным конвейером на 5-9 ГГц все же развили, интересно, она бы хоть догнала сегодняшние 3-4 ГГц процы в SuperPI или кукурузилась где-то на уровне Core 2 Duo?
                                                                          0
                                                                          это не было очевидным в нулевых. возвращение к архитектуре pentium pro не имело никакого отношения к многоядерности кроме того не одним pentium pro жива компьютерная индустрия.
                                                                        +1
                                                                        Еще один момент статьи, который наверное упустили поспешные читатели.

                                                                        Автор явно намекает, что теперь и обучение программированию в университетах должно измениться. к основам программирования должны добавиться еще основы параллельного программирования, чтобы не оказалось так, что в институте учат, что код программы выполняется последовательно, как задумано программистом, а на практике оказывается есть out-of-order execution, memory model, memory barriers, согласование кешей, отложенная запись и чтение и прочая прочая прочая. то есть старая модель программирования — мысленное понимание программистом того, как выполняется его программа — не соотствествует новым дизайнам процессоров.
                                                                          0
                                                                          Гм, а разве ещё не? Мне казалось, что многопоточное программирование изучают в любом приличном месте.

                                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                        Самое читаемое