![]() |
||||||||||||||||||||||||||||||||||
![]() |
||||||||||||||||||||||||||||||||||
![]() |
RefNUMA – библиотека для организации виртуальной общей памяти в программах, использующих MPI.А. О. Лацис
Приложение 1. Как работает RefNUMA.Основная идея реализации RefNUMA отразилась в ее названии: Reflective NUMA, или рефлективная (зеркалированная) общая память класса NUMA. Общее представление о принципах реализации такой памяти может быть полезно прикладному программисту для лучшего понимания, на что при работе системы тратятся время и ресурсы памяти, что и почему работает хорошо, а что – плохо. Объяснять будем в два этапа. Сначала опишем формально работоспособную, но совершенно не эффективную тривиальную реализацию, а потом расскажем, как ее пришлось усовершенствовать, чтобы получилась RefNUMA. Тривиальная реализация общей памяти, когерентной с точностью до барьера, выглядит так. В каждом процессе хранится полная копия (зеркало) всей общей памяти, причем в двух экземплярах – оригинал зеркала и копия зеркала. Вся работа программы идет с оригиналом. При выполнении барьера каждый процесс сравнивает оригинал с копией, рассылает всем процессам замеченные различия, после чего обновляет копию, переписывая в нее оригинал. Вполне очевидно, что такая реализация вопиюще неэффективна по трем параметрам: по необходимому объему памяти, по объему локальной работы (сравнение), и по объему коммуникаций. Добившись значительной экономии по каждому из трех указанных параметров, можно надеяться получить систему, работоспособную на практике. Реализация RefNUMA отличается от приведенной выше тривиальной схемы, в общих чертах, следующим. Сравнение копии с оригиналом подразумевает некоторую гранулярность. В самом деле, сравнивать и пересылать при несовпадении отдельные биты очень уж неэффективно. В RefNUMA квантом сравнения (и пересылки при несовпадении) является значение типа double, длина которого в байтах на 64-разрядных системах обычно совпадает с длиной значения типа long. Именно такие (а не более короткие) элементы рекомендуется хранить в секционированных массивах. Разбиение массива общей памяти на свои и чужие секции позволяет значительно сократить объем пересылок. Сведения об изменениях в чужих секциях посылаются только хозяину секции, сведения об изменениях в своей секции – только тем, кто реально к ней обращался. Копия зеркала хранится только для тех чужих страниц (страница – 512 значений double), к которым реально было обращение в этом процессе, и только для тех своих страниц, к которым реально были обращения в других процессах. Оригинал зеркала формально отводится целиком при создании массива, но фактически занимают место только те страницы физической памяти, к которым реально было хотя бы одно обращение. Основные факты и правила, которые следует знать и стараться соблюдать при использовании RefNUMA, следующие.
В заключение скажем несколько слов об особенностях реализации RefNUMA, в частности, о ее мобильности (переносимости на другие компьютеры). Библиотека RefNUMA реализована на базе коммуникационной библиотеки shmem, которая обычно используется в программах вместе с MPI. На суперкомпьютерах ИПМ им. М. В. Келдыша РАН имеются две реализации shmem – Qlogic shmem и Shmem-Экспресс. Версии библиотеки RefNUMA существуют для обоих вариантов. На большинстве суперкомпьютеров установленные реализации shmem отсутствуют. Для того, чтобы сделать возможным перенос RefNUMA на любой суперкомпьютер, оснащенный реализацией MPI, была реализована специальная вспомогательная библиотека microshmem. Она реализована на базе MPI, причем используются только наиболее тривиальные, устоявшиеся возможности минимальных конфигураций MPI-1. Эта библиотека реализует ровно те функции shmem, которые использует RefNUMA, и делает это настолько эффективно по быстродействию, насколько это возможно сделать, оставаясь в рамках тривиальных, устоявшихся возможностей MPI. Библиотека microshmem крайне компактна, и собирается командой mpicc за доли секунды. На ее базе RefNUMA может работать с единственным, по сравнению со «штатными» версиями shmem, ограничением, о котором будет рассказано ниже. Эффективность программ, использующих RefNUMA, конечно, очень сильно зависит от качества используемой реализации shmem. Версия RefNUMA на базе Qlogic shmem может как проигрывать, так и выигрывать версии на базе Shmem-Экспресс по скорости, в зависимости от особенностей прикладной программы. Разница, в любом случае, не очень велика (менее чем в полтора раза). Версия RefNUMA на базе microshmem несколько менее эффективна, но все же позволяет получать вполне приемлемый уровень производительности и масштабируемости. К сожалению, реализация RefNUMA на базе microshmem отличается от «штатных» реализаций не только количественно. Важнейший источник сравнительно высокой эффективности реализации RefNUMA – принцип предоставления процессу сведений об изменениях в чужих для данного процесса областях общей памяти по неявному запросу. Обратившись впервые за время работы программы к некоторому элементу массива общей памяти, находящемуся в чужой для данного процесса части этого массива, процесс как бы «оформляет подписку» на уведомления обо всех изменениях в соответствующей странице. Отныне, при каждом барьере, в зеркало этого процесса будут присылаться изменившиеся (если кто-то, включая сам данный процесс, их изменил) значения этих данных. Пусть первое за время работы программы обращение к некоторому чужому для данного процесса элементу массива общей памяти есть чтение, а не запись. Сам факт обращение означает, что «подписка оформлена», но «первой доставки» (первого барьера после первого обращения) еще не было. Какое значение будет прочитано при этом, первом за время работы программы, обращении по чтению? Программист вправе рассчитывать, что будет прочитано какое-то из значений, которому прочитанный элемент массива был равен в какой-то момент времени, не раньше предыдущего барьера. В терминах используемой нами метафоры, будет организована «однократная спец-доставка» данного, индивидуального значения в зеркало данного процесса. Именно так работают версии RefNUMA для «штатных» (Qlogic shmem, Shmem-Экспресс) реализаций shmem. В том, что это так, можно убедиться, убрав в Примере 3 из настоящего документа барьер перед записью данных в массив общей памяти, и посмотрев, как именно изменятся данные, читаемые из массива процессом номер 3 до записи в свою часть массива. В версии RefNUMA на базе microshmem «однократная спец-доставка» реализована не будет. Первое за время работы программы обращение процесса к чужому для данного процесса элементу массива общей памяти, являющееся обращением на чтение, вернет непредсказуемое значение. В частности, это может быть нуль, или «мусор», а может быть и «правильное» значение, если, например, другие данные из той же страницы читались впервые до предыдущего барьера («подписка оформляется» на страницу целиком). Эта особенность первого чтения чужих данных создает очевидные неудобства для программиста. В некоторых простых программах эти неудобства можно побороть, выполнив специальный цикл предварительного обращения к чужим элементам массива общей памяти в самом начале работы программы. Для этого можно использовать специальные системные функции в составе microshmem: touch_long( long val ) и touch_double( double val ), прототипы которых находятся в заголовочном файле microshmem.h. Единственное назначение этих функций – гарантированно «тронуть» аргумент вызова в первый раз, чтобы после ближайшего барьера обращения к этим данным из тронувшего их процесса возвращали правильное значение. ◄ Пример 10 Приложение 2 ► |
![]() |
||||||||||||||||||||||||||||||||
Тел. +7(499)220-79-72; E-mail: inform@kiam.ru |