Архивы: по дате | по разделам | по авторам

И орешки всё грызет…

Архив
автор : Платон Жигарновский   15.06.2005

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

Возможно, прочитав эту статью, один читатель обиженно воскликнет, что «Компьютерра» уже писала про двухъядерные процессоры, а другой возмутится, что в статье нет ни цифр, ни тестов производительности. Попробую заранее успокоить и того и другого: наконец-то официально анонсированные Pentuim D и Athlon 64 X2 на пару с Pentium Extreme Edition уже успели побывать в нашей лаборатории и пройти изнурительное многодневное тестирование. Материал об этом появится в следующем номере. Однако «КТ» никогда не ограничивалась простым описанием попадающих к нам железяк. И тем более было бы странно ограничиться лишь констатацией производительности новых двухъядерников: куда интереснее разобраться в том, как они устроены, почему показывают те или иные результаты и чего от них можно ждать в будущем.

Этой статьей мы открываем небольшой цикл, посвященный будущему центральных процессоров. В ней, помимо «теории» и «практики» многоядерных CPU, мы расскажем о том, как делаются программы для них, какой инструментарий для этого используется, какие проблемы возникают; попробуем заглянуть в будущее и представить, куда пойдет дальнейшая эволюция центральных процессоров.

Sимметричная Mногопроцессорность

Чтобы разобраться в том, как работают современные двухъядерные процессоры, обратимся к теории работы SMP-систем (то есть систем с симметричной многопроцессорностью). Для начала: это системы из двух и более процессоров, в которых все процессоры обладают одинаковыми правами на доступ к общей системной памяти. То есть если процессор A может обратиться к энному адресу в системной памяти, то и процессор B может это сделать, причем тем же способом и с той же скоростью, что и процессор A. Как правило, сами центральные процессоры в SMP тоже должны быть одинаковыми — и, таким образом, теоретически взаимозаменяемыми (от «перестановки» двух любых процессоров в системе ничего не должно изменяться). Число этих процессоров обычно должно быть кратно степени двойки: 2, 4, 8, 16 и т. д.

С точки зрения операционной системы никаких отличий между процессорами в SMP-системе попросту нет: любые процессы или потоки, запущенные на одном CPU, можно спокойно перебрасывать на другой; в теории не должно возникать ни малейшей разницы в том, как они будут исполняться. Поэтому планирование задач для SMP-систем — сравнительно простая штука: просто вместо одного процессора, на который ОС может выполнять задачи, у нее в распоряжении оказывается несколько — на N процессорах одновременно может выполняться N параллельных задач. И производительность подобного решения теоретически может быть в N раз выше.

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

Принципиально разных вариантов, на самом деле, оказывается всего два. Первый — оставить каждому процессору по независимой шине, подключить энное количество шин к чипсету, а дальше, уже «внутри» чипсета, разбираться с тем, как поступающие запросы обслуживать. А поскольку чипсет так и так вынужден заниматься обслуживанием обращений к памяти от нескольких типов устройств (даже в однопроцессорной системе наравне с CPU на доступ к ОЗУ претендуют как минимум DMA-контроллер системы и GART-контроллер AGP- или PEG-видеокарты), то все получается довольно изящно.

Подобный подход, например, использовала в своем 760-м чипсете компания AMD: два процессора Athlon MP — две шины EV6 — один чипсет, к которому все и подключено. Однако процессорная шина — одна из самых сложных и быстродействующих шин в любом компьютере, и ее разводка, как правило, сильно затруднена необходимостью обеспечить стабильно работающую на высоких тактовых частотах широкую параллельную шину. Скажем, для современных Pentium 4 ширина этой шины — 64 бита, а тактовая частота достигает одного гигагерца: вот и попробуй провести ее на плате так, чтобы и сигнал распространялся нормально, и ни на кого из «соседей» она не влияла, и места на материнской плате еще для чего-нибудь бы хватило. Реально оказывается, что развести больше двух «настоящих» FSB на одной плате невозможно, и Athlon MP соответственно ориентировался на использование только в двухпроцессорных системах.

Альтернативный способ — сделать одну шину FSB общей для нескольких процессоров. Пусть они передают данные по очереди! В конце концов, все равно два запроса от процессоров в оперативную память не могут быть выполнены одновременно: память-то у них общая, и контроллер памяти (как правило) тоже один! При этом пока один процессор будет «разговаривать» с чипсетом, все остальные, «сидящие» на той же шине, смогут «подслушивать» их разговор и делать соответствующие «выводы». Процессор A отправил запрос на запись в оперативную память по адресу 0xABCD — и процессор B тут же просматривает свой кэш в поисках соответствующей этому адресу линейки и делает пометку, что данные, находящиеся в ней, отныне устарели (не соответствуют тому, что реально находится в ОЗУ). Именно эта схема свойственна подавляющему большинству современных SMP-систем.

Все не менее красиво и логично, чем в «лобовом» решении с несколькими FSB, однако, как и за все хорошее, в подобных системах приходится за «простоту» решения изрядно расплачиваться. Главных минусов у общей шины два.

Во-первых, появляется необходимость арбитража шины: процессоры вынуждены как-то между собой «договариваться», какому из них в данный момент можно использовать шину. Впрочем, процессору так или иначе приходится как-то «договариваться» с чипсетом, когда он его может о чем-то запрашивать, а когда следует с запросами повременить (ибо дел у коммутатора и контроллера памяти и без того невпроворот), так что арбитраж в том или ином виде все равно присутствует даже в системах с «персональными» FSB. Просто в случае SMP он становится заметно более сложным и комплексным. А занимается им, как правило, небольшая схема в чипсете — арбитр шины.

А во-вторых, к сожалению, развести одну быструю и широкую шину на энное количество процессоров хоть и проще, нежели развести энное количество быстрых шин, но только до определенного предела. Возрастает электрическая нагрузка на шину, возрастает длина шины, возрастает ее сложность. Проще говоря, если процессорная шина, рассчитанная на один Pentium 4, способна работать на частотах до 1066 (800) МГц, то шина, рассчитанная на два Pentium 4 Xeon уже работает на частоте 667 (533) МГц, а шина, на которую можно установить четыре Xeon, будет работать на частотах не выше 400 МГц. При этом, как легко догадаться, требования к пропускной способности шины, которую делят между собой все эти процессоры, противоположны: чем больше процессоров, тем быстрее должна быть объединяющая их шина. Увы, но эта проблема неразрешима: «классические» SMP чрезвычайно плохо масштабируются «вверх» по числу процессоров. А последствия этого мы уже описывали: даже в идеальном случае типовая производительность системы с одним процессором составляет 100%, с двумя — 170%, с четырьмя — жалкие 220%. Оперативная память по меркам CPU является страшнейшим тормозом, и если процессор раньше работал 50% времени, а остальное время простаивал, ожидал поступления данных из оперативной памяти; то теперь он будет работать 33% времени; ждать данные 67% времени, а общая производительность программы от использования двух процессоров увеличится всего на треть (!). А ведь мы еще даже не рассматривали проблемы распараллеливания программного кода — реальные цифры будут еще хуже. Так что использование общей шины позволяет объединить сколь угодно большое число процессоров, но вот толку от них при этом немного.

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

Ладно, с подключением CPU к памяти мы разобрались. А что делать с организацией межпроцессорного взаимодействия? Хорошо, если каждый процессор в каждый момент времени работает со «своим» участком оперативной памяти и нет необходимости в создании «общих» ее кусочков. Тем более что механизмы виртуальной памяти позволяют легко гарантировать, что один процесс не будет «лазить» в оперативную память «соседа». Но эта ситуация безумно далека от реальной жизни: как минимум в единой памяти должна жить сама оперативная система; в едином адресном пространстве живут потоки одного процесса; наконец, системе требуются механизмы синхронизации работающих одновременно задач. А значит, нам требуется еще обеспечить:

  • Механизмы обеспечения когерентности кэшей. Если процессор A что-то поменял в оперативной памяти, то все остальные процессы должны сделать соответствующие пометки «устарело» напротив своих копий этих данных в «своих» кэшах. Для этого существуют специальные алгоритмы поддержания когерентности кэшей. Процессоры «подслушивают» разделяемые шины, обмениваются между собой специальными «сообщениями», но, главное, для обеспечения когерентности некоторые изменения вносятся в кэш-память: что делать с ней, если случилось то-то и то-то; где вносить специальные пометки и флаги. Intel использует старый добрый протокол MESI (Modified, Exclusive, Shared, Invalid — по названиям возможных статусов — «состояниям» линеек кэша); AMD использует чуть более «продвинутый» протокол MOESI (где к четырем «традиционным» добавлен статус Owner).
  • Поддержку специальных «атомарных» операций. Примерами подобных операций являются процедуры взятия блокировки (семафора или мьютекса): процессор должен вначале проверить состояние определенной ячейки памяти и, в зависимости от ее состояния, либо решить, что ячейка «занята», либо что-то туда записать. А что будет, если в промежутке между чтением ячейки и записью туда то же самое попробует сделать соседний процессор? Он тоже прочитает ячейку, увидит, что она пока «свободна», и тоже туда что-нибудь запишет. Причем оба процессора будут уверены в том, что «взяли» блокировку. Как же решить эту проблему? Для этого требуется, чтобы при выполнении определенных цепочек инструкций один из процессоров «запрещал» всем остальным доступ к оперативной памяти до того момента, как эта «атомарная» цепочка не будет завершена. То есть пока не будет выполнена операция чтения и операция записи в ячейку мьютекса, никто к оперативной памяти обратиться не сможет. На практике это означает, что в x86 существует специальный префикс для инструкции — «выполнить в атомарном режиме», а процессоры и арбитр шины этот режим поддерживают.

  • Наконец, last, but not least, в SMP-системе возникает проблема и с прерываниями. Что делать с прерыванием в однопроцессорной системе, понятно: его принимает программируемый контроллер прерываний и, если процессор не возражает (не запрещены прерывания), «прерывает» выполнение текущей программы на процессоре, заставляя последний вначале выполнить заданную программу по обработке возникшего где-то в периферии события (аппаратного прерывания). Прерывания позволяют CPU оперативно реагировать на возникающую где-то за его пределами информацию, не расходуя слишком много сил на постоянную проверку «не случилось ли где-нибудь в клавиатуре нажатия клавиши», поэтому современный компьютер без них попросту немыслим. А ведь помимо аппаратных могут возникать еще и программные прерывания, которые генерирует сам процессор в случае возникновения каких-либо ошибок. Обратилась ли программа к несуществующему адресу в памяти (или этот адрес находится в своп-файле), выполнила ли «недопустимую операцию» — в любом случае CPU просто не может знать, что делать дальше, — и обращается к одной из заложенных в него на все случаи жизни (на все прерывания) программ. И если в обычной системе с прерываниями успешно разбирается один-единственный Programmable Interrupt Controller, то в SMP одним PIC’ом, к сожалению, не обойтись. Приходится вводить единый APIC (Advanced PIC) для «общесистемных» событий в дополнение к персональному APIC’у для каждого из процессоров и заниматься довольно непростыми процедурами синхронизации обработки прерывания на всех (если, скажем, требуется аварийно завершить работу программы, потоки от которой оказались одновременно запущенными на всех процессорах) или каких-то конкретных CPU.

    Ну что? Простая эта штука, SMP? К счастью, SMP-системы существуют и эксплуатируются уже довольно давно, так что соответствующая поддержка и необходимые схемы сегодня закладываются в кристаллы едва ли не всех выпускающихся процессоров (включая и одноядерные): унификация всего и вся — великая сила! Так что изменения в современных компьютерах при переходе к многопоточным и многоядерным архитектурам получаются сравнительно небольшими

    Многоядерные процессоры: SMP в одном кристалле

    Начнем с самого простого: двухъядерных процессоров Pentium D. Intel явно не стала мудрствовать лукаво со своим Smithfield’ом… и попросту объединила два идентичных ядра Prescott в одном кристалле. Представьте себе, что традиционную двухпроцессорную SMP-систему на новых Xeon’ах просто-напросто совместили в пределах одного сокета. Оба ядра «сидят» на единой процессорной шине; никаких дополнительных блоков и схем по сравнению с традиционной SMP не прибавляется и не убавляется. У каждого ядра свой «локальный» APIC, в чипсете — «знающий» о наличии двух процессоров «глобальный» APIC и традиционная схема арбитража шины: все настолько скучно, насколько это вообще можно представить.

    Куда как более хитрый подход к многопоточности — это технология Hyper-Threading, давно и успешно применяющаяся в процессорах Pentium 4 еще со времен ядра Northwood с 800-МГц системной шиной. В подобной системе находится всего одно ядро, которое «делает вид», что ядер на самом деле два. Сделать это сравнительно несложно: достаточно продублировать в процессоре программные и некоторые служебные регистры (в которых, собственно, и записывается «состояние» выполняющегося процесса); продублировать APIC (как уже говорилось, он обязан у каждого CPU быть «свой») и внести незначительные изменения в исполнительный конвейер процессора (чтобы последний отличал инструкции «разных» ядер). На практике, правда, для достижения высокой эффективности еще и разделяются некоторые кэши… но в целом изменению подвергается не больше пары-тройки процентов от площади всего кристалла процессора. Исполнительный конвейер и кэши данных у обоих «виртуальных» процессоров общие, поэтому из одного «целого» процессора мы фактически получаем две «половинки» с пропорционально меньшей производительностью. Но поскольку исполнительные устройства Pentium 4 работают на совершенно заоблачных тактовых частотах (до 7–8 ГГц!) и загрузить их работой один поток, как правило, не в состоянии, то использование двух явно независимых потоков исполнения позволяет заметно поднять КПД, с которым эти исполнительные блоки используются (на практике — от 10 до 25%). Еще одно преимущество от использования HT — операционной системе становится проще имитировать множество независимых компьютеров на одном-единственном процессоре.

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

    Самый сложный и «навороченный» способ реализации двухъядерников применяет AMD. Дело в том, что в ее процессорах архитектуры K8 изрядная часть чипсета (включая, в частности, контроллер памяти, DMA, коммутатор, GART и APIC) интегрируется непосредственно в центральный процессор. А с внешним миром (периферией, другими процессорами и «чужой» оперативной памятью) вся эта система-в-миниатюре общается по одной или нескольким высокоскоростным последовательным шинам Hyper-Transport. Архитектура SUMA, которую использует для своих серверных процессоров AMD, — это вообще тема отдельного разговора; однако нас сейчас интересует только конкретная реализация в этой архитектуре двух- и многоядерных процессоров. Обратимся к блок-схеме.

    Непростая система, не правда ли? Однако львиная доля всей этой сложной схемотехники является составной частью архитектуры K8 как таковой и к двухъядерности никакого отношения попросту не имеет. А в подключении второго ядра вообще нет ничего хитрого: перед нами… самая банальная SMP, уютно расположившаяся в пределах одного кристалла. Просто в нее входит Northbridge («чипсет») и межпроцессорная шина SRI (System Request Interface), которая работает на полной тактовой частоте ядра, имеет, по всей видимости, ширину 64 бита, и к которой подключено от одного до двух процессорных ядер K8. Однако это как раз тот случай, когда количество переходит в качество: просто представьте себе полноценную процессорную шину, работающую на частоте в пару гигагерц! Заметим, что эту шину в кристалл закладывали изначально — она присутствует и в уже давным-давно выпускающихся Athlon 64, Sempron и Opteron: просто 130-нм технологический процесс ранее делал производство соответствующих двухъядерных процессоров экономически нерентабельным. Пропускная способность внешних интерфейсов процессора (оперативной памяти и линков к другим процессорам) не идет ни в какое сравнение с пропускной способностью и латентностью SRI: фактически можно считать, что каждое из ядер K8 напрямую подключено к коммутатору в чипсете (без возникновения традиционных проблем с разводкой этих прямых FSB на материнской плате). Приплюсуйте сюда то, что для «стороннего» наблюдателя принципиально ничего в процессоре не изменяется! А это значит, что двухъядерные процессоры AMD можно (теоретически) использовать в любых уже существующих системах, работающих с соответствующими типами сокета (Socket 939 в данном случае). Добавьте к этому невысокое тепловыделение процессоров K8 — и вы поймете, насколько удачно ложится двухъядерность на архитектуру K8.

    Двухъядерные Pentium D и Athlon 64 X2 уже потихоньку появляются в продаже: подробный обзор их производительности, тепловыделения и ценовых аспектов — в следующем номере журнала.
     

  • © ООО "Компьютерра-Онлайн", 1997-2024
    При цитировании и использовании любых материалов ссылка на "Компьютерру" обязательна.