Организация параллельного ввода-вывода для кластеров с многоядерными процессорами, страница 3
Описание файла
PDF-файл из архива "Организация параллельного ввода-вывода для кластеров с многоядерными процессорами", который расположен в категории "". Всё это находится в предмете "дипломы и вкр" из 12 семестр (4 семестр магистратуры), которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 3 страницы из PDF
Навигиция внутрифайла с помощью MPI_File_seek∙ IO_TEST_FILE_RW_SEP_FILES - Метод на основе индивидуальных операций ввода-вывода. Запись каждым процессом в отдельный файл.∙ IO_TEST_FILE_RW_IO_SERV_SEP_FILES от 1 до 128 - Методна основе выделения процессов ввода-вывода, где число означаетчисло этих серверов.Отсутствие значения в таблице вызвано невозможностью проведениятеста из-за недостаточного количества памяти на узле.Как видно из графиков времени работы, наилучшие результаты показывают два метода: запись каждым процессом в отдельный файл изапись с помощью разбиения на группы и выделения процессов вводавывода. На других машинах ситуация в целом аналогичная.
Поэтомуэти два метода были выбраны для реализации записи распределённыхмассивов внутри DVM-системы.176. Описание практической части6.1. Система тестовВ ходе реализации была создана система тестирования и подготовлен набор тестов для проведения замеров призводительности различныхсхем ввода-вывода. Система написана на языке C++ и представляет изсебя основной модуль и набор классов-тестов реализующих конкретныеметоды.Рис. 4: Схема организации системы тестированияНа вход системе тестирования подаются такие параметры, как: диапазон размеров данных, в котором проводить тесты, число процессоров,число итераций записи и чтения для расчёта среднего времени исполнения.186.2. DVM-системаДля организации ввода-вывода распределённых массивов в системеподдержки времени исполнения для языка C-DVM реализован ряд функций, таких как dvm_fopen, dvm_fclose, dvm_fread, dvm_fwrite, привоизводящих открытие, закрытие файлов и чтение/запись массивов данных.
Вызов одной из этих функций приводит к последовательному вызову цепочки обработчиков и, в конечном итоге, вызову соответствующейстандартной POSIX-функции. Например, вызов функции dvm_fread длячтения из файла распределённого массива приводит к следующей цепочке вызовов:dvm_fread() -> dvm_dfread() -> DisArrRead() -> dvm_ReadArray() -> fread()Новые способы ввода-вывода были добавлены путём изменения поведения промежуточных функций, а именно, например, фрагмент кодавызова dvm_ReadArray() был изменён на выбор одной из трёх функцийswitch(DisArrIOType){case 1: Res1 = dvm_ReadArraySep(stream, Ar, &FB); break;case 2: Res1 = dvm_ReadArrayIOGroup(stream, Ar, &FB); break;default: Res1 = dvm_ReadArray(stream, Ar, &FB); break;}Здесь переменная DisArrIOType - глобальная переменная, отвечающая за активный в данный момент способ ввода-вывода.
dvm_ReadArraySepи dvm_ReadArrayIOGroup - функции, реализующие первый и четвёртыйспособ ввода-вывода соответственно.6.2.1 Схема работы функцийПеременная DisArrIOType задаёт активный в данный момент способввода-вывода. Для её изменения в пользовательской программе можновызвать функцию системы поддержкиint dvm_iotypechange(int type);где type - идентификатор нужного метода.В процессе открытия файла на запись проверяется значение этойпеременной и, соответственно, открывается один общий файл, по одномуфайлу для каждого процесса или по одному файлу для каждого процессаввода-вывода. Размер группы, которой выделяется один процесс вводавывода, определяется глобальной переменной19intIOGroupSize;и также может быть настроен пользователем.При чтении/записи каждым процессом в свой файл не возникает дополнительных коммуникационных расходов на передачу данных междупроцессами.
При использовании метода с выделением процессов вводавывода проверяется возможность сохранения в памяти этого процессавсех необходимых данных. В случае неудачи выделения необходимогообъёма памяти, данные принимаются и записываются частями, соответствующими подмассивам исходного распределённого массива.Для обеспечения возможности чтения данных при запуске программы на числе процессоров отличном от того, на котором производиласьзапись, при записи формируется файл описания %filename%.dvmio.meta,где %filename% - имя открываемого файла. В этот файл заносится информация о числе процессоров, на которых запущена программа, и используемый в данный момент метод ввода-вывода.
Файл описания используется при чтении таким образом:∙ При открытии файла на чтение проверяется наличие соответствующего файла описания.∙ Если он отсутствует, выбирается простой метод ввода-вывода длячтения этого файла∙ Если файл описания есть, то из него выбирается информация оспособе ввода-вывода, использованном при записи, и о числе процессоров, на которых была произведена запись.
Открываются всенужные файлы.∙ При вызове функции чтения распределённого массива сравнивается текущая конфигурация с конфигурацией, при которой записанфайл. Если они совпадают, то происходит чтение с помощью соответствующей функции чтения распределённого массива.∙ Если не совпадают, то способ чтения меняется на тот, при котором произошла запись, производится чтение всех нужных файлов.При этом одному процессу может достаться несколько файлов, если сейчас задача запущена на меньшем числе узлов, или ни одного,если на большем.∙ Далее все прочитанные данные перераспределяются между процессами в соответствии с текущим распределением частей массива наузлах.206.2.2 РезультатыРис. 5: Время ввода-вывода на программе, реализующей двумерный метод ЯкобиНа рисунке 5 представлен результат работы реализованных методовввода-вывода.
Тестирование производилось на программе, написаной наязыке C-DVM, реализующей итерационный метод Якоби на двумернойсетке. Программа запускалась на 16-ти процессорах для обработки двумерных матриц ранга от 28 до 215 . Как видно из графика, при записираспределённой двумерной матрицы ранга 215 на 16-ти процессорах прииспользовании функции вывода каждым процессом в отдельный файлбыло достигнуто тридцатикратное ускорение.217. ЗаключениеВ данной работе были рассмотрены различные варианты организации параллельного ввода-вывода для кластеров с многоядерными процессорами, реализована система тестов для определения наилучшего способа ввода-вывода на конкретной архитектуре.
Были реализованы внутри DVM-системы два новых метода, позволяющих сохранять и читатьраспределённые массивы, используя ввод-вывод в отдельные для каждого процесса файлы, а также с помощью выделения специальных процессоров ввода-вывода.22Приложение А. Исходный код тестов/*IO_TEST_FILE_RW*/class io_test_file_rw: public io_test{char filename[10];MPI_File fh;MPI_Status status;public:io_test_file_rw(t_test_params& test_params): io_test(test_params){strcpy(filename, "datafile");strcpy(test_name, "IO_TEST_FILE_RW");if(test_params.rank == 0){fprintf(stdout,"Constructor %s\n", test_name);}local_data_A = new double[local_data_size];local_data_B = new double[local_data_size];}~io_test_file_rw(){delete[] local_data_A;delete[] local_data_B;}void write(){int count;MPI_File_open(MPI_COMM_WORLD, filename,MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);MPI_File_set_view(fh, real_nodes_rank * local_data_size * sizeof(double),MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);MPI_File_write(fh, local_data_A, local_data_size, MPI_DOUBLE, &status);MPI_File_sync(fh);MPI_Get_count(&status, MPI_DOUBLE, &count);if(count != local_data_size){fprintf(stderr, "Error while writing %d != %d on rank = %d\n\n",count, local_data_size, real_nodes_rank);}MPI_File_close(&fh);}23void read(){int count;MPI_File_open(MPI_COMM_WORLD, filename,MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);MPI_File_set_view(fh, real_nodes_rank * local_data_size * sizeof(double),MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);MPI_File_read(fh, local_data_B, local_data_size, MPI_DOUBLE, &status);MPI_Get_count(&status, MPI_DOUBLE, &count);if(count != local_data_size){fprintf(stderr, "Error while reading %d != %d on rank = %d\n\n",count, local_data_size, real_nodes_rank);}MPI_File_close(&fh);}};24/*IO_TEST_FILE_RW_SEP_FILES*/class io_test_file_rw_sep_files: public io_test{char filename[20];MPI_File fh;MPI_Status status;public:io_test_file_rw_sep_files(t_test_params& test_params): io_test(test_params){sprintf(filename, "datafile.%d", real_nodes_rank);strcpy(test_name, "IO_TEST_FILE_RW_SEP_FILES");if(test_params.rank == 0){fprintf(stdout,"Constructor %s\n", test_name);}local_data_A = new double[local_data_size];local_data_B = new double[local_data_size];}~io_test_file_rw_sep_files(){delete[] local_data_A;delete[] local_data_B;}void write(){int count;char err_buffer[MPI_MAX_ERROR_STRING];int resultlen;fh = 0;int ierr = MPI_File_open(MPI_COMM_SELF, filename,MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);if(ierr != MPI_SUCCESS){fprintf(stderr, "Error while opening on write [%s] on rank = %d\n",filename, real_nodes_rank);MPI_Error_string(ierr,err_buffer,&resultlen);fprintf(stderr,err_buffer);return;}MPI_File_write(fh, local_data_A, local_data_size,MPI_DOUBLE, &status);MPI_File_sync(fh);MPI_Get_count(&status, MPI_DOUBLE, &count);25if(count != local_data_size){fprintf(stderr, "Error while writing %d != %d on rank = %d\n\n",count, local_data_size, real_nodes_rank);}MPI_File_close(&fh);}void read(){int count;char err_buffer[MPI_MAX_ERROR_STRING];int resultlen;fh = 0;int ierr = MPI_File_open(MPI_COMM_SELF, filename,MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);if(ierr != MPI_SUCCESS){fprintf(stderr, "Error while opening on read [%s] on rank = %d\n",filename, real_nodes_rank);MPI_Error_string(ierr,err_buffer,&resultlen);fprintf(stderr,err_buffer);return;}MPI_File_read(fh, local_data_B, local_data_size,MPI_DOUBLE, &status);MPI_Get_count(&status, MPI_DOUBLE, &count);if(count != local_data_size){fprintf(stderr, "Error while reading %d != %d on rank = %d\n\n",count, local_data_size, real_nodes_rank);}MPI_File_close(&fh);}};26/*IO_TEST_FILE_RW_IO_SERV_SEP_FILES*/class io_test_file_rw_io_serv_sep_files: public io_test{char filename[20];MPI_File fh;MPI_Status status;int io_servers[1024], io_serv_comm_size, io_serv_comm_rank;MPI_Group world_group, real_group;MPI_Comm io_serv_comm;int myio_rank;int io_serv_num, group_size;public:io_test_file_rw_io_serv_sep_files(t_test_params& test_params,int io_serv_num_param) : io_test(test_params){sprintf(filename, "datafile.%d", test_params.rank);strcpy(test_name, "IO_TEST_FILE_RW_IO_SERV_SEP_FILES");io_serv_num = io_serv_num_param;group_size = test_params.world_size / io_serv_num;if(test_params.rank % group_size == 0) real_nodes_rank = -1;io_servers[0] = 0;for(int i = 1; i < io_serv_num; i++)io_servers[i] = io_servers[i - 1] + group_size;MPI_Comm_group(MPI_COMM_WORLD, &world_group);MPI_Group_excl(world_group, io_serv_num, io_servers, &real_group);MPI_Comm_create(MPI_COMM_WORLD, real_group, &Comm_Real_Nodes);if(test_params.rank % group_size != 0){MPI_Comm_rank(Comm_Real_Nodes, &real_nodes_rank);MPI_Comm_size(Comm_Real_Nodes, &real_nodes_count);}int color = test_params.rank / group_size;MPI_Comm_split (MPI_COMM_WORLD, color, test_params.rank, &io_serv_comm);MPI_Comm_size(io_serv_comm, & io_serv_comm_size);MPI_Comm_rank(io_serv_comm, & io_serv_comm_rank);if(real_nodes_rank == -1){myio_rank = io_serv_comm_rank;}myio_rank = 0;MPI_Barrier(MPI_COMM_WORLD);27local_data_size = test_params.data_size /(test_params.world_size - io_serv_num);int sz = local_data_size;if(myio_rank == io_serv_comm_rank) sz *= io_serv_comm_size - 1;if(test_params.rank < 2)sz += test_params.data_size % (test_params.world_size - io_serv_num);local_data_A = new double[sz];local_data_B = new double[sz];MPI_Barrier(MPI_COMM_WORLD);}~io_test_file_rw_io_serv_sep_files(){delete[] local_data_A;delete[] local_data_B;}void write(){char err_buffer[MPI_MAX_ERROR_STRING];int resultlen;MPI_Barrier(MPI_COMM_WORLD);int *displs = (int *)malloc(io_serv_comm_size*sizeof(int));int *rcounts = (int *)malloc(io_serv_comm_size*sizeof(int));displs[0] = 0;rcounts[0] = 0;for (int i = 1; i < io_serv_comm_size; ++i) {displs[i] = (i - 1) * local_data_size;rcounts[i] = local_data_size;}int sz = local_data_size;if(myio_rank == io_serv_comm_rank) sz *= io_serv_comm_size - 1;if(test_params.rank < 2)sz += test_params.data_size% (test_params.world_size - io_serv_num);if(test_params.rank / group_size == 0){rcounts[1] += test_params.data_size% (test_params.world_size - io_serv_num);for (int i = 2; i < io_serv_comm_size; ++i) {displs[i] += test_params.data_size% (test_params.world_size - io_serv_num);}}int ierr = MPI_Gatherv(local_data_A, sz, MPI_DOUBLE,local_data_A, rcounts, displs, MPI_DOUBLE, myio_rank, io_serv_comm);28if(ierr != MPI_SUCCESS){MPI_Error_string(ierr,err_buffer,&resultlen);fprintf(stderr,err_buffer);return;}free(displs);free(rcounts);if(myio_rank == io_serv_comm_rank){int count;fh = 0;int ierr = MPI_File_open(MPI_COMM_SELF, filename,MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);if(ierr != MPI_SUCCESS){fprintf(stderr, "Error while opening on write [%s] on rank = %d\n",filename, real_nodes_rank);MPI_Error_string(ierr,err_buffer,&resultlen);fprintf(stderr,err_buffer);return;}sz = local_data_size * (io_serv_comm_size - 1);if(test_params.rank == 0)sz += test_params.data_size % (test_params.world_size - io_serv_num);MPI_File_write(fh, local_data_A, sz, MPI_DOUBLE, &status);MPI_File_sync(fh);MPI_Get_count(&status, MPI_DOUBLE, &count);if(count != sz){fprintf(stderr, "Error while writing %d != %d on rank = %d\n\n",count, sz, real_nodes_rank);}MPI_File_close(&fh);}}void read(){int sz;if(myio_rank == io_serv_comm_rank){int count;char err_buffer[MPI_MAX_ERROR_STRING];int resultlen;fh = 0;29int ierr = MPI_File_open(MPI_COMM_SELF, filename,MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);if(ierr != MPI_SUCCESS){fprintf(stderr, "Error while opening on read [%s] on rank = %d\n",filename, real_nodes_rank);MPI_Error_string(ierr,err_buffer,&resultlen);fprintf(stderr,err_buffer);return;}sz = local_data_size * (io_serv_comm_size - 1);if(test_params.rank == 0)sz += test_params.data_size % (test_params.world_size - io_serv_num);MPI_File_read(fh, local_data_B, sz, MPI_DOUBLE, &status);MPI_Get_count(&status, MPI_DOUBLE, &count);if(count != sz){fprintf(stderr, "Error while reading %d != %d on rank = %d\n\n",count, sz, real_nodes_rank);}MPI_File_close(&fh);}int *displs = (int *)malloc(io_serv_comm_size*sizeof(int));int *rcounts = (int *)malloc(io_serv_comm_size*sizeof(int));displs[0] = 0;rcounts[0] = 0;for (int i = 1; i < io_serv_comm_size; ++i) {displs[i] = (i - 1) * local_data_size;rcounts[i] = local_data_size;}sz = local_data_size;if(myio_rank == io_serv_comm_rank) sz *= io_serv_comm_size - 1;if(test_params.rank < 2)sz += test_params.data_size% (test_params.world_size - io_serv_num);if(test_params.rank / group_size == 0){rcounts[1] += test_params.data_size% (test_params.world_size - io_serv_num);for (int i = 2; i < io_serv_comm_size; ++i) {displs[i] += test_params.data_size% (test_params.world_size - io_serv_num);}}MPI_Scatterv(local_data_B, rcounts, displs, MPI_DOUBLE,30local_data_B, sz, MPI_DOUBLE, myio_rank, io_serv_comm);free(displs);free(rcounts);}};Остальные тесты имеют незначительные отличия, такие как использование функции MPI_File_seek вместо MPI_File_set_view иMPI_File_write_all вместо MPI_File_write.Литература[1] «DVM-система», ИПМ им.
Келдыша РАН, Москва, Россия [HTML](http://www.keldysh.ru/dvm/)[2] MPI: A Message-Passing Interface Standard [pdf] http://www.mpiforum.org/docs/mpi-3.0/mpi30-report.pdf[3] А.С.Антонов // Параллельное программирование с использованием технологии OpenMP // Издательство московского университета,2009[4] Описание вычислительного комплекса IBM BlueGene/P. Высокопроизводительные вычисления на ВМК МГУ.
[HTML](http://hpc.cmc.msu.ru/bgp)[5] PARALLEL.RUСуперкомпьютер«Ломоносов».(http://parallel.ru/cluster/lomonosov.html)[HTML].