25 декабря 2011 г. 3:57
ТЕХНОЛОГИИ ИНФОРМАЦИОННОГО ОБЩЕСТВА
Современные микроархитектуры многопроцессорных вычислительных систем в телекоммуникациях
В телекоммуникационной среде в значительной мере используются новейшие разработки в области вычислительной техники. Сложные алгоритмы взаимодействия оборудования, обработки данных и управления качеством услуг требуют соответствующих платформ и подходов к организации программной модели. В докладе рассмотрены основные микроархигектурные решения ЭВМ и их применимость к моделям распараллеливания алгоритмов.
Филиппов АА,
аспирант, кафедра АИТСС МТУСИ, voidI 101@mail.ru
В телекоммуникационных областях потоки данных большие по определению. Причем, не только передаваемой информации (полезной нагрузке каналов передачи данных), но и служебной — в том числе и необходимой для исследования и управления качеством поставляемых услуг связи. Именно ей в последнее время всебольше и больше уделяется вниманкв и именно для обработки этих данных требуются большие вычислительные мощности. Для сбора технических, статистических и прочих данных требуются вычислительные системы большой мощности ориентированные на многозадачность.
Современный рынок высокопроизводительных серверов предлагает множество готовых устройств, построенных на центральных процессорах (ЦП) специфичных архитектур, которых, по сути, не так уж и много. В статье рассматриваются два наиболее значимых и широко используемых решения в многопроцессорных архитектурах — Uniform Memory Access — Symmetric Multiprocessing (SMP) и Non-Uniform Memory Access (NUMA). Цель — определить, какая из них наилучшим образом подходит под нужда задач телекоммуникационной среды, наиболее эффективно будет справляться с потоком поступающих запросов, вызовов и прочих операций спонтанного и запланированного характера. То есть, будь то абонентские запросы на подключение или процедуры мониторинга сети, система должна быть готова к их приему, обработке и предоставлению результатов
Основные понятия и определения
Приложение (application) — программа (система алгоритмов), выполняемая в среде операционной системы (ОС) в виде единого процесса. Оно может быть однопоточным (single-threaded) и многопоточным (muhi-lhreaded). Многопоточные приложения могут создавать множество потоков (tveods) — параллельно выполняемых алго-
ритмов. Приложение или поток можно привязать к конкретному ЦП средствами ОС. Режим Node Interleave (реализуется аппаратно) — чередование памяти страницами по 4КБ между модулями, принадлежащими разным ЦП.
Симметричная мультипроцессорность
Симметричная мультипроцессорность (SMP) - архитектура, подразумевающая разделение между несколькими процессорами общей оперативной памяти (RAM). На сегодняшний день она пока остается достаточно распространённой (рис. 1).
В SMP все процессоры могут обращаться к данным из любой области общей памяти по общей системной шине (FSB). При этом задачи довольно просто можно перераспределять между процессорами (эта возможность реализована во всех современных ОС). Однако, при обращении ЦП к памяти (которая работает гораздо медленнее него), вся система затрачивает значительное время только на получение данных, а поскольку лишь один процессор может производить операции с памятью единовременно, ситуация сильно усугубляется даже при грамотно построенной очереди обращений. Кроме того, на системной шине работает не только контроллер памяти, но и все периферийные устройства.
Такая система напоминает ситуацию с централизованной системой храненга данных (СХД), к которой идут обращения от многих узлов, что можно часто видеть на практике.
Неоднородная архитектура памяти
NUMA — схема организации ОЗУ в многопроцессорных системах в которой блоки памяти разделены между однородными ЦП и имеют различное время доступа для разных процессоров Эго означает, что у каждого ЦП есть "свой” блок внешней разделяемой RAM, к которой он может обращаться в любое время с максимальным приоритетом, испытывая при этом миндальные задержки по времени. Вместе с тем, ЦП может обращаться и к "соседской" памяти, что будет происходить несколько медленнее так как эта память расположена дальше (топологически и физически) и приоритет при таком обраще-
CPU0
Сою N
CPU1
FSB
Рис 1. Упрощенная схема многогроцрссорной архитектуры SMP 86
..... -АЛ!
memory
Vy
chipset
vo
DDR RAM
T-Comm, #10-2011
ТЕХНОЛОГИИ ИНФОРМАЦИОННОГО ОБЩЕСТВА
I/O
Х16
D DR RAM DR RAM
і _sJ \ ч 1
00R Hype'
memory Transport
I/O
CPU2
ш
DDR RAM
00R Hyper
memory Transport
I/O
DDR Hyper
memory Transport
I/O
CPU3
а с
Л
DDR RAM
DOR Hyper
memory Transport
controller VO
Х16
IЮ
а
ftic. 2. Упрснц»**» схема многопроцессорной срхитектуры NUMA ЭВМ на базе ЦП AMD Opteron
нии ниже, чем у ЦП, которому она "принадлежит” (рис. 2).
Преимущество такой архитектуры очевидно — обращение каждого ЦП к "своей" памяти происходит практически без задержек. При достаточном для выполнения программы объёме RAM и NUMA-оптимизированном алгоритме обращения к "чужой" памяти минимально, за счёт чего система избавляется от эффекта горлышка бутылки в шине памяти.
Для достижения максимальной производительности нужно иметь соответствующим образом написанные и оптимизированные приложения, поддержка NUMA-платформ должна поддерживаться на уровне ОС, чтобы как минимум "видеть" всю имеющуюся память и уметь распределять процессы по ЦП.
Первая и наиболее удачная реализация NUMA — Cache-Coherent Non-Uniform Memory Architecture, ccNUMA — в широко доступных серверных платформах была представлена компанией
Advanced Micro Devices (AMD) на базе процессоров Opteron начиная с 200-х и 800-х серий. Затем на NUMA Intel перевела свои процессоры, начиная с ядра Nehalem.
Эти ЦП имеют два уровня неразделяемой между ядрами кэшпамяти (L1 и L2) и общий для ник всех кэш L3. В процессор встроен быстрый контроллер оперативной памяти, а также универсальный скоростной интерфейс Hyper Transport для взаимодействия ЦП с соседними ЦП или другими устройствами.
Исследование пропускной способности подсистемы памяти (ПСП), проведённое лабораторией iXBT.com доказало эффективность такого решения [ 1 ]. А именно — в случаях разнесения однопоточных приложений по процессорам или использования NUMA-оптимизированных приложений наблюдается реальное увеличение скорости обращения к памяти в два раза в двухпроцессорной конфигурации (см. табл. 1).
Таблица 1
Зависимость пиковой пропускной способности подсистемы памяти от режима работы процессов (два ЦП)
Пиковая пропускная способность подсистемы памяти, ГБ/с (двухканальная DDR-400)
Платформа Однопоточное приложение Несколько 0.1Н0Н010ЧИЫХ приложений Мноіопоточнос приложение NUMA-aware многопоточное приложение
SMP 6.4 6.4 6.4 6.4
Несимметричная NUMA 4.2 (6.4*) 6.4 6.4 6.4
Симметричная NUMA 4.2 (6.4') 8.4(12.8') 6.4 12.8
Симметричная NUMA. Node Interleave 4.2 8.4 8.4 8.4
* » случае «привязки» приложения к одному из процессоров
T-Comm, #10-2011
87
Таблица 2
Зависимость пиковой пропускной способности подсистемы памяти от режима работы процессе» (четыре ЦП)
П.тформа Пиковая пропускная способность подсистемы памяти, разы
Олнопоточнос приложение, количество конин Многопоточное NUMA-aware многопоточное приложение"
1 2 4 и ри.южен И6
SMP 1.00 1.00 1.00 1.00 1.00
Симметричная 2-уиювая NUMA 0.65 (1.00") 1.30 (2.00") 1.30 (2.00") 1.00 2.00
Симметричная 2-угловая NUMA. Node Interleave 0.65 1.30 1.30 1.30 1.30
Симметричная 4-уповая NUMA 0.65 (1.00") 1.30 (2.00") 2.60 (4.00") 1.00 4.00
Симметричная 4-угловая NUMA, Node Interleave 0.65 1.30 2.60 2.60 2.60
* относительно максимальной теоретической ПСП для одного контроллера памяти *• число потоков, обращающихся к памяти, не менее 4
Как вццно из полученных данных, классическая SMP обеспечивает всегда одну и ту же пиковую ПОП. Так называемая "несимметричная NUMA" — режим работы, когда вся память принадлежит одному процессору (все модули у одного ЦП), "симметричная NUMA"
— классический вариант с равномерным размещением модулей памяти между ЦП.
Для NUMA-ориентированных приложений (т.е. написанных с учетом особенностей архитектуры) складывается несколько идеалистическая картина: два процессора — двойное увеличение ПСП.
Однако же и при переходе на 4х-процессорную конфигурацию эта тенденция сохраняется — ПСП для NUMA-ориентированных приложений возрастает четырехкратно по сравнению с SMP-системой. Эго происходит в том случае, когда независимые алгоритмы выполняются на разных ЦП и пользуются только локальной, памятью.
И снова повторяется единственная проигрышная ситуация — запуск однопоточного приложения. В случае же необходимости запускать неоптимизированное однопоточное приложение на помощь приходит режим Node Interleave, который позволяет сделать обращение к памяти более равномерным
Графические процессоры
Идея использования графических процессоров (GPU) для "не-грофических” расчетов не нова, первый GP(General Purpose)GPU был разработан ещев 1978 г для нужд авиации. В связи с бурным развитием графических ускорителей, у разработчиков возник закономерный интерес к возможности их массового "нетрадиционного" использования [10]. Более того, параллельно с видеоадаптерами развивались также "физические акселераторы" — процессорные платы для расчётов физики в играх и приложениях CAD, по архитектуре очень схожие с графическими (в настоящее время эта технология выкуплена производителями графжеских ускорителей и интегрирована в GPU).
Технология расчётов на GPU нашла большое количество сторонников среди потенциальных потребителей и была реализована крупнейшими разработчиками GPU — AMD АН и nVidia. В ноябре 2006 года ATI представила первую реализацию технологии GPGPU
- "dose То Metal" (впоследствии — ATI Stream), a nVidia — "CUDA" (Compute Unified Device Architecture).
Графические процессоры очень специфичны и принцип из работы в корне отличается от традиционных ЦП. Изначально GPU раз-
Core Control ALU ALU
ALU ALU
L1D cache
12 cache
DRAM controller
I/O
CPU
fac 3. Упрощенные структурные схемы ЦП и ГП 88
c a control ALU ALU ALU ALU ALU ALU ALU
mem
control ALU ALU ALU ALU ALU ALU ALU
mem
h e control ALU ALU ALU ALU ALU ALU ALU
mem
control ALU ALU ALU ALU ALU ALU
mem
DRAM controller
GPU
T-Comm, # 10-2011
роботы вались с целью максимально быстрой обработки больших массивов данных — текстур и вершин. Для отрисовки сцены нужно быстро загрузить, преобразовать текстуры, пересчитать массив координат, произвести преобразования, расчёт света и теней и т.п. Эго значит, что вместо последовательного выполнения случайных инструкций GPU должен произвести набор вполне конкретных процедур, различающихся входными данными [9]. Для ускорения этих процессов в GPU применяется роспараллеливание на самом низком уровне — на уровне инструкций. То есть, вместо одного универсального одра, в нем имеется большое количество специфических АЛУ(рис.З).
В GPU большое количество вычислительных одер, объединённых в группы с общей локальной памятью (не кэш, позволяет обмениваться данными внутри потока) - мультипроцессоры. Потоки, выполняемые на разных мультипроцессорах, не могут обмениваться данными. Инструкции в GPU выполняются по принципу SIMT (Single Ins&udion Multiple Threads), аналог SIMD CPU [15], когда одна инструкция прк^еняется ко всем потокам мультипроцессора.
Это выгодно, например, когда нужно "разом" обработать текстуру (или несколько, или даже все) — огромный двумерный массив точек, на последовательную обработку которого у обычного CPU уйдёт значительное количество тактов (т.е. времени в целом). Такой подход в свою очередь, накладывает значительные ограничения на использование GPU, а именно — математические операции по преобразованию больших массивов данных. То есть, когда входной массив обрабатывается максимально единовременно, пускай даже и в несколько проходов. Такой подход удобен в трёхмерной графике, обработке видео, наукоёмких задачах (при решении уравнений, фрактальных вычислениях и т.п.), где можно наблюдать ускорение до 100(!) раз [11]. В области телекоммуникаций таких задач немного
— проектирование сетей (это, как правило, однократные вычисления, не требующие закупки специального оборудования), статистические расчёты (в том числе и в реальном времени) и шифрование трафика (а его вообще выполняет специальное, сертифицируемое оборудование). Таким образом, в реальности пока нет необходимости в закупке дорогостоящего вычислительного оборудования но базе GPU, особенно если учесть сложность оптимизации приложений под него.
Программное распараллеливание
Теперь рассмотрим различные уровни распараллеливания задач и то, насколько они применимы к рассмотренным архитектурам.
Распараллеливание на уровне задач
Этот тип распараллеливания возможен в тех случаях, когда решаемая задача естественным образом состоит из независимых подзадач, каждую из которых можно решить отдельно. Оно обычно реализовано на уровне ОС, когда разные программы запускаются на разных ЦП или одрах (13]. Следует учитывать, однако, что программы могут пытаться использовать одни и те же данные. Одно дело, когда запущены mail- и web-серверы, которые друг с другом почти не связаны. В этом случае чрезвычайно выгодна NUMA-архитектура, в который на каждый процессор физически выделен свой банк памяти. Другое дело, когда разные приложения (или несколько копий одного) работают с одними и теми же данными, что может увеличить "дальность" данных для соседних узлов. В таком случае более выгодной может оказаться SMP-архитектура. Однако стоит заметить, что включение режима Node interleave поможет если не ускорить^ то усреднить время доступа к памяти обоих приложений. Более того, современные NUMA-системы достаточно быстры и "интеллектуальны" чтобы сделать проигрыш малозначительным Технология GPGPU, как уже упоминалось ранее, в этой ситуации бесполезна.
Уровень параллелизма данных
Заключается в применении одной и той же операции к множеству элементов данных. На практике используется, например, в современных архиваторах, СУБД сетевых хранилищах и т.п. При этом все данные разбиваются на блоки, которые затем по одному и тому же алгоритму обрабатываются на нескольких узлах. Очевидно, в этой ситуации будет выигрывать NUMA-система. Более того, в приложениях численного моделирования, когда, например, исследуемое пространство разбивается на геометрические ячейки, обрабатываемые параллельно, ключевую роль может сыграть GPGPU. Ведь именно GPU силён в параллельной обработке большого числа небольших ячеек сданными. В математической физике этот тип параллелизма называется геометрическим параллелизмом.
Уровень распараллеливания алгоритмов
Как и следует из названия, распараллеливание изначально производится на стадии разработки алгоритма. Следовательно, в рамках одной программы на процессоры одновременно могут ложиться как однотипные, так и совершенно разные задачи.
Эффективное выполнение такой программы полностью зависит от программиста. В этом случае ни операционная система, ни, тем более, процессор не знают, какие подпрограммы каким образом лучше распределить по вычислительным узлам Например, в программе, приведённой на схеме (рис. 4), присутствует два ветвления. Здесь выгоднее было бы разместить потоки Ы,Ь2 и ЬЗ на одном многоядерном процессоре, а поток с — но соседнем процессоре (подразумевается, что потоки b однотипны и используют больше общих данных между собой, чем с блоком с).
Параллелизм на уровне инструкций
Осуществляется на уровне процессора. При этом выполняются специальные "параллельные" инструкции — такие как MMX, SSE, 3DNow! (обработка векторов, матриц и т). На обычных ЦП это дает ощутимый прирост производительности в специально оптимизированных приложениях. Отчасти это похоже на принцип, использующийся в GPU. Само по себе, разумеется, такое распараллеливание создаваться не будет. Ситуация во многом похожа на предыдущий случай — заботиться о поведении программы должен разработчик. Но в этом случае ключевую роль игроет низкоуровневая оптимизация бинарного кода программы, получаемого во время ком-
:
FV«e 4. Условная блсжсхема грсхраммы с вложенным параллелизмом
T-Comm, # 10-2011
89
СмО ... CowN
Кшм Имея*
и саск* CPU Uc*0«
Ч'ЯL'nl<
-А
FW. 5. Организсиия многопроцессорной с
>i Irtd Сото
пиляции [14]. Например, при задействовании технологий SSE/3DNow! вместо последовательности математических операций достаточно записать исходные данные в регистры, вызвать специальные инструкции и считать выходные данные. Выполнение таких инструкций не зависит от архитектуры, но чётко связано с возможностями используемой модели CPU Для GPU такая ситуация типуна — на работу именно в таком (и только таком) режиме он и рассчитан (только в значительно больших масштабах). Остальные части алгоритма при этом всеравно ложатся на ЦП Для облегчения роботы программистов разработчики GPGPU создали сначала API для распараллеливания алгоритмов за счет GPU: проприетарные nVvidka CUDA и ATI Stream и открытый стандарт OpenCL
На практике
В реальности, распараллеливание алгоритмов на многопроцессорных машинах, применяется, в основном в тех облостях. где нужны мощные вычислительные машины для параллельных расчётов Эго не обязательно научно-исследовательские задачи — современные кластерные суперкомпьютеры применяются повсеместно, но особенно остро вопрос времени выполнения задач стоит перед системами управления бизнес-прся^ссами и системами управления сетями связи.
Эффективность внедрения построенных на NUMA-платформах систем можно подтвердить простым примером. В любой области предоставления услуг связи существует поток независимых входящих вызовов. В любой момент времени система биллинга должна принять и обработать запрос абонента на предоставления услуг Алгоритм обработки вызова возможно, не слишком сложен математически, но крайне высоки требования к скорости его выполнения. Специальная служба (постоянно работающее приложение, запущенное в момент загрузки ОС) отслеживает входящие вызовы, и для каждого из них создаёт отдельный поток (thread) для выполнения алгоритма обработки. Очевидно, что эти потоки представляют собой несвязанные параллельные алгоритмы, что как нельзя лучше вписывается в идеологию NUMA Учитывая, что обмен с внешней средой минимален, можно смело говорить о самом благоприятном режиме роботы ЦП и памяти, рассмотренном в табл. 2.
Таким образом, на основе полученных ранее данных напрашивоется вывод о том, что NUMA-платформы являются на сегодняшний день наиболее эффективным решением в рассмотренном случае и ему подобных.
Масштабируемость
Многопроцессорность подразумевает использование многих вычислительных узлов. В мощных SMP-серверах и рабочих станциях устанавливалось по два, по четыре и даже большее число ЦП, и все они "сажались" на одну шину (FSB). Так кок же масштабируются современные мультипроцессорные системы?
Во-первых, систему можно расширить "вглубь" — установить процессоры с большим количеством ядер (на момент написания в продажу поступили шестиядерные модели). Этот метод очень удобен, ток кок во многих случаях позволяет обойтись без замены материнской платы, в то время как число вычислительных узлов увеличится. Но это утверждение справедливо лишь до поры: рано или поздно материнская плата или чипсет не смогут поддерживать новые модели ЦП.
Реализации многоядерности в ЦП AMD и Intel различны. Если у Intel архитектура ядер более традиционная и похожа на SMP-моши-ну (рис. 5), то у AMD даже внутри ЦП ядра организованы согласно принципам NUMA с реализацией направлению данных по минимальному пути (НТ Assisi).
Второй, более привычный способ масштабирования — “вширь"
— это увеличение числа самих ЦП в системе. Для этого требуются значительно большие затраты, так как необходима замена материнской платы (или даже сервера целиком). С приходом NUMA пришла необходимость одновременной (синхронной) модернизации процессоров и оперативной памяти — каждому процессору теперь принадлежит свой банк памяти.
В многопроцессорных системах имеются специальные скоростные связи между ЦП [8]. В платформе Intel это связи между контроллерами памяти разных процессоров при помощи шины "QuickPalh Interconnect" OPI [12]. Такой подход ориентирован именно на обращение к соседней памяти.
В высокопроизводительных вычислительных системах используются более дорогие и сложные ЦПУ Itanium, разработку которых Intel начала совместно с Hev^elt-Packard ещев конце 1990-х Архитектура Itanium позволяет соединять до 8 ЦПУ без использования внешних коммутаторов и других микросхем. В многопроцессорных 6л эйдах и им подобных системах до 2010 г использовался дополнительный набор микросхем (рис 6 слева), но начиная с серии Itanium 9300 появилась возможность объединять blode-модули в единую SMP-систему (до 8 устройств), используя шину QPI самостоятельно (рис. 6 справа).
FVic. 6. Орссмоация многопроцессорной системы Intel Itanium
90
T-Comm, # 10-2011
НТ Link
I/O
Ffcc. 7. Оргсмнэация многоадерной и многопроцессорной системы AMD
В процессорах АМО "с Ы11МА внутри” обмен данными между ядрами как внутри, так и между ЦП производится скоростной шиной НурегТгагероП [8]. Шина является универсальной и полнодуплексной, причём она соединяет разные чара одного процессора с внешними устройствами. Такой подход весьма интересен тем, что позволяет перейти от "плоской" к "объемной" многопроцессорной системе, похожей на кристаллическую решетку. В этом случае достигается меньшее логическое расстояние между самыми удаленными адрами (рис 7).
С "графическими" вычислительными системами масштабирование производится путем замены или добавления плат-ускорителей. Их количество зависит от числа свободных слотов на материнской плате сервера.
Ну и, наконец можно объединять ЭВМ в кластеры, что тоже в некоторых случаях является подобием ЫиМА Но это уже выходит за рамки статьи.
Выводы
Современные высокопроизводительные вычислительные системы во всём своём многообразии позволяют решать самые разнообразные и требовательные задачи. Для каждого метода распараллеливания найдётся подходящая архитектура. Однако, для эффективного использования любой платформы необходимо заранее планировать и оптимизировать должным образом свои программы. Телекоммуникационное ПО, как правило, разрабатывается с учётом этих требований. Но даже в случае отсутствия оптимизации ПО под N11МА-платформу современные операционные системы позволяют своими средствами добиться приемлемого по эффективности использования аппаратных ресурсов.
Литература
1 Д»*<ірий Беседоі- Noo-Urdform Memory- /^chiteclure (NUMA): иссле-доесние подсистемы памяти двухпроцессорных платформ AMD Opteron с помощью RighlMcik Memory Andyzer (части 1 и 2) [hip://www.ixbl.com/ сри/ mrano-numashiml ].
2. Kd Hwaig, Advanced Computer Architecture. ISBN 0-07-113342-9.
3. NUMA Frequently Asked Questions [http://lse.sourceforge.net/ numo/foq].
4 David Kantar. Inside Nebalem: Intel's Fufcjie Processor and System [http.//wvvwrealwoHdtech.com/includes/templates/ artides.cfm?ArfdeJD=RW T040208182719&node=print ].
5. Сергей Озеров, Алекс Кфабукх Двухыщврные процессоры Intel и AMD: теория [ hip://www.ferTaru/online/processors/s25920/print J.
6 Станислав ГфмапасСМР vs. SMP на платформе AMD SMP-системы на базе Opteron 250 и двухъядерного Opteron 2 75 (hflp://www.ixbtcom/ ери/ апхі-cmp-vs-smpshtmlJ
7 Main X Bfigh, Mat Dobson, Daren Hart Gent Hutzznga. Linux on NUMA Systems [ OlS2004-numo.papef.pd ].
8. Mainstreom NUMA & Ihe TCP/IP stock: Part 1 and 2 [http.//blogs.msdn.com/ddperf/archive/2008/06/10/ mainslream-numo-and-the-tcp-ip-stock-port-iaspx J (10 June 2008).
9 Алексеи Берилла NVIDIA CUDA — неграфические вычисления на графических процессорах [ Ьф://wwwixbt.com/video3/cuck> 1 shtrrl j
23 сентября 2008.
10 Stev* Gre^r. AH Stream vs. NVDIA CUDA — GPGPU computing beetle royale [ hnp://pcpercom/ari»dephp?aid=745 ] Aug 07, 2009.
11. Сергей Мерькоа. Cybertnk PowerDirector, NVIDIA CUDA и ATI Stream (h^://wwwixbtcom/cfr^deo/cpckucb-slream5Hml ] 15 июня 2009 r.
12. National Instrument, Top Bght Features of the Intel Core i7 Processors for Test, Measurement, and Control [ http://zone.nLcom/devzone/oda/tut/p/id/ II266] Aug 30. 2010.
13. Андрей Ксфпоь Знакомство с уровнями роспаралпелиесяия, (bflp://hdbrc^xixju/rss/compcriy/intel/blog/80342] 9 янверя 2010,21 ЮЗ.
14. Материслы Лаборатории Параллельных информационных технологий НИ ВЦ МГУ (hUp://pardW.ni ].
15. Y.WalocK Alternating Sequential/Parallel Processing (ed. by G.Goos, J.Hartmanb). перевод И A hWonaeeaA-M. Степанова, М.: "Мир", 1985, ВБК 32.973, В16, УДК 681.3. - С.26.
Т-Comm, # 10-2011
91