I'm exploring MPI in C++ and I wanted to parallelize the creation of a picture of the Mandelbrot set. I'm using the ppm format. Each processor builds its part and sends it back to the main process that receives it as MPI_CHAR. This is the code:
#include "mpi.h"
#include <iostream>
#include <string>
#include <fstream>
#include <complex>
using namespace std;
int mandelbrot(int x, int y, int width, int height, int max) {
complex<float> point((float) (y - height/2.0) * 4.0/width, (float) (x - width/2.0) * 4.0/width);
complex<float> z(0, 0);
unsigned int iteration = 0;
while (abs(z) < 4 && iteration < max) {
z = z * z + point;
iteration++;
}
return iteration;
}
int main(int argc, char **argv) {
int numprocs;
int myid;
int buff_size = 404270; // 200x200
char buff[buff_size];
int i;
MPI_Status stat;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
int width = 200, height = 200, max_iter = 1000;
if (myid == 0) {
ofstream image("mandel.ppm");
image << "P3\n" << width << " " << height << " 255\n";
for(i=1; i < numprocs; i++) {
MPI_Probe(i, 0, MPI_COMM_WORLD, &stat);
int length;
MPI_Get_count(&stat, MPI_CHAR, &length);
MPI_Recv(buff, length, MPI_CHAR, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
image << buff;
}
} else {
stringstream ss;
// proc rank: 1, 2, ..., n
int part = height/(numprocs-1), start = (myid - 1) * part, end = part * myid;
printf("%d -> %d\n", start, end);
for (int row = start; row < end; row++) {
for (int col = 0; col < width; col++) {
int iteration = mandelbrot(row, col, width, height, max_iter);
if (row == start) ss << 255 << ' ' << 255 << ' ' << 255 << "\n";
else if (iteration < max_iter) ss << iteration * 255 << ' ' << iteration * 20 << ' ' << iteration * 5 << "\n";
else ss << 0 << ' ' << 0 << ' ' << 0 << "\n";
}
}
printf("\n sizeof = %d\n", ss.str().length());
MPI_Send(ss.str().c_str(), ss.str().length(), MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
Code compilation:
$ mpic++ -std=c++0x mpi.mandel.cpp -o mpi.mandel
Running with 3 processes (process main + process rank 1 and 2)
$ mpirun -np 3 ./mpi.mandel
Resulting ppm pictures when running with 3, 4, and 5 process:
It seems that the point-to-point communication of sending-receiving is mixing the results when more than 3 processes try to send the MPI_CHAR elements to the main process. How can avoid this behavior?
It works when creating the buffer buff with the same length as the receiving message:
.
.
for (int i=1; i < numprocs; i++) {
MPI_Probe(i, 0, MPI_COMM_WORLD, &stat);
int length;
MPI_Get_count(&stat, MPI_CHAR, &length);
printf("\nfrom %d <<-- %d (stat.source=%d) Receiving %d chars\n", myid, i, stat.MPI_SOURCE, length);
char buff[length + 1];
MPI_Recv(buff, length, MPI_CHAR, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
buff[length] = '\0';
image << buff;
}
.
.
Thus, we don't need anymore the declaration at the beginning int buff_size = 404270; neither char buff[buff_size];
Related
I'm trying to use one-sided communications in MPI.
The following example consists of an array of 4 doubles that is split between 2 processes.
The first process writes 0, 1, 2, 3 in the distributed array while the second one subsequently tries to read it. Unfortunately, it doesn't work. I must be doing something wrong somewhere.
Thanks!
#include <mpi.h>
#include <iostream>
int main(){
MPI_Init(0, nullptr);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int n=2;
double* data, x;
MPI_Win window;
MPI_Alloc_mem(n*sizeof(double), MPI_INFO_NULL, &data);
MPI_Win_create(data, n*sizeof(float), sizeof(float), MPI_INFO_NULL, MPI_COMM_WORLD, &window);
int i;
MPI_Win_fence(0, window);
if(rank==0){
for(i=0; i<n*size; ++i){
x=i;
MPI_Put(&x, 1, MPI_DOUBLE, i/n, i%n, 1, MPI_DOUBLE, window);
}
MPI_Win_fence(0, window);
}else{
MPI_Win_fence(0, window);
for(i=0; i<n*size; ++i){
MPI_Get(&x, 1, MPI_DOUBLE, i/n, i%n, 1, MPI_DOUBLE, window);
std::cout << i << " " << i/n << " " << i%n << " => " << x << "\n";
}
}
MPI_Win_free(&window);
MPI_Free_mem(data);
MPI_Finalize();
return 0;
}
I need to send array pieces to all processes using MPI_Scatter then to get sum of all elements. Where should I initialize array then to scatter it? In root rank?
If I initialize array on root rank then other ranks dont get their data. Otherway I can initialize array for everyone (out of if(rank == root)...else), but it means, that I create array several times.
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <iostream>
#include <time.h>
using namespace std;
int main(int argc, char* argv[])
{
int size;
int rank;
srand(time(NULL));
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
int arr_size = size * 2;
int block = arr_size / (size);
int* B = new int[block];
if (rank == 0)
{
int* A = new int[arr_size];
cout << "generated array: " << endl;
for (int i = 0; i < arr_size; i++)
{
A[i] = rand() % 100;
cout << A[i] << " ";
}
cout << endl;
MPI_Scatter(A, block, MPI_INT, B, block, MPI_INT, 0, MPI_COMM_WORLD);
}
cout << "process " << rank << " received: " << endl;
for (int i = 0; i < block; i++)
{
cout << B[i] << " ";
}
cout << endl;
int local_sum = 0;
for (int i = 0; i < block; i++)
{
local_sum += B[i];
}
cout << "sum in process " << rank << " = " << local_sum << endl;
cout << endl;
int global_sum;
MPI_Reduce(&local_sum, &global_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0)
{
cout << "sum = " << global_sum << endl;
}
MPI_Finalize();
return 0;
}
I get something like this (only root rank got its data):
process 1 received:
process 3 received:
-842150451 -842150451
-842150451 -842150451
sum in process 1 = -1684300902
sum in process 3 = -1684300902
process 2 received:
-842150451 -842150451
sum in process 2 = -1684300902
process 0 received:
4 9
sum in process 0 = 13
sum = -757935397
MPI_Scatter() is a collective operation and must hence be invoked by all the ranks.
Declare int *A = NULL; on all ranks and only allocate and populate on rank zero.
int* A = NULL;
int* B = new int[block];
if (rank == 0)
{
A = new int[arr_size];
cout << "generated array: " << endl;
for (int i = 0; i < arr_size; i++)
{
A[i] = rand() % 100;
cout << A[i] << " ";
}
cout << endl;
}
MPI_Scatter(A, block, MPI_INT, B, block, MPI_INT, 0, MPI_COMM_WORLD);
I have the following OpenCl code:
kernel void vectorAddition(global read_only int* vector1, global read_only int* vector2, global write_only int* vector3)
{
int indx = get_global_id(0);
vector3[0] = vector1[0] + vector2[0];
}
and the host code:
# define __CL_ENABLE_EXCEPTIONS
#include <CL/cl.hpp>
#include <fstream>
#include <iostream>
#include <vector>
#include <cstdint>
#include <exception>
int main()
{
try
{
const long N_elements = 10;
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
std::vector<cl::Device> devices;
platforms[1].getDevices(CL_DEVICE_TYPE_ALL, &devices);
std::ifstream helloWorldFile("VectorAddition.cl");
std::string src(std::istreambuf_iterator<char>(helloWorldFile), (std::istreambuf_iterator<char>()));
cl::Program::Sources sources(1, std::make_pair(src.c_str(), src.length() + 1));
cl::Context context(devices);
cl::Program program(context, sources);
cl_int err = program.build(devices, "-cl-std=CL1.2");
int* vector1 = new int[N_elements];
for (int i = 0; i < N_elements; i++) vector1[i] = 1;
for (long i = 0; i < N_elements; i++) std::cout << vector1[i] << " ";
std::cout << std::endl;
cl::Buffer vec1Buff(context, CL_MEM_READ_ONLY, sizeof(int) * N_elements, vector1);
int* vector2 = new int[N_elements];
for (int i = 0; i < N_elements; i++) vector2[i] = 2;
for (long i = 0; i < N_elements; i++) std::cout << vector2[i] << " ";
std::cout << std::endl;
cl::Buffer vec2Buff(context, CL_MEM_READ_ONLY, sizeof(int) * N_elements, vector2);
int* vector3 = new int[N_elements];
cl::Buffer vec3Buff(context, CL_MEM_WRITE_ONLY, sizeof(int) * N_elements, vector3);
cl::Kernel kernel(program, "vectorAddition", &err);
kernel.setArg(0, vec1Buff);
kernel.setArg(1, vec2Buff);
kernel.setArg(2, vec3Buff);
cl::CommandQueue queue(context, devices[0]);
queue.enqueueWriteBuffer(vec1Buff, CL_TRUE, 0, sizeof(int) * N_elements, vector1);
queue.enqueueWriteBuffer(vec2Buff, CL_TRUE, 0, sizeof(int) * N_elements, vector2);
queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(N_elements), cl::NDRange(10));
queue.enqueueReadBuffer(vec3Buff, CL_TRUE, 0, sizeof(int) * N_elements, vector3);
for (long i = 0; i < N_elements; i++) std::cout << vector3[i] << " ";
delete[] vector1;
delete[] vector2;
delete[] vector3;
}
catch (cl::Error error)
{
std::cout << error.what() << "(" << error.err() << ")" << std::endl;
}
std::cin.get();
return 0;
}
I'v got the following output:
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 0 0 0 0 0 0 0 0 0
Why is there only the first item calculated ?
Specifications of my GPU:
platform name: NVIDIA CUDA,
platform profile: FULL_PROFILE,
platform version: OpenCL 1.2 CUDA 8.0.0,
platform vendor: NVIDIA Corporation,
platform extensions: cl_khr_global_int32_base_atomics, cl_khr_global_int32_extended_atomics, cl_khr_local_int32_base_atomics, cl_khr_local_int32_extended_atomics, cl_khr_fp64, cl_khr_byte_addressable_store, cl_khr_icd cl_khr_gl_sharing, cl_nv_compiler_options, cl_nv_device_attribute_query, cl_nv_pragma_unroll, cl_nv_d3d9_sharing, cl_nv_d3d10_sharing, cl_khr_d3d10_sharing, cl_nv_d3d11_sharing, cl_nv_copy_opts, cl_nv_create_buffer,
device name: GeForce 820M,
device vendor: NVIDIA Corporation,
device type: 4,
device max compute units: 2,
device max work item dimensions: 3,
device max work item sizes: 1024 1024 64,
device max workgroup size: 1024,
device max clk freq: 1250,
device addr bits: 64,
device max mem allocation size: 536870912,
device image support: 1
vector3[0] = vector1[0] + vector2[0];
All the items in your kernel sum and save to the same position, 0.
You should use the id of the item instead, so all of them work on different data:
kernel void vectorAddition(global read_only int* vector1, global read_only int* vector2, global write_only int* vector3)
{
int indx = get_global_id(0);
vector3[indx] = vector1[indx] + vector2[indx];
}
So I have a some code where I am using MPI_Bcast to send information from the root node to all nodes, but instead I want to get my P0 to send chunks of the array to individual processes.
How do I do this with MPI_Send and MPI_Receive?
I've never used them before and I don't know if I need to loop my MPI_Receive to effectively send everything or what.
I've put giant caps lock comments in the code where I need to replace my MPI_Bcast(), sorry in advance for the waterfall of code.
Code:
#include "mpi.h"
#include <stdio.h>
#include <math.h>
#define MAXSIZE 10000000
int add(int *A, int low, int high)
{
int res = 0, i;
for(i=low; i<=high; i++)
res += A[i];
return(res);
}
int main(argc,argv)
int argc;
char *argv[];
{
int myid, numprocs, x;
int data[MAXSIZE];
int i, low, high, myres, res;
double elapsed_time;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
if (myid == 0)
{
for(i=0; i<MAXSIZE; i++)
data[i]=1;
}
/* star the timer */
elapsed_time = -MPI_Wtime();
//THIS IS WHERE I GET CONFUSED ABOUT MPI_SEND AND MPI_RECIEVE!!!
MPI_Bcast(data, MAXSIZE, MPI_INT, 0, MPI_COMM_WORLD);
x = MAXSIZE/numprocs;
low = myid * x;
high = low + x - 1;
if (myid == numprocs - 1)
high = MAXSIZE-1;
myres = add(data, low, high);
printf("I got %d from %d\n", myres, myid);
MPI_Reduce(&myres, &res, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
/* stop the timer*/
elapsed_time += MPI_Wtime();
if (myid == 0)
printf("The sum is %d, time taken = %f.\n", res,elapsed_time);
MPI_Barrier(MPI_COMM_WORLD);
printf("The sum is %d at process %d.\n", res,myid);
MPI_Finalize();
return 0;
}
You need MPI_Scatter. A good intro is here: http://mpitutorial.com/tutorials/mpi-scatter-gather-and-allgather/
I think in your code it could look like this:
elements_per_proc = MAXSIZE/numprocs;
// Create a buffer that will hold a chunk of the global array
int *data_chunk = malloc(sizeof(int) * elements_per_proc);
MPI_Scatter(data, elements_per_proc, MPI_INT, data_chunk,
elements_per_proc, MPI_INT, 0, MPI_COMM_WORLD);
If you really want use MPI_Send and MPI_Recv, then you can use something like this:
int x = MAXSIZE / numprocs;
int *procData = new int[x];
if (rank == 0) {
for (int i = 1; i < num; i++) {
MPI_Send(data + i*x, x, MPI_INT, i, 0, MPI_COMM_WORLD);
}
} else {
MPI_Recv(procData, x, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
}
I'm trying to divide the string str_data1 and send it to the slave processors in MPI_COMM_WORLD, but I am getting an error on the slaves.
The error looks something like this:
2
2
3
�E0� �E0�� �E0O�
#include <stdio.h>
#include <string.h>
#include "mpi.h"
int main(int argc, char* argv[]) {
int rank;
int p;
MPI_Status status;
int msg_size = 0;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &p);
char *str_data1 = "Samsung Galaxy Tab S 10.5 Wi-Fi, Tablet PC Android";
int len1 = strlen(str_data1), i;
char *str1[len1];
char *a[len1];
if (rank == 0) {
char *ds = strdup(str_data1);
int n = 0;
a[n] = strtok(ds, " ,");
while (a[n] && n < len1) {
a[++n] = strtok(NULL, " ,");
}
int chunk = n / p;
int str_size = chunk;
for (i = 1; i < p; i++) {
if (i == p - 1) {
str_size = n - chunk * i;
}
MPI_Send(&str_size, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
MPI_Send(&a, str_size + 1, MPI_CHAR, i, 0, MPI_COMM_WORLD);
}
} else {
MPI_Recv(&msg_size, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
char messagsg_size];
printf(" \n %d ", msg_size);
MPI_Recv(&message, msg_size + 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD,
&status);
printf(" \n %s ", message);
}
MPI_Finalize();
return 0;
}
does anyone have any clues what im doing wrong? Thanks.
Some newer compilers will give you nice warning about these sorts of things. It's been very nice ever since Clang added this. If you were to use that compiler, you'd see this:
$ mpic++ asdf.c
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated
asdf.c:17:23: warning: conversion from string literal to 'char *' is deprecated [-Wc++11-compat-deprecated-writable-strings]
char *str_data1 = "Samsung Galaxy Tab S 10.5 Wi-Fi, Tablet PC Android";
^
asdf.c:39:22: warning: argument type 'char *(*)[len1]' doesn't match specified 'MPI' type tag that requires 'char *' [-Wtype-safety]
MPI_Send(&a, str_size + 1, MPI_CHAR, i, 0, MPI_COMM_WORLD);
^~ ~~~~~~~~
asdf.c:47:18: warning: argument type 'char (*)[msg_size]' doesn't match specified 'MPI' type tag that requires 'char *' [-Wtype-safety]
MPI_Recv(&message, msg_size + 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD,
^~~~~~~~ ~~~~~~~~
3 warnings generated.
That shows you that you're using the wrong types for your character arrays. You should be using either just character arrays:
char a[len1];
or character pointers:
char *a;
Either way, you need to do some code rework to make that work correctly. Specifically, this section:
char *ds = strdup(str_data1);
int n = 0;
a[n] = strtok(ds, " ,");
while (a[n] && n < len1) {
a[++n] = strtok(NULL, " ,");
}
I don't think that string tokenizer is doing what you think it is since it would just be overwriting itself over and over.
Thanks Wesly.. i tired with structure.. looks like its working..
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<mpi.h>
struct tokan {
char buff[30];
} t[50];
int main(int argc, char* argv[]) {
int rank;
int p;
MPI_Status status;
int msg_size = 0, i = 0, j = 0, msg_size1 = 0;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &p);
int n = 0, m = 0, k = 0, N = 0, M = 0;
char *str1, *str2, *str_data2, *str_data1;
if (rank == 0) {
str_data1 =
"processes in this communicator will now abort, and potentially your MPI job";
str_data2 =
"The behavior is undefined if lhs or rhs are not pointers to null-terminated byte strings";
str1 = (char *) malloc(strlen(str_data1) * sizeof(char));
str2 = (char *) malloc(strlen(str_data2) * sizeof(char));
strcpy(str1, str_data1);
strcpy(str2, str_data2);
int len;
M = strlen(str2);
N = strlen(str1);
char *ptr;
k = 0;
char *ds = strdup(str_data1);
ptr = strtok(ds, " ,");
while (ptr != NULL) {
++n;
len = strlen(ptr);
for (j = 0; j < len; j++) {
t[k].buff[j] = *(ptr + j);
}
//printf(" %s ", t[k].buff);
k++;
ptr = strtok(NULL, " ,");
}
int chunk = n / p;
int str_size = chunk, cnt = 0;
j = chunk;
for (i = 1; i < p; i++) {
if (i == p - 1) {
str_size = n - chunk * i;
}
MPI_Send(&str_size, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
for (cnt = 0; cnt < str_size; cnt++) {
/*printf("process 0: %c %d %d %d %d %d %d\n", t[j].buff, str_size, n,
chunk, cnt, j, l);*/
MPI_Send(t[j].buff, 100, MPI_CHAR, i, 0, MPI_COMM_WORLD);
j++;
}
}
str_size = chunk;
for (i = 1; i < p; i++) {
MPI_Send(&M, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
//printf("process 0: %s %d %d %d %d\n", str2, n, chunk, cnt, j);
MPI_Send(str2, M, MPI_CHAR, i, 0, MPI_COMM_WORLD);
}
} else {
MPI_Recv(&msg_size, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
int k;
for (k = 0; k < msg_size; k++) {
MPI_Recv(t[k].buff, 100, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &status);
printf(" \nstr1: %s %d %d %d\n", t[k].buff, rank, msg_size, k);
}
printf("***************");
MPI_Recv(&M, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
str2 = (char *) malloc((M + 1) * sizeof(char));
MPI_Recv(str2, M, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &status);
str2[M] = '\0';
printf(" \nstr2: %s %d %d %d \n", str2, rank, k, M);
}
if (rank == 0)
free(str1);
free(str2);
MPI_Finalize();
return 0;
}