Полезное:
Как сделать разговор полезным и приятным
Как сделать объемную звезду своими руками
Как сделать то, что делать не хочется?
Как сделать погремушку
Как сделать так чтобы женщины сами знакомились с вами
Как сделать идею коммерческой
Как сделать хорошую растяжку ног?
Как сделать наш разум здоровым?
Как сделать, чтобы люди обманывали меньше
Вопрос 4. Как сделать так, чтобы вас уважали и ценили?
Как сделать лучше себе и другим людям
Как сделать свидание интересным?
Категории:
АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника
|
Return 0;}
Пример результата работы программы:
Input n: 1000 S = 5175
Рассмотренная программа полностью работоспособна и решает поставленную задачу. Однако данный код не позволяет отследить и проанализировать процессы, вызванные распараллеливанием программы. Во многих случаях, особенно на этапе отладки программы, в ее листинг полезно добавлять команды вывода сведений о выполняемых этапах работы и промежуточных результатах. Ниже приводится пример этой же программы с добавленными командами вывода информационных сообщений.
#include "stdafx.h" #include <stdio.h> #include <conio.h> #include <mpi.h> #include <iostream> using namespace std;
int main(int argc, char* argv[]) { int proc_rank, proc_count; MPI_Status Status;
MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &proc_count); MPI_Comm_rank(MPI_COMM_WORLD, &proc_rank);
double t0, t1, t2, t3, t4; // Переменные для измерения времени t0=MPI_Wtime(); // Запоминаем начальный момент времени
//=================================================================== if ((proc_rank==0) && (proc_count>1)) // Если это "главный" процесс { // и процессов минимум два int n; // Размер массива (изначально неизвестен)
cout << "Input n: "; cin >> n; // Вводим с клавиатуры размер массива
t1 = MPI_Wtime(); // Запоминаем момент времени до начала работы cout << "Process 0 ("<< MPI_Wtime()-t0 << "): started.\n"; // Заводим массив на n элементов и заполняем его случайными числами: int *mas; // Указатель на массив типа int
mas = new int [n]; // Выделяем память под массив (без проверки) for (int i=0; i<n; i++) // и заполняем случайными числами от 0 до 10 mas[i]=rand()%11;
// Распараллелим поиск суммы ровно на два процесса. // Даже если доступных процессорных ядер будет больше, // они не будут использоваться. Ровно два процесса: // текущий процесс с рангом 0 и процесс с рангом 1.
// Поскольку процесс 1 не знает о размере массива, он // не может знать, какой объем сообщения ему принимать. // Поэтому мы сначала передадим процессу 1 число, равное // размеру массива. Так процесс 1 сможет подготовить // память под принимаемый массив. cout << "Process 0 ("<< MPI_Wtime()-t0 << "): sanding size...\n"; MPI_Send (&n, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); cout <<"Process 0 ("<< MPI_Wtime()-t0 << "): size=" << n << " sended.\n";
// Теперь отсылаем первую половину массива процессу 1 cout << "Process 0 ("<< MPI_Wtime()-t0 << "): sanding array...\n"; MPI_Send (mas, n/2, MPI_INT, 1, 0, MPI_COMM_WORLD); cout << "Process 0 ("<< MPI_Wtime()-t0 << "): array sended.\n";
// Пока процесс 1 обрабатывает первую половину массива, // делаем пока свою часть работы: ищем сумму элементов // оставшейся части массива int S=0; for (int i=n/2; i<n; i++) S=S+mas[i];
cout << "Process 0 ("<< MPI_Wtime()-t0 << "): local summa calculated, S=" << S << ".\n";
// Готовимся получать результат работы процесса 1. // Результатом будет одно число типа int. int S1; // Место под результат
// Ждем сообщение от процесса 1 cout << "Process 0 ("<< MPI_Wtime()-t0 << "): waiting answer from process 1...\n"; MPI_Recv (&S1, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &Status);
cout <<"Process 0 ("<< MPI_Wtime()-t0 << "): answer received, S1=" << S1 << ".\n";
S=S+S1; // Складываем обе суммы cout << "Process 0 ("<< MPI_Wtime()-t0 << "): S = " << S << endl;
delete [] mas; // Освобождаем память, выделенную под массив
cout << "Process 0 ("<< MPI_Wtime()-t0 << "): finished.\n";
t2 = MPI_Wtime(); // Запоминаем момент времени по окончании работы cout <<"Process 0 ("<<MPI_Wtime()-t0<<"): total time = "<<(t2-t1)<<" seconds.\n";
} // if - процесс 0 //=================================================================== if (proc_rank==1) // Если это процесс с номером 1 { int size; // Размер принимаемого массива int Summa; // Сумма его элементов
t3 = MPI_Wtime(); // Запоминаем момент времени до начала работы cout << " Process 1 ("<< MPI_Wtime()-t0 << "): started.\n";
// Готовимся принять размер массива // Ждем сообщение от процесса 0 cout << " Process 1 ("<< MPI_Wtime()-t0 << "): waiting size...\n"; MPI_Recv (&size, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &Status); cout << " Process 1 ("<< MPI_Wtime()-t0 << "): size=" << size << " received.\n";
size=size/2; // Мы получили размер всего массива, // но принимать будем только половину
// Динамически выделяем память под принимаемый массив int *ptr = new int [size];
// Готовимся принять массив данных // Ждем сообщение от процесса 0 cout << " Process 1 ("<< MPI_Wtime()-t0 << "): waiting array...\n"; MPI_Recv (ptr, size, MPI_INT, 0, 0, MPI_COMM_WORLD, &Status); cout << " Process 1 ("<< MPI_Wtime()-t0 << "): array received.\n";
// Ищем сумму элементов массива Summa=0; for (int i=0; i<size; i++) Summa+=ptr[i];
cout << " Process 1 ("<<MPI_Wtime()-t0<<"): local summa calculated:"<<Summa<<endl;
// Отсылаем ответ процессу 0 cout << " Process 1 ("<< MPI_Wtime()-t0 << "): sanding Summa...\n"; MPI_Send (&Summa, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); cout << " Process 1 ("<< MPI_Wtime()-t0 << "): summa sended.\n";
delete [] ptr; // Освобождаем память
cout << " Process 1 ("<< MPI_Wtime()-t0 << "): finished.\n"; t4 = MPI_Wtime(); // Запоминаем момент времени по окончании работы cout<<" Process 1 ("<< MPI_Wtime()-t0 << "): total time = "<<(t4-t3)<<" sec.\n";
} // if - процесс 1 //=================================================================== MPI_Finalize(); // закрываем MPI-библиотеку return 0; }
В начале программы объявляются переменные t0-t4 типа double. Переменная t0 служит для момента времени запуска процесса, переменные t1 и t2 – для хранения начального и конечного времени работы процесса 0, t3 и t4 – процесса 1. В каждом из операторов cout происходит печать значения MPI_Wtime()-t0, которое равно количеству секунд, прошедших от момента t0. Ниже приведен пример результатов работы программы. Для удобства дальнейшего пояснения каждая строка была вручную пронумерована.
1 Input n: 100000000 2 Process 0 (4.36572): started. 3 Process 0 (6.53677): sanding size... 4 Process 0 (6.53681): size=100000000 sended. 5 Process 0 (6.53682): sanding array... 6 Process 0 (6.65851): array sended. 7 Process 0 (6.77024): local summa calculated, S=250004717. 8 Process 0 (6.77027): waiting answer from process 1... 9 Process 0 (6.7703): answer received, S1=249962239. 10 Process 0 (6.77031): S = 499966956 11 Process 1 (3.01869e-007): started. 12 Process 1 (8.78439e-005): waiting size... 13 Process 1 (6.53681): size=100000000 received. 14 Process 1 (6.60401): waiting array... 15 Process 1 (6.65853): array received. 16 Process 1 (6.77026): local summa calculated:249962239 17 Process 0 (6.86812): finished. 18 Process 0 (6.86815): total time = 2.50244 seconds. 19 Process 1 (6.77029): sanding Summa... 20 Process 1 (6.7703): summa sended. 21 Process 1 (6.82206): finished. 22 Process 1 (6.82216): total time = 6.82216 sec.
Дадим пояснение некоторых моментов в работе программы. 1. В строке 2 указано время старта процесса 0 (4.36 сек.), в строке 11 – время старта процесса 1 (почти ноль). Дело в том, что время старта процесса 0 отсчитывается в программе с момента после ввода значения n с клавиатуры. Очевидно, что в данном примере, ввод с клавиатуры занял 4.36 секунды. Время старта процесса 1 отсчитывается сразу с момента запуска процесса 1, то есть почти сразу после момента времени t0. Но процесс1, запустившись, пока ничего не делает и ждет получения сообщения от процесса 0 (строка 12). Таким образом, 4.36 секунды – это время, потраченное на ввод с клавиатуры, и его не следует относить ко времени обработки массива данных. 2. Между моментами, отмеченными в строках 3 и 4, происходит отправка процессом 0 процессу 1 значения n. Как можно видеть, отправка одного значения типа int занимает десятитысячную долю секунды. Следует, однако, понимать, что в строке 4 указан момент времени, когда сообщение было отправлено процессом 0. Это не значит, что в этот же момент оно было получено процессом 1. Это значит, что в данный момент система MPI приняла сообщение от процесса 0. В реальной жизни этот момент соответствует ситуации, когда с телефона было отправлено SMS-сообщение, или когда бумажное письмо было опущено в почтовый ящик. Сколько времени будет осуществляться доставка сообщения – неизвестно. В худшем случае оно вообще не будет доставлено. Тем не менее, процессу 0 было сообщено, что сообщение ушло, о чем он проинформировал нас в строке 4. К слову говоря, строка 13 соответствует моменту времени, когда значение n было получено процессом 1. Очевидно, что этот момент совпадает с моментом отправки сообщения процессом 0 (строка 4). Поскольку пример программы запускался на двух ядрах одного процессора, передача сообщения произошла почти мгновенно. Однако в общем случае передача сообщения может занимать достаточно большое время. 3. Строки 5 и 6 соответствуют началу и концу отправки процессом 0 сообщения, содержащего половину массива (50 млн. чисел типа int). Момент вывода строки 15 соответствует моменту приема данного сообщения процессом 1. Разница между временем в строке 15 и временем в строке 5 говорит о том, что процесс передачи и приема этого сообщения занял 0.12 секунды. 4. Все строки, выдаваемые программой на экран, следуют не в хронологическом порядке. Студентам предлагается самостоятельно расставить их в порядке возникновения соответствующих событий. Подсказка: первой будет идти строка 11, затем 12, затем 2, затем 3 и т.д. Также необходимо обратить внимание на следующее. Как известно, переменные t0-t4, объявляемые в функции main(), создаются внутри каждого процесса. Процесс 0 имеет свои переменные t0-t4, процесс 1 – свои. После создания переменных каждый процесс выполняет команду
t0=MPI_Wtime();
занося в переменную t0 текущий момент времени. Программист, использующий MPI, должен понимать, что два процесса не могут быть запущены в абсолютно один и тот же момент времени. Моменты запуска каждого из процессов ВСЕГДА различаются. Если процессы запускаются на одном компьютере, то их запуском руководит операционная система, которая сначала запустит один процесс, а потом (пусть даже сразу, без лишних задержек) – второй. Но это будут разные моменты времени. Если в программу после команды t0=MPI_Wtime() добавить строки
|