| @ -0,0 +1,56 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,102 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| #include <boost/thread.hpp> | |||
| using namespace std; | |||
| const int NUM_OF_THREADS = 100; | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| std::cout <<c[i][j] <<"\t"; | |||
| } | |||
| std::cout << std::endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| double **add(double **a,double **b,double **c, int n, int start,int end) { | |||
| for (int i=start;i<end;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| //mtx.lock(); | |||
| c[i][j] = a[i][j] + b[i][j]; | |||
| //mtx.unlock(); | |||
| } | |||
| } | |||
| return c; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage:" << argv[0] << "<matrix.size>" << endl; | |||
| } | |||
| int n=std::atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::thread_group tg; | |||
| //std::vector<boost::thread> threads; | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res=boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| int rows_per_thread = n / NUM_OF_THREADS; | |||
| int remaining_rows = n % NUM_OF_THREADS; | |||
| int current_row = 0; | |||
| for(int i = 0; i < NUM_OF_THREADS && current_row < n; i++){ | |||
| int rows_to_process = rows_per_thread + (i< remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| try { | |||
| tg.create_thread(boost::bind(add,a,b,c,n,current_row,end_row)); | |||
| } catch (const exception &e) { | |||
| cerr << "create_thread failed, thread:" << i << "," << e.what() << endl; | |||
| } | |||
| current_row = end_row; | |||
| } | |||
| tg.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res3 =boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| std::cout <<" n,setup(us),exec*(us)" << n<<","<<res.count() <<","<<res3.count()<< std::endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,174 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| #define DEBUG 0 | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 100; | |||
| struct SharedMatrix { | |||
| vector<double*> chunks; | |||
| double** row_pointers; | |||
| int size; | |||
| }; | |||
| void create_shared_matrix(SharedMatrix** matrix, int n) { | |||
| const size_t CHUNK_SIZE = 1024 * 1024; // 1MB chunks | |||
| size_t total_size = n * n * sizeof(double); | |||
| size_t num_chunks = (total_size + CHUNK_SIZE - 1) / CHUNK_SIZE; | |||
| *matrix = new SharedMatrix; | |||
| (*matrix)->size = n; | |||
| (*matrix)->chunks.reserve(num_chunks); | |||
| // Allocate memory chunks | |||
| for (size_t i = 0; i < num_chunks; ++i) { | |||
| size_t chunk_size = min(CHUNK_SIZE, total_size - i * CHUNK_SIZE); | |||
| void* ptr = mmap(NULL, chunk_size, | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, | |||
| -1, 0); | |||
| if (ptr == MAP_FAILED) { | |||
| perror("mmap failed"); | |||
| // Clean up already allocated chunks | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, CHUNK_SIZE); | |||
| } | |||
| delete *matrix; | |||
| exit(1); | |||
| } | |||
| (*matrix)->chunks.push_back(static_cast<double*>(ptr)); | |||
| } | |||
| // Create row pointers | |||
| (*matrix)->row_pointers = new double*[n]; | |||
| size_t current_chunk = 0; | |||
| size_t offset_in_chunk = 0; | |||
| for (int i = 0; i < n; ++i) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->chunks[current_chunk]) + offset_in_chunk); | |||
| offset_in_chunk += n * sizeof(double); | |||
| // Move to next chunk if needed | |||
| while (offset_in_chunk >= CHUNK_SIZE) { | |||
| offset_in_chunk -= CHUNK_SIZE; | |||
| current_chunk++; | |||
| // Handle case where row spans chunks | |||
| if (i < n - 1) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->row_pointers[i]) - CHUNK_SIZE + offset_in_chunk); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void cleanup_matrix(SharedMatrix** matrix) { | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, (*matrix)->chunks.size() > 1 ? 1024 * 1024 : | |||
| (*matrix)->size * (*matrix)->size * sizeof(double)); | |||
| } | |||
| delete[] (*matrix)->row_pointers; | |||
| delete *matrix; | |||
| *matrix = nullptr; | |||
| } | |||
| void init_matrix(SharedMatrix** matrix, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < (*matrix)->size; ++i) { | |||
| for (int j = 0; j < (*matrix)->size; ++j) { | |||
| (*matrix)->row_pointers[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| int addV(int start_row, int end_row, SharedMatrix** a, SharedMatrix** b, SharedMatrix** c) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < (*a)->size; ++j) { | |||
| (*c)->row_pointers[i][j] = (*a)->row_pointers[i][j] + (*b)->row_pointers[i][j]; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| SharedMatrix *a = nullptr, *b = nullptr, *c = nullptr; | |||
| create_shared_matrix(&a, n); | |||
| create_shared_matrix(&b, n); | |||
| create_shared_matrix(&c, n); | |||
| init_matrix(&a); | |||
| init_matrix(&b); | |||
| init_matrix(&c, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Calculate work distribution | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process | |||
| addV(current_row, end_row, &a, &b, &c); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all children | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(&a); | |||
| cleanup_matrix(&b); | |||
| cleanup_matrix(&c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,174 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| #define DEBUG 0 | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 24; | |||
| struct SharedMatrix { | |||
| vector<double*> chunks; | |||
| double** row_pointers; | |||
| int size; | |||
| }; | |||
| void create_shared_matrix(SharedMatrix** matrix, int n) { | |||
| const size_t CHUNK_SIZE = 1024 * 1024; // 1MB chunks | |||
| size_t total_size = n * n * sizeof(double); | |||
| size_t num_chunks = (total_size + CHUNK_SIZE - 1) / CHUNK_SIZE; | |||
| *matrix = new SharedMatrix; | |||
| (*matrix)->size = n; | |||
| (*matrix)->chunks.reserve(num_chunks); | |||
| // Allocate memory chunks | |||
| for (size_t i = 0; i < num_chunks; ++i) { | |||
| size_t chunk_size = min(CHUNK_SIZE, total_size - i * CHUNK_SIZE); | |||
| void* ptr = mmap(NULL, chunk_size, | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, | |||
| -1, 0); | |||
| if (ptr == MAP_FAILED) { | |||
| perror("mmap failed"); | |||
| // Clean up already allocated chunks | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, CHUNK_SIZE); | |||
| } | |||
| delete *matrix; | |||
| exit(1); | |||
| } | |||
| (*matrix)->chunks.push_back(static_cast<double*>(ptr)); | |||
| } | |||
| // Create row pointers | |||
| (*matrix)->row_pointers = new double*[n]; | |||
| size_t current_chunk = 0; | |||
| size_t offset_in_chunk = 0; | |||
| for (int i = 0; i < n; ++i) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->chunks[current_chunk]) + offset_in_chunk); | |||
| offset_in_chunk += n * sizeof(double); | |||
| // Move to next chunk if needed | |||
| while (offset_in_chunk >= CHUNK_SIZE) { | |||
| offset_in_chunk -= CHUNK_SIZE; | |||
| current_chunk++; | |||
| // Handle case where row spans chunks | |||
| if (i < n - 1) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->row_pointers[i]) - CHUNK_SIZE + offset_in_chunk); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void cleanup_matrix(SharedMatrix** matrix) { | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, (*matrix)->chunks.size() > 1 ? 1024 * 1024 : | |||
| (*matrix)->size * (*matrix)->size * sizeof(double)); | |||
| } | |||
| delete[] (*matrix)->row_pointers; | |||
| delete *matrix; | |||
| *matrix = nullptr; | |||
| } | |||
| void init_matrix(SharedMatrix** matrix, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < (*matrix)->size; ++i) { | |||
| for (int j = 0; j < (*matrix)->size; ++j) { | |||
| (*matrix)->row_pointers[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| int addV(int start_row, int end_row, SharedMatrix** a, SharedMatrix** b, SharedMatrix** c) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < (*a)->size; ++j) { | |||
| (*c)->row_pointers[i][j] = (*a)->row_pointers[i][j] + (*b)->row_pointers[i][j]; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| SharedMatrix *a = nullptr, *b = nullptr, *c = nullptr; | |||
| create_shared_matrix(&a, n); | |||
| create_shared_matrix(&b, n); | |||
| create_shared_matrix(&c, n); | |||
| init_matrix(&a); | |||
| init_matrix(&b); | |||
| init_matrix(&c, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Calculate work distribution | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process | |||
| addV(current_row, end_row, &a, &b, &c); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all children | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(&a); | |||
| cleanup_matrix(&b); | |||
| cleanup_matrix(&c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,130 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| #define DEBUG 0 | |||
| using namespace std; | |||
| // Create a mmap-allocated matrix with pointers | |||
| double** create_shared_matrix(int n) { | |||
| int data_size = n * n * sizeof(double); | |||
| void* hint = (void*)0x10000000; // Example hint address | |||
| double* data = (double*)mmap(hint, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap"); | |||
| exit(1); | |||
| } | |||
| // Create an index matrix locally (does not need to be shared) | |||
| double** mat = new double*[n]; | |||
| for (int i = 0; i < n; ++i) | |||
| mat[i] = &data[i * n]; | |||
| return mat; | |||
| } | |||
| // Detach mmap and delete indexes | |||
| void cleanup_matrix(double** mat, int n) { | |||
| munmap(mat[0], n * n * sizeof(double)); | |||
| delete[] mat; | |||
| } | |||
| void init_matrix(double** mat, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) | |||
| for (int j = 0; j < n; ++j) | |||
| if (zero) | |||
| mat[i][j] = 0 ; | |||
| else | |||
| mat[i][j]=dis(gen); | |||
| } | |||
| //Add vector column | |||
| int addV(int p, double **a, double**b, double **c, int n, int offset) { | |||
| for (int j=0;j<n;j++) | |||
| c[offset-1][j]=a[offset-1][j]+b[offset-1][j]; | |||
| return getpid()%getppid(); | |||
| } | |||
| void print_matrix(double** mat, int n) { | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) | |||
| cout << mat[i][j] << "\t"; | |||
| cout << endl; | |||
| } | |||
| } | |||
| int main(int argc, char**argv) { | |||
| int mom_pid=getpid(); | |||
| int status; int pid; | |||
| int n, num_processes; | |||
| n=atoi(argv[1]); | |||
| num_processes=n; | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3=boost::chrono::system_clock::now(); | |||
| /*scheduler*/ | |||
| int count=0; | |||
| while (true) { | |||
| pid = fork(); | |||
| if (pid < 0) { | |||
| cout << "Fork failed!" << endl; | |||
| break; | |||
| } else { | |||
| count++; | |||
| if (pid == 0 && mom_pid==getppid() ) { | |||
| if (DEBUG) | |||
| cout <<"["<<count<<"]"<< " PID:" << getpid() << " Mom:" << getppid() << endl; | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| } | |||
| if (count>=num_processes) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| addV(count,a,b,c,n,count); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| // Parent waits for every child process to be finished | |||
| for (int i = 0; i < num_processes; ++i) { | |||
| wait(&status); | |||
| if (WIFEXITED(status)) | |||
| if(DEBUG) | |||
| std::cout << "Child Process in:" << "<= Parent:"<< (int) getpid() << " exited with status" << WEXITSTATUS(status) << std::endl; | |||
| usleep(10); | |||
| } | |||
| t4=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res2 = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4-t3); | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| cout <<" n,setup(us),exec(us)" << n<<","<<res.count() <<","<<res2.count()<<endl; | |||
| } | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| int addV(int p, double **a, double**b, double **c, int n, int offset) { | |||
| for (int j=0;j<n;j++) | |||
| c[offset][j]=a[offset][j]+b[offset][j]; | |||
| return getpid()%getppid(); | |||
| } | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| cout <<c[i][j] <<"\t"; | |||
| } | |||
| cout << endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| int n; | |||
| n=atoi(argv[1]); | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3=boost::chrono::system_clock::now(); | |||
| for (int i=0;i<n;i++) | |||
| addV(i,a,b,c,n,i); | |||
| t4=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res2= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t4-t3); | |||
| //print_c(c,n); | |||
| cout <<" n,setup(us),exec(us)" << n<<","<<res.count() <<","<<res2.count()<<endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,56 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,148 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 100; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,148 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 4; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,136 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for a single row | |||
| int mulV_single_row(int row, double** a, double** b, double** c, int n) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[row][k] * b[k][j]; | |||
| } | |||
| c[row][j] = sum; | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| vector<pid_t> children; | |||
| // Create one process per row | |||
| for (int i = 0; i < n; ++i) { | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned row | |||
| mulV_single_row(i, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,56 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,56 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,187 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 100; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| int mom_pid=getpid(); | |||
| /*scheduler*/ | |||
| int count =0; | |||
| int status; | |||
| int pid; | |||
| int end_row; | |||
| int rows_to_process; | |||
| while(true) { | |||
| rows_to_process = rows_per_process + (count < remaining_rows ? 1 : 0); | |||
| end_row = current_row + rows_to_process; | |||
| pid=fork(); | |||
| if (pid<0) { | |||
| cout << "Error forking!" << endl; | |||
| exit(1); | |||
| } else { | |||
| count++; | |||
| if (pid==0 && mom_pid==getppid()) { | |||
| //cout << count << ":" << current_row << "-" << end_row << endl; | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| //main parent | |||
| current_row = end_row; | |||
| } | |||
| if (count >=MAX_PROCESSES) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| //DO | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| for (int i=0;i<MAX_PROCESSES;++i) { | |||
| wait(&status); | |||
| //usleep(1); | |||
| } | |||
| } | |||
| /* vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| }*/ | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,187 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 4; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| int mom_pid=getpid(); | |||
| /*scheduler*/ | |||
| int count =0; | |||
| int status; | |||
| int pid; | |||
| int end_row; | |||
| int rows_to_process; | |||
| while(true) { | |||
| rows_to_process = rows_per_process + (count < remaining_rows ? 1 : 0); | |||
| end_row = current_row + rows_to_process; | |||
| pid=fork(); | |||
| if (pid<0) { | |||
| cout << "Error forking!" << endl; | |||
| exit(1); | |||
| } else { | |||
| count++; | |||
| if (pid==0 && mom_pid==getppid()) { | |||
| //cout << count << ":" << current_row << "-" << end_row << endl; | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| //main parent | |||
| current_row = end_row; | |||
| } | |||
| if (count >=MAX_PROCESSES) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| //DO | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| for (int i=0;i<MAX_PROCESSES;++i) { | |||
| wait(&status); | |||
| //usleep(1); | |||
| } | |||
| } | |||
| /* vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| }*/ | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,170 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for a single row | |||
| int mulV_single_row(int row, double** a, double** b, double** c, int n) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[row][k] * b[k][j]; | |||
| } | |||
| c[row][j] = sum; | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| int num_processes=n; | |||
| int mom_pid=getpid(); | |||
| /*scheduler*/ | |||
| int count =0; | |||
| int status; | |||
| int pid; | |||
| while(true) { | |||
| pid=fork(); | |||
| if (pid<0) { | |||
| cout << "Error forking!" << endl; | |||
| exit(1); | |||
| } else { | |||
| count++; | |||
| if (pid==0 && mom_pid==getppid()) { | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| } | |||
| if (count >=num_processes) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| //DO | |||
| mulV_single_row(count, a, b, c, n); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| for (int i=0;i<num_processes;++i) { | |||
| wait(&status); | |||
| //usleep(1); | |||
| } | |||
| } | |||
| /* vector<pid_t> children; | |||
| // Create one process per row | |||
| for (int i = 0; i < n; ++i) { | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned row | |||
| mulV_single_row(i, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| }*/ | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,120 @@ | |||
| #include <mpi.h> | |||
| #include <iostream> | |||
| #include <random> | |||
| #include <chrono> | |||
| #include <boost/chrono.hpp> | |||
| #include <omp.h> | |||
| #define num_threads 4 | |||
| using namespace std; | |||
| using namespace boost; | |||
| void generate_matrix(double* matrix, int n, std::mt19937& gen, std::uniform_real_distribution<double>& dist) { | |||
| #pragma omp parallel for | |||
| for (int i = 0; i < n * n; ++i) { | |||
| matrix[i] = dist(gen); | |||
| } | |||
| } | |||
| void multiply_rows(const double* a, const double* b, double* c, int n, int row_start, int row_end) { | |||
| int num_rows = row_end - row_start; | |||
| // Collapsed all three loops - best for very large matrices | |||
| #pragma omp parallel for schedule(dynamic, 100) collapse(2) | |||
| for (int i = 0; i < num_rows; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| for (int k = 0; k < n; ++k) { | |||
| c[i*n + j] += a[i*n + k] * b[k*n + j]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| int main(int argc, char* argv[]) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| // Pre-MPI array generation | |||
| double* a_full = new double[n*n](); // Initialize to zero | |||
| double* b_full = new double[n*n](); | |||
| double* c_full = new double[n*n](); | |||
| std::mt19937 gen(static_cast<unsigned>(time(nullptr))); | |||
| std::uniform_real_distribution<double> dist(0.0, 1.0); | |||
| generate_matrix(a_full, n, gen, dist); | |||
| generate_matrix(b_full, n, gen, dist); | |||
| // Start timing | |||
| boost::chrono::high_resolution_clock::time_point start = boost::chrono::high_resolution_clock::now(); | |||
| MPI_Init(&argc, &argv); | |||
| int rank, size; | |||
| MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |||
| MPI_Comm_size(MPI_COMM_WORLD, &size); | |||
| // Set number of OpenMP threads | |||
| omp_set_num_threads(num_threads); | |||
| int rows_per_proc = n / size; | |||
| int extra = n % size; | |||
| int my_rows = rows_per_proc + (rank < extra ? 1 : 0); | |||
| int row_start = rank * rows_per_proc + min(rank, extra); | |||
| double* a_sub = new double[my_rows * n](); | |||
| double* c_sub = new double[my_rows * n](); | |||
| double* b = new double[n * n](); | |||
| // Distribute B | |||
| MPI_Bcast(b_full, n * n, MPI_DOUBLE, 0, MPI_COMM_WORLD); | |||
| std::copy(b_full, b_full + n * n, b); | |||
| // Distribute rows of A | |||
| if (rank == 0) { | |||
| for (int i = 0; i < size; ++i) { | |||
| int r_start = i * rows_per_proc + min(i, extra); | |||
| int r_count = rows_per_proc + (i < extra ? 1 : 0); | |||
| if (i == 0) { | |||
| std::copy(a_full, a_full + r_count * n, a_sub); | |||
| } else { | |||
| MPI_Send(a_full + r_start * n, r_count * n, MPI_DOUBLE, i, 0, MPI_COMM_WORLD); | |||
| } | |||
| } | |||
| } else { | |||
| MPI_Recv(a_sub, my_rows * n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); | |||
| } | |||
| MPI_Barrier(MPI_COMM_WORLD); | |||
| multiply_rows(a_sub, b, c_sub, n, row_start, row_start + my_rows); | |||
| MPI_Barrier(MPI_COMM_WORLD); | |||
| // Gather results | |||
| if (rank == 0) { | |||
| std::copy(c_sub, c_sub + my_rows * n, c_full + row_start * n); | |||
| for (int i = 1; i < size; ++i) { | |||
| int r_start = i * rows_per_proc + min(i, extra); | |||
| int r_count = rows_per_proc + (i < extra ? 1 : 0); | |||
| ::MPI_Recv(c_full + r_start * n, r_count * n, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); | |||
| } | |||
| boost::chrono::high_resolution_clock::time_point end = boost::chrono::high_resolution_clock::now(); | |||
| auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(end - start); | |||
| cout << "Matrix multiplication completed in " << duration.count() << " ms" << endl; | |||
| } else { | |||
| MPI_Send(c_sub, my_rows * n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); | |||
| } | |||
| // Clean up | |||
| delete[] a_sub; | |||
| delete[] c_sub; | |||
| delete[] b; | |||
| if (rank == 0) { | |||
| delete[] a_full; | |||
| delete[] b_full; | |||
| delete[] c_full; | |||
| } | |||
| MPI_Finalize(); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| #include <iostream> | |||
| #include <boost/chrono.hpp> | |||
| #include <random> | |||
| #include <omp.h> | |||
| #include <cstdlib> | |||
| using namespace std; | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void delete_matrix(double **a, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] a[i]; | |||
| } | |||
| delete[] a; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n=atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| auto setup_start = boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| auto setup_end = boost::chrono::system_clock::now(); | |||
| auto start = boost::chrono::system_clock::now(); | |||
| #pragma omp parallel for schedule(static) collapse(2) | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| for (int k=0;k<n;k++) { | |||
| c[i][j]+=a[i][k]*b[k][j]; | |||
| } | |||
| } | |||
| } | |||
| auto stop = boost::chrono::system_clock::now(); | |||
| auto setup = boost::chrono::duration_cast<boost::chrono::milliseconds>(setup_end - setup_start); | |||
| auto comp = boost::chrono::duration_cast<boost::chrono::milliseconds>(stop - start); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup.count() << "," << comp.count() << endl; | |||
| delete_matrix(a, n); | |||
| delete_matrix(b, n); | |||
| delete_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,76 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| int mulV(int p, double **a, double **b, double **c, int n, int offset) { | |||
| for (int j = 0; j < n; j++) { | |||
| c[offset][j] = 0; | |||
| for (int k = 0; k < n; k++) { | |||
| c[offset][j] += a[offset][k] * b[k][j]; | |||
| } | |||
| } | |||
| return getpid()%getppid(); | |||
| } | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| cout <<c[i][j] <<"\t"; | |||
| } | |||
| cout << endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| int n; | |||
| n=atoi(argv[1]); | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| //print_c(c,n); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| for(int i = 0; i < n; i++) | |||
| mulV(i, a, b, c, n, i); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res2 = | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout <<" n,setup(us),exec*(us)" << n<<","<<res.count() <<","<<res2.count()<<endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,98 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| #include <boost/thread.hpp> | |||
| #include <vector> | |||
| const int NUM_OF_THREADS = 100; | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| std::cout <<c[i][j] <<"\t"; | |||
| } | |||
| std::cout << std::endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| double **multiply(double **a,double **b,double **c, int n, int start,int end) { | |||
| for (int i=start;i<end;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| double sum = 0.0; | |||
| for (int k=0;k<n;k++) { | |||
| sum += a[i][k]*b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return c; | |||
| } | |||
| int main(int agrc, char ** argv) { | |||
| int n; | |||
| n=atoi(argv[1]); | |||
| boost::thread_group tg; | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res=boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| int rows_per_thread = n / NUM_OF_THREADS; | |||
| int remaining_rows = n % NUM_OF_THREADS; | |||
| int current_row = 0; | |||
| int threads_left = std::min(NUM_OF_THREADS, n); | |||
| for(int i = 0; i < threads_left && current_row < n; i++){ | |||
| int rows_to_process = rows_per_thread + (i< remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| try { | |||
| tg.create_thread(boost::bind(multiply,a,b,c,n,current_row,end_row)); | |||
| } catch (const exception &e) { | |||
| cerr << "create_thread failed, thread:" << i << "," << e.what() << endl; | |||
| } | |||
| current_row = end_row; | |||
| } | |||
| tg.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res3 =boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| std::cout <<" n,setup(ms),exec(ms)" << n<<","<<res.count() <<","<<res3.count()<< std::endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,111 @@ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/thread.hpp> | |||
| #include <boost/bind/bind.hpp> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| using namespace boost::placeholders; | |||
| // Allocates a matrix with double** | |||
| double** create_matrix(int n) { | |||
| //double* data = new double[n * n]; | |||
| double** matrix = new double*[n]; | |||
| for (int i = 0; i < n; ++i) | |||
| matrix[i]=new double[n]; | |||
| // matrix[i] = &data[i * n]; | |||
| return matrix; | |||
| } | |||
| // Frees matrix memory | |||
| void cleanup_matrix(double** matrix) { | |||
| if (matrix) { | |||
| //delete[] matrix[0]; | |||
| delete[] matrix; | |||
| } | |||
| } | |||
| // Initializes matrix with random values or zeros | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) | |||
| for (int j = 0; j < n; ++j) | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| // Matrix multiplication: C = A × B for rows [start_row, end_row) | |||
| void mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) | |||
| sum += a[i][k] * b[k][j]; | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| const int MAX_THREADS = n; | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| double** a = create_matrix(n); | |||
| double** b = create_matrix(n); | |||
| double** c = create_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Use boost::thread_group with boost::bind | |||
| boost::thread_group group; | |||
| int rows_per_thread = n / MAX_THREADS; | |||
| int remaining_rows = n % MAX_THREADS; | |||
| int current_row = 0; | |||
| for (int i = 0; i < MAX_THREADS && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_thread + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| group.create_thread( | |||
| boost::bind(mulV, current_row, end_row, a, b, c, n) | |||
| ); | |||
| current_row = end_row; | |||
| } | |||
| group.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(a); | |||
| cleanup_matrix(b); | |||
| cleanup_matrix(c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,57 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| -lcudart | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,174 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| #define DEBUG 0 | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 100; | |||
| struct SharedMatrix { | |||
| vector<double*> chunks; | |||
| double** row_pointers; | |||
| int size; | |||
| }; | |||
| void create_shared_matrix(SharedMatrix** matrix, int n) { | |||
| const size_t CHUNK_SIZE = 1024 * 1024; // 1MB chunks | |||
| size_t total_size = n * n * sizeof(double); | |||
| size_t num_chunks = (total_size + CHUNK_SIZE - 1) / CHUNK_SIZE; | |||
| *matrix = new SharedMatrix; | |||
| (*matrix)->size = n; | |||
| (*matrix)->chunks.reserve(num_chunks); | |||
| // Allocate memory chunks | |||
| for (size_t i = 0; i < num_chunks; ++i) { | |||
| size_t chunk_size = min(CHUNK_SIZE, total_size - i * CHUNK_SIZE); | |||
| void* ptr = mmap(NULL, chunk_size, | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, | |||
| -1, 0); | |||
| if (ptr == MAP_FAILED) { | |||
| perror("mmap failed"); | |||
| // Clean up already allocated chunks | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, CHUNK_SIZE); | |||
| } | |||
| delete *matrix; | |||
| exit(1); | |||
| } | |||
| (*matrix)->chunks.push_back(static_cast<double*>(ptr)); | |||
| } | |||
| // Create row pointers | |||
| (*matrix)->row_pointers = new double*[n]; | |||
| size_t current_chunk = 0; | |||
| size_t offset_in_chunk = 0; | |||
| for (int i = 0; i < n; ++i) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->chunks[current_chunk]) + offset_in_chunk); | |||
| offset_in_chunk += n * sizeof(double); | |||
| // Move to next chunk if needed | |||
| while (offset_in_chunk >= CHUNK_SIZE) { | |||
| offset_in_chunk -= CHUNK_SIZE; | |||
| current_chunk++; | |||
| // Handle case where row spans chunks | |||
| if (i < n - 1) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->row_pointers[i]) - CHUNK_SIZE + offset_in_chunk); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void cleanup_matrix(SharedMatrix** matrix) { | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, (*matrix)->chunks.size() > 1 ? 1024 * 1024 : | |||
| (*matrix)->size * (*matrix)->size * sizeof(double)); | |||
| } | |||
| delete[] (*matrix)->row_pointers; | |||
| delete *matrix; | |||
| *matrix = nullptr; | |||
| } | |||
| void init_matrix(SharedMatrix** matrix, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < (*matrix)->size; ++i) { | |||
| for (int j = 0; j < (*matrix)->size; ++j) { | |||
| (*matrix)->row_pointers[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| int addV(int start_row, int end_row, SharedMatrix** a, SharedMatrix** b, SharedMatrix** c) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < (*a)->size; ++j) { | |||
| (*c)->row_pointers[i][j] = (*a)->row_pointers[i][j] + (*b)->row_pointers[i][j]; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| SharedMatrix *a = nullptr, *b = nullptr, *c = nullptr; | |||
| create_shared_matrix(&a, n); | |||
| create_shared_matrix(&b, n); | |||
| create_shared_matrix(&c, n); | |||
| init_matrix(&a); | |||
| init_matrix(&b); | |||
| init_matrix(&c, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Calculate work distribution | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process | |||
| addV(current_row, end_row, &a, &b, &c); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all children | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(&a); | |||
| cleanup_matrix(&b); | |||
| cleanup_matrix(&c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,174 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| #define DEBUG 0 | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 24; | |||
| struct SharedMatrix { | |||
| vector<double*> chunks; | |||
| double** row_pointers; | |||
| int size; | |||
| }; | |||
| void create_shared_matrix(SharedMatrix** matrix, int n) { | |||
| const size_t CHUNK_SIZE = 1024 * 1024; // 1MB chunks | |||
| size_t total_size = n * n * sizeof(double); | |||
| size_t num_chunks = (total_size + CHUNK_SIZE - 1) / CHUNK_SIZE; | |||
| *matrix = new SharedMatrix; | |||
| (*matrix)->size = n; | |||
| (*matrix)->chunks.reserve(num_chunks); | |||
| // Allocate memory chunks | |||
| for (size_t i = 0; i < num_chunks; ++i) { | |||
| size_t chunk_size = min(CHUNK_SIZE, total_size - i * CHUNK_SIZE); | |||
| void* ptr = mmap(NULL, chunk_size, | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, | |||
| -1, 0); | |||
| if (ptr == MAP_FAILED) { | |||
| perror("mmap failed"); | |||
| // Clean up already allocated chunks | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, CHUNK_SIZE); | |||
| } | |||
| delete *matrix; | |||
| exit(1); | |||
| } | |||
| (*matrix)->chunks.push_back(static_cast<double*>(ptr)); | |||
| } | |||
| // Create row pointers | |||
| (*matrix)->row_pointers = new double*[n]; | |||
| size_t current_chunk = 0; | |||
| size_t offset_in_chunk = 0; | |||
| for (int i = 0; i < n; ++i) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->chunks[current_chunk]) + offset_in_chunk); | |||
| offset_in_chunk += n * sizeof(double); | |||
| // Move to next chunk if needed | |||
| while (offset_in_chunk >= CHUNK_SIZE) { | |||
| offset_in_chunk -= CHUNK_SIZE; | |||
| current_chunk++; | |||
| // Handle case where row spans chunks | |||
| if (i < n - 1) { | |||
| (*matrix)->row_pointers[i] = reinterpret_cast<double*>( | |||
| reinterpret_cast<char*>((*matrix)->row_pointers[i]) - CHUNK_SIZE + offset_in_chunk); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void cleanup_matrix(SharedMatrix** matrix) { | |||
| for (auto chunk : (*matrix)->chunks) { | |||
| munmap(chunk, (*matrix)->chunks.size() > 1 ? 1024 * 1024 : | |||
| (*matrix)->size * (*matrix)->size * sizeof(double)); | |||
| } | |||
| delete[] (*matrix)->row_pointers; | |||
| delete *matrix; | |||
| *matrix = nullptr; | |||
| } | |||
| void init_matrix(SharedMatrix** matrix, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < (*matrix)->size; ++i) { | |||
| for (int j = 0; j < (*matrix)->size; ++j) { | |||
| (*matrix)->row_pointers[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| int addV(int start_row, int end_row, SharedMatrix** a, SharedMatrix** b, SharedMatrix** c) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < (*a)->size; ++j) { | |||
| (*c)->row_pointers[i][j] = (*a)->row_pointers[i][j] + (*b)->row_pointers[i][j]; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| SharedMatrix *a = nullptr, *b = nullptr, *c = nullptr; | |||
| create_shared_matrix(&a, n); | |||
| create_shared_matrix(&b, n); | |||
| create_shared_matrix(&c, n); | |||
| init_matrix(&a); | |||
| init_matrix(&b); | |||
| init_matrix(&c, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Calculate work distribution | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process | |||
| addV(current_row, end_row, &a, &b, &c); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all children | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(&a); | |||
| cleanup_matrix(&b); | |||
| cleanup_matrix(&c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,130 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| #define DEBUG 0 | |||
| using namespace std; | |||
| // Create a mmap-allocated matrix with pointers | |||
| double** create_shared_matrix(int n) { | |||
| int data_size = n * n * sizeof(double); | |||
| void* hint = (void*)0x10000000; // Example hint address | |||
| double* data = (double*)mmap(hint, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap"); | |||
| exit(1); | |||
| } | |||
| // Create an index matrix locally (does not need to be shared) | |||
| double** mat = new double*[n]; | |||
| for (int i = 0; i < n; ++i) | |||
| mat[i] = &data[i * n]; | |||
| return mat; | |||
| } | |||
| // Detach mmap and delete indexes | |||
| void cleanup_matrix(double** mat, int n) { | |||
| munmap(mat[0], n * n * sizeof(double)); | |||
| delete[] mat; | |||
| } | |||
| void init_matrix(double** mat, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) | |||
| for (int j = 0; j < n; ++j) | |||
| if (zero) | |||
| mat[i][j] = 0 ; | |||
| else | |||
| mat[i][j]=dis(gen); | |||
| } | |||
| //Add vector column | |||
| int addV(int p, double **a, double**b, double **c, int n, int offset) { | |||
| for (int j=0;j<n;j++) | |||
| c[offset-1][j]=a[offset-1][j]+b[offset-1][j]; | |||
| return getpid()%getppid(); | |||
| } | |||
| void print_matrix(double** mat, int n) { | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) | |||
| cout << mat[i][j] << "\t"; | |||
| cout << endl; | |||
| } | |||
| } | |||
| int main(int argc, char**argv) { | |||
| int mom_pid=getpid(); | |||
| int status; int pid; | |||
| int n, num_processes; | |||
| n=atoi(argv[1]); | |||
| num_processes=n; | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3=boost::chrono::system_clock::now(); | |||
| /*scheduler*/ | |||
| int count=0; | |||
| while (true) { | |||
| pid = fork(); | |||
| if (pid < 0) { | |||
| cout << "Fork failed!" << endl; | |||
| break; | |||
| } else { | |||
| count++; | |||
| if (pid == 0 && mom_pid==getppid() ) { | |||
| if (DEBUG) | |||
| cout <<"["<<count<<"]"<< " PID:" << getpid() << " Mom:" << getppid() << endl; | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| } | |||
| if (count>=num_processes) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| addV(count,a,b,c,n,count); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| // Parent waits for every child process to be finished | |||
| for (int i = 0; i < num_processes; ++i) { | |||
| wait(&status); | |||
| if (WIFEXITED(status)) | |||
| if(DEBUG) | |||
| std::cout << "Child Process in:" << "<= Parent:"<< (int) getpid() << " exited with status" << WEXITSTATUS(status) << std::endl; | |||
| usleep(10); | |||
| } | |||
| t4=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res2 = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4-t3); | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| cout <<" n,setup(us),exec(us)" << n<<","<<res.count() <<","<<res2.count()<<endl; | |||
| } | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| int addV(int p, double **a, double**b, double **c, int n, int offset) { | |||
| for (int j=0;j<n;j++) | |||
| c[offset][j]=a[offset][j]+b[offset][j]; | |||
| return getpid()%getppid(); | |||
| } | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| cout <<c[i][j] <<"\t"; | |||
| } | |||
| cout << endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| int n; | |||
| n=atoi(argv[1]); | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3=boost::chrono::system_clock::now(); | |||
| for (int i=0;i<n;i++) | |||
| addV(i,a,b,c,n,i); | |||
| t4=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res2= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t4-t3); | |||
| //print_c(c,n); | |||
| cout <<" n,setup(us),exec(us)" << n<<","<<res.count() <<","<<res2.count()<<endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,102 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| #include <boost/thread.hpp> | |||
| using namespace std; | |||
| const int NUM_OF_THREADS = 100; | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| std::cout <<c[i][j] <<"\t"; | |||
| } | |||
| std::cout << std::endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| double **add(double **a,double **b,double **c, int n, int start,int end) { | |||
| for (int i=start;i<end;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| //mtx.lock(); | |||
| c[i][j] = a[i][j] + b[i][j]; | |||
| //mtx.unlock(); | |||
| } | |||
| } | |||
| return c; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage:" << argv[0] << "<matrix.size>" << endl; | |||
| } | |||
| int n=std::atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::thread_group tg; | |||
| //std::vector<boost::thread> threads; | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res=boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| int rows_per_thread = n / NUM_OF_THREADS; | |||
| int remaining_rows = n % NUM_OF_THREADS; | |||
| int current_row = 0; | |||
| for(int i = 0; i < NUM_OF_THREADS && current_row < n; i++){ | |||
| int rows_to_process = rows_per_thread + (i< remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| try { | |||
| tg.create_thread(boost::bind(add,a,b,c,n,current_row,end_row)); | |||
| } catch (const exception &e) { | |||
| cerr << "create_thread failed, thread:" << i << "," << e.what() << endl; | |||
| } | |||
| current_row = end_row; | |||
| } | |||
| tg.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res3 =boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| std::cout <<" n,setup(us),exec*(us)" << n<<","<<res.count() <<","<<res3.count()<< std::endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,57 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| -lcudart | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,148 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 100; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,148 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 24; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,136 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for a single row | |||
| int mulV_single_row(int row, double** a, double** b, double** c, int n) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[row][k] * b[k][j]; | |||
| } | |||
| c[row][j] = sum; | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| vector<pid_t> children; | |||
| // Create one process per row | |||
| for (int i = 0; i < n; ++i) { | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned row | |||
| mulV_single_row(i, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| } | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,57 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| -lcudart | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,57 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g -fopenmp | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| -lcudart | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,187 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 100; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| int mom_pid=getpid(); | |||
| /*scheduler*/ | |||
| int count =0; | |||
| int status; | |||
| int pid; | |||
| int end_row; | |||
| int rows_to_process; | |||
| while(true) { | |||
| rows_to_process = rows_per_process + (count < remaining_rows ? 1 : 0); | |||
| end_row = current_row + rows_to_process; | |||
| pid=fork(); | |||
| if (pid<0) { | |||
| cout << "Error forking!" << endl; | |||
| exit(1); | |||
| } else { | |||
| count++; | |||
| if (pid==0 && mom_pid==getppid()) { | |||
| //cout << count << ":" << current_row << "-" << end_row << endl; | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| //main parent | |||
| current_row = end_row; | |||
| } | |||
| if (count >=MAX_PROCESSES) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| //DO | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| for (int i=0;i<MAX_PROCESSES;++i) { | |||
| wait(&status); | |||
| //usleep(1); | |||
| } | |||
| } | |||
| /* vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| }*/ | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,187 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| const int MAX_PROCESSES = 24; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for rows [start_row, end_row) | |||
| int mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i][k] * b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| //double **a = nullptr, **b = nullptr, **c = nullptr; | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Distribute work among 100 processes | |||
| int rows_per_process = n / MAX_PROCESSES; | |||
| int remaining_rows = n % MAX_PROCESSES; | |||
| int current_row = 0; | |||
| int mom_pid=getpid(); | |||
| /*scheduler*/ | |||
| int count =0; | |||
| int status; | |||
| int pid; | |||
| int end_row; | |||
| int rows_to_process; | |||
| while(true) { | |||
| rows_to_process = rows_per_process + (count < remaining_rows ? 1 : 0); | |||
| end_row = current_row + rows_to_process; | |||
| pid=fork(); | |||
| if (pid<0) { | |||
| cout << "Error forking!" << endl; | |||
| exit(1); | |||
| } else { | |||
| count++; | |||
| if (pid==0 && mom_pid==getppid()) { | |||
| //cout << count << ":" << current_row << "-" << end_row << endl; | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| //main parent | |||
| current_row = end_row; | |||
| } | |||
| if (count >=MAX_PROCESSES) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| //DO | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| for (int i=0;i<MAX_PROCESSES;++i) { | |||
| wait(&status); | |||
| //usleep(1); | |||
| } | |||
| } | |||
| /* vector<pid_t> children; | |||
| for (int i = 0; i < MAX_PROCESSES && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_process + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned rows | |||
| mulV(current_row, end_row, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| current_row = end_row; | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| }*/ | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,170 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| // Allocates a shared matrix with double** row pointers | |||
| double** create_shared_matrix(int n) { | |||
| size_t data_size = n * n * sizeof(double); | |||
| double* data = static_cast<double*>( | |||
| mmap(NULL, data_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (data == MAP_FAILED) { | |||
| perror("mmap failed for matrix data"); | |||
| exit(1); | |||
| } | |||
| size_t row_ptrs_size = n * sizeof(double*); | |||
| double** row_ptrs = static_cast<double**>( | |||
| mmap(NULL, row_ptrs_size, PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, -1, 0)); | |||
| if (row_ptrs == MAP_FAILED) { | |||
| perror("mmap failed for row pointers"); | |||
| munmap(data, data_size); | |||
| exit(1); | |||
| } | |||
| for (int i = 0; i < n; ++i) { | |||
| row_ptrs[i] = &data[i * n]; | |||
| } | |||
| return row_ptrs; | |||
| } | |||
| // Frees the shared matrix | |||
| void cleanup_matrix(double** matrix, int n) { | |||
| if (matrix == nullptr) return; | |||
| double* data = matrix[0]; | |||
| munmap(matrix, n * sizeof(double*)); | |||
| munmap(data, n * n * sizeof(double)); | |||
| } | |||
| // Initializes matrix with random values (or zeros) | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Computes C = A × B for a single row | |||
| int mulV_single_row(int row, double** a, double** b, double** c, int n) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[row][k] * b[k][j]; | |||
| } | |||
| c[row][j] = sum; | |||
| } | |||
| return 0; | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| // Allocate matrices in shared memory | |||
| double** a = create_shared_matrix(n); | |||
| double** b = create_shared_matrix(n); | |||
| double** c = create_shared_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); // Initialize c with zeros | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| int num_processes=n; | |||
| int mom_pid=getpid(); | |||
| /*scheduler*/ | |||
| int count =0; | |||
| int status; | |||
| int pid; | |||
| while(true) { | |||
| pid=fork(); | |||
| if (pid<0) { | |||
| cout << "Error forking!" << endl; | |||
| exit(1); | |||
| } else { | |||
| count++; | |||
| if (pid==0 && mom_pid==getppid()) { | |||
| break; | |||
| } else if (mom_pid!=getpid()) { | |||
| return -1; | |||
| } | |||
| } | |||
| if (count >=num_processes) | |||
| break; | |||
| } | |||
| /*Task assign*/ | |||
| if (pid==0) { | |||
| //DO | |||
| mulV_single_row(count, a, b, c, n); | |||
| return getpid()%getppid(); | |||
| } else { | |||
| for (int i=0;i<num_processes;++i) { | |||
| wait(&status); | |||
| //usleep(1); | |||
| } | |||
| } | |||
| /* vector<pid_t> children; | |||
| // Create one process per row | |||
| for (int i = 0; i < n; ++i) { | |||
| pid_t pid = fork(); | |||
| if (pid < 0) { | |||
| cerr << "Fork failed!" << endl; | |||
| break; | |||
| } else if (pid == 0) { | |||
| // Child process computes its assigned row | |||
| mulV_single_row(i, a, b, c, n); | |||
| exit(0); | |||
| } else { | |||
| children.push_back(pid); | |||
| } | |||
| } | |||
| // Wait for all child processes to finish | |||
| for (pid_t pid : children) { | |||
| waitpid(pid, nullptr, 0); | |||
| }*/ | |||
| t4 = boost::chrono::system_clock::now(); | |||
| // Print timing results | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| // Cleanup | |||
| cleanup_matrix(a, n); | |||
| cleanup_matrix(b, n); | |||
| cleanup_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| #include <iostream> | |||
| #include <boost/chrono.hpp> | |||
| #include <random> | |||
| #include <omp.h> | |||
| #include <cstdlib> | |||
| using namespace std; | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void delete_matrix(double **a, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] a[i]; | |||
| } | |||
| delete[] a; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n=atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| auto setup_start = boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| auto setup_end = boost::chrono::system_clock::now(); | |||
| auto start = boost::chrono::system_clock::now(); | |||
| #pragma omp parallel for schedule(static) collapse(2) | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| for (int k=0;k<n;k++) { | |||
| c[i][j]+=a[i][k]*b[k][j]; | |||
| } | |||
| } | |||
| } | |||
| auto stop = boost::chrono::system_clock::now(); | |||
| auto setup = boost::chrono::duration_cast<boost::chrono::milliseconds>(setup_end - setup_start); | |||
| auto comp = boost::chrono::duration_cast<boost::chrono::milliseconds>(stop - start); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup.count() << "," << comp.count() << endl; | |||
| delete_matrix(a, n); | |||
| delete_matrix(b, n); | |||
| delete_matrix(c, n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,76 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| int mulV(int p, double **a, double **b, double **c, int n, int offset) { | |||
| for (int j = 0; j < n; j++) { | |||
| c[offset][j] = 0; | |||
| for (int k = 0; k < n; k++) { | |||
| c[offset][j] += a[offset][k] * b[k][j]; | |||
| } | |||
| } | |||
| return getpid()%getppid(); | |||
| } | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| cout <<c[i][j] <<"\t"; | |||
| } | |||
| cout << endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| int main(int argc, char ** argv) { | |||
| int n; | |||
| n=atoi(argv[1]); | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res= | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| //print_c(c,n); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| for(int i = 0; i < n; i++) | |||
| mulV(i, a, b, c, n, i); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res2 = | |||
| boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout <<" n,setup(us),exec*(us)" << n<<","<<res.count() <<","<<res2.count()<<endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,98 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/chrono.hpp> | |||
| #include <boost/thread.hpp> | |||
| #include <vector> | |||
| const int NUM_OF_THREADS = 100; | |||
| double **init_a(double **a, int n, bool zero=false) { | |||
| a=new double*[n]; | |||
| for (int i=0;i<n;i++) | |||
| a[i]=new double[n]; | |||
| std::random_device rd; | |||
| std::mt19937 gen(rd()); | |||
| std::uniform_real_distribution<double> dis(0.0,1.0); | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| if (zero) | |||
| a[i][j]=0; | |||
| else | |||
| a[i][j]=dis(gen); | |||
| } | |||
| } | |||
| return a; | |||
| } | |||
| void print_c(double **c,int n) { | |||
| for (int i=0;i<n;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| std::cout <<c[i][j] <<"\t"; | |||
| } | |||
| std::cout << std::endl; | |||
| } | |||
| } | |||
| void delete_matrix(double **matrix, int n) { | |||
| for (int i = 0; i < n; i++) { | |||
| delete[] matrix[i]; | |||
| } | |||
| delete[] matrix; | |||
| } | |||
| double **multiply(double **a,double **b,double **c, int n, int start,int end) { | |||
| for (int i=start;i<end;i++) { | |||
| for (int j=0;j<n;j++) { | |||
| double sum = 0.0; | |||
| for (int k=0;k<n;k++) { | |||
| sum += a[i][k]*b[k][j]; | |||
| } | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| return c; | |||
| } | |||
| int main(int agrc, char ** argv) { | |||
| int n; | |||
| n=atoi(argv[1]); | |||
| boost::thread_group tg; | |||
| boost::chrono::system_clock::time_point t1,t2,t3,t4; | |||
| t1=boost::chrono::system_clock::now(); | |||
| double **a=init_a(a,n); | |||
| double **b=init_a(b,n); | |||
| double **c=init_a(c,n,true); | |||
| t2=boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res=boost::chrono::duration_cast<boost::chrono::milliseconds>(t2-t1); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| int rows_per_thread = n / NUM_OF_THREADS; | |||
| int remaining_rows = n % NUM_OF_THREADS; | |||
| int current_row = 0; | |||
| int threads_left = std::min(NUM_OF_THREADS, n); | |||
| for(int i = 0; i < threads_left && current_row < n; i++){ | |||
| int rows_to_process = rows_per_thread + (i< remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| try { | |||
| tg.create_thread(boost::bind(multiply,a,b,c,n,current_row,end_row)); | |||
| } catch (const exception &e) { | |||
| cerr << "create_thread failed, thread:" << i << "," << e.what() << endl; | |||
| } | |||
| current_row = end_row; | |||
| } | |||
| tg.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| boost::chrono::milliseconds res3 =boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| std::cout <<" n,setup(ms),exec(ms)" << n<<","<<res.count() <<","<<res3.count()<< std::endl; | |||
| delete_matrix(a,n); | |||
| delete_matrix(b,n); | |||
| delete_matrix(c,n); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,111 @@ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <boost/thread.hpp> | |||
| #include <boost/bind/bind.hpp> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| using namespace boost::placeholders; | |||
| // Allocates a matrix with double** | |||
| double** create_matrix(int n) { | |||
| //double* data = new double[n * n]; | |||
| double** matrix = new double*[n]; | |||
| for (int i = 0; i < n; ++i) | |||
| matrix[i]=new double[n]; | |||
| // matrix[i] = &data[i * n]; | |||
| return matrix; | |||
| } | |||
| // Frees matrix memory | |||
| void cleanup_matrix(double** matrix) { | |||
| if (matrix) { | |||
| //delete[] matrix[0]; | |||
| delete[] matrix; | |||
| } | |||
| } | |||
| // Initializes matrix with random values or zeros | |||
| void init_matrix(double** matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n; ++i) | |||
| for (int j = 0; j < n; ++j) | |||
| matrix[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| // Matrix multiplication: C = A × B for rows [start_row, end_row) | |||
| void mulV(int start_row, int end_row, double** a, double** b, double** c, int n) { | |||
| for (int i = start_row; i < end_row; ++i) | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) | |||
| sum += a[i][k] * b[k][j]; | |||
| c[i][j] = sum; | |||
| } | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| const int MAX_THREADS = n; | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| double** a = create_matrix(n); | |||
| double** b = create_matrix(n); | |||
| double** c = create_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| // Use boost::thread_group with boost::bind | |||
| boost::thread_group group; | |||
| int rows_per_thread = n / MAX_THREADS; | |||
| int remaining_rows = n % MAX_THREADS; | |||
| int current_row = 0; | |||
| for (int i = 0; i < MAX_THREADS && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_thread + (i < remaining_rows ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| group.create_thread( | |||
| boost::bind(mulV, current_row, end_row, a, b, c, n) | |||
| ); | |||
| current_row = end_row; | |||
| } | |||
| group.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(a); | |||
| cleanup_matrix(b); | |||
| cleanup_matrix(c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,57 @@ | |||
| # Compiler and flags | |||
| NVCC := nvcc | |||
| CXX := g++ | |||
| NVCCFLAGS := -std=c++11 -O3 --expt-relaxed-constexpr | |||
| CXXFLAGS := -std=c++1z -g | |||
| INCLUDES := -I/usr/local/include/eigen3 \ | |||
| -I/usr/local/include/third_party \ | |||
| -I/usr/local/include/tensorflow \ | |||
| -I/usr/local/cuda/include | |||
| LDFLAGS := -L/usr/local/lib \ | |||
| -L/usr/local/cuda/lib64 | |||
| LIBS := -ltensorflow_cc \ | |||
| -ltensorflow_framework \ | |||
| -lpthread \ | |||
| -ldl \ | |||
| -lrt \ | |||
| -lboost_system \ | |||
| -lboost_chrono \ | |||
| -lboost_thread \ | |||
| -lcudart | |||
| # Source files | |||
| CUDA_SRCS := $(wildcard *.cu) | |||
| SRC:=$(wildcard *.cpp) | |||
| # Object files | |||
| CUDA_OBJS := $(CUDA_SRCS:.cu=.o) | |||
| CPP_OBJS := $(SRC:.cpp=.o) | |||
| #EXECUTABLES:=$(SRC:.cpp=) | |||
| # Executables | |||
| CUDA_EXECS := $(CUDA_SRCS:.cu=) | |||
| EXECUTABLES := $(SRC:.cpp=) | |||
| all: $(CUDA_EXECS) $(EXECUTABLES) | |||
| # Rule for CUDA files | |||
| %.o: %.cu | |||
| $(NVCC) $(NVCCFLAGS) $(INCLUDES) -c $< -o $@ | |||
| %: %.o | |||
| $(NVCC) $(NVCCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| # Rule for C++ files | |||
| #%.o: %.cpp | |||
| # $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ | |||
| #%: %.o | |||
| # $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| %:%.cpp | |||
| $(CXX) $(CXXFLAGS) $(INCLUDES) $< -o $@ $(LDFLAGS) $(LIBS) | |||
| clean: | |||
| rm -f $(EXECUTABLES) $(CUDA_OBJS) $(CPP_OBJS) $(CUDA_EXECS) | |||
| .PHONY: all clean | |||
| @ -0,0 +1,151 @@ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstring> | |||
| #include <cuda_runtime.h> | |||
| #include <boost/chrono.hpp> | |||
| void CHECK_CUDA(cudaError_t call) { | |||
| cudaError_t err = call; | |||
| if (err != cudaSuccess) { | |||
| std::cerr << "CUDA error at " << __FILE__ << ":" << __LINE__ << ": " << cudaGetErrorString(err) << std::endl; | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| } | |||
| float* flatten(float** matrix, int N) { | |||
| float* flat = new float[N*N]; | |||
| for (int i = 0; i < N; ++i) | |||
| memcpy(flat + i*N, matrix[i], N*sizeof(float)); | |||
| return flat; | |||
| } | |||
| float** initMatrix(int N) { | |||
| float** matrix = new float*[N]; | |||
| for (int i = 0; i < N; ++i) { | |||
| matrix[i] = new float[N]; | |||
| for (int j = 0; j < N; ++j) | |||
| matrix[i][j] = static_cast<float>(rand()) / RAND_MAX; | |||
| } | |||
| return matrix; | |||
| } | |||
| __global__ void matrixAddKernel(float* A, float* B, float* C, int N,int start_row, int end_row) { | |||
| int row = blockIdx.y * blockDim.y + threadIdx.y + start_row; | |||
| int col = blockIdx.x * blockDim.x + threadIdx.x; | |||
| if (row < end_row && col < N) { | |||
| C[(row-start_row) * N + col] = A[(row-start_row) * N + col] + B[(row-start_row) * N + col]; | |||
| } | |||
| } | |||
| void printMatrix(float* matrix, int rows, int cols) { | |||
| for (int i = 0; i < rows; ++i) { | |||
| for (int j = 0; j < cols; ++j) { | |||
| std::cout << matrix[i * cols + j] << "\t"; | |||
| } | |||
| std::cout << std::endl; | |||
| } | |||
| } | |||
| int main() { | |||
| int n, k; | |||
| std::cout << "Enter matrix size (N): "; | |||
| std::cin >> n; | |||
| std::cout << "Enter number of GPUs (K): "; | |||
| std::cin >> k; | |||
| boost::chrono::system_clock::time_point setup_start = boost::chrono::system_clock::now(); | |||
| float** A_2d = initMatrix(n); | |||
| float** B_2d = initMatrix(n); | |||
| float* A = flatten(A_2d, n); | |||
| float* B = flatten(B_2d, n); | |||
| /* printMatrix(A,n,n); | |||
| std::cout << "------------------------------------------" << std::endl; | |||
| printMatrix(B,n,n); | |||
| std::cout << "------------------------------------------" << std::endl; */ | |||
| float* shm_ptr = (float*)mmap(NULL, n * n * sizeof(float), | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_SHARED | MAP_ANONYMOUS, | |||
| -1, 0); | |||
| if (shm_ptr == MAP_FAILED) { | |||
| perror("mmap failed"); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| memset(shm_ptr, 0, n * n * sizeof(float)); | |||
| boost::chrono::system_clock::time_point setup_end = boost::chrono::system_clock::now(); | |||
| boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); | |||
| for (int gpu_id = 0; gpu_id < k; ++gpu_id) { | |||
| pid_t pid = fork(); | |||
| if (pid == 0) { | |||
| CHECK_CUDA(cudaSetDevice(gpu_id)); | |||
| int rows_per_gpu = n / k; | |||
| int remainder = n % k; | |||
| int start_row = gpu_id * rows_per_gpu; | |||
| int end_row = start_row + rows_per_gpu + (gpu_id == k-1 ? remainder : 0); | |||
| int rows_this_gpu = end_row - start_row; | |||
| float *d_A, *d_B, *d_C; | |||
| CHECK_CUDA(cudaMalloc(&d_A,rows_this_gpu * n * sizeof(float))); | |||
| CHECK_CUDA(cudaMalloc(&d_B, n * n * sizeof(float))); | |||
| CHECK_CUDA(cudaMalloc(&d_C, rows_this_gpu * n * sizeof(float))); | |||
| CHECK_CUDA(cudaMemcpy(d_A, A + start_row * n , rows_this_gpu * n * sizeof(float), cudaMemcpyHostToDevice)); | |||
| CHECK_CUDA(cudaMemcpy(d_B, B + start_row * n , rows_this_gpu * n * sizeof(float), cudaMemcpyHostToDevice)); | |||
| dim3 block(16, 16); | |||
| dim3 grid((n + block.x - 1) / block.x, (rows_this_gpu + block.y - 1) / block.y); | |||
| matrixAddKernel<<<grid, block>>>(d_A, d_B, d_C, n, start_row, end_row); | |||
| CHECK_CUDA(cudaDeviceSynchronize()); | |||
| float* partial_C = new float[rows_this_gpu * n]; | |||
| CHECK_CUDA(cudaMemcpy(partial_C, d_C , rows_this_gpu * n * sizeof(float), cudaMemcpyDeviceToHost)); | |||
| for (int i = 0; i < rows_this_gpu; ++i) | |||
| memcpy(shm_ptr + (start_row + i) * n, partial_C + i * n, n * sizeof(float)); | |||
| CHECK_CUDA(cudaFree(d_A)); | |||
| CHECK_CUDA(cudaFree(d_B)); | |||
| CHECK_CUDA(cudaFree(d_C)); | |||
| delete[] partial_C; | |||
| exit(EXIT_SUCCESS); | |||
| } else if (pid < 0) { | |||
| perror("fork failed"); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| } | |||
| for (int gpu_id = 0; gpu_id < k; ++gpu_id) | |||
| wait(NULL); | |||
| boost::chrono::system_clock::time_point end = boost::chrono::system_clock::now(); | |||
| //printMatrix(shm_ptr,n,n); | |||
| auto setup = boost::chrono::duration_cast<boost::chrono::milliseconds>(setup_end - setup_start).count(); | |||
| auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(end - start).count(); | |||
| std::cout << "Setup Time: " << setup << " ms" << std::endl; | |||
| std::cout << "Comp Time: " << duration << " ms" << std::endl; | |||
| // Clean up | |||
| for (int i = 0; i < n; ++i) { | |||
| delete[] A_2d[i]; | |||
| delete[] B_2d[i]; | |||
| } | |||
| delete[] A_2d; | |||
| delete[] B_2d; | |||
| delete[] A; | |||
| delete[] B; | |||
| munmap(shm_ptr, n * n * sizeof(float)); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,159 @@ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <unistd.h> | |||
| #include <sys/wait.h> | |||
| #include <sys/mman.h> | |||
| #include <cstring> | |||
| #include <cuda_runtime.h> | |||
| #include <boost/chrono.hpp> | |||
| void CHECK_CUDA(cudaError_t call) { | |||
| cudaError_t err = call; | |||
| if (err != cudaSuccess) { | |||
| std::cerr << "CUDA error at " << __FILE__ << ":" << __LINE__ << ": " << cudaGetErrorString(err) << std::endl; | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| } | |||
| float* flatten(float** matrix, int N) { | |||
| float* flat = new float[N*N]; | |||
| for (int i = 0; i < N; ++i) | |||
| memcpy(flat + i*N, matrix[i], N*sizeof(float)); | |||
| return flat; | |||
| } | |||
| float** initMatrix(int N) { | |||
| float** matrix = new float*[N]; | |||
| for (int i = 0; i < N; ++i) { | |||
| matrix[i] = new float[N]; | |||
| for (int j = 0; j < N; ++j) | |||
| matrix[i][j] = static_cast<float>(rand()) / RAND_MAX; | |||
| } | |||
| return matrix; | |||
| } | |||
| __global__ void matrixMulKernel(float* A, float* B, float* C, int N, int start_row, int end_row) { | |||
| int row = blockIdx.y * blockDim.y + threadIdx.y + start_row; | |||
| int col = blockIdx.x * blockDim.x + threadIdx.x; | |||
| if (row < end_row && col < N) { | |||
| float sum = 0.0f; | |||
| for (int k = 0; k < N; ++k) | |||
| sum += A[row*N + k] * B[k*N + col]; | |||
| C[(row-start_row)*N + col] = sum; | |||
| } | |||
| } | |||
| void printMatrix(float* matrix, int rows, int cols) { | |||
| for (int i = 0; i < rows; ++i) { | |||
| for (int j = 0; j < cols; ++j) { | |||
| std::cout << matrix[i * cols + j] << "\t"; | |||
| } | |||
| std::cout << std::endl; | |||
| } | |||
| } | |||
| int main() { | |||
| int n, k; | |||
| std::cout << "Enter matrix size (N): "; | |||
| std::cin >> n; | |||
| std::cout << "Enter number of GPUs (K): "; | |||
| std::cin >> k; | |||
| // Initialize matrices | |||
| float** A_2d = initMatrix(n); | |||
| float** B_2d = initMatrix(n); | |||
| float* A = flatten(A_2d, n); | |||
| float* B = flatten(B_2d, n); | |||
| // Allocate shared memory using anonymous mmap (no shm_open needed) | |||
| float* shm_ptr = (float*)mmap( | |||
| NULL, // Let OS choose address | |||
| n * n * sizeof(float), // Size of mapping | |||
| PROT_READ | PROT_WRITE, // Read/write permissions | |||
| MAP_SHARED | MAP_ANONYMOUS, // Shared & anonymous (no file backing) | |||
| -1, // File descriptor (ignored for MAP_ANONYMOUS) | |||
| 0 // Offset (ignored) | |||
| ); | |||
| if (shm_ptr == MAP_FAILED) { | |||
| perror("mmap failed"); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| memset(shm_ptr, 0, n * n * sizeof(float)); // Initialize to 0 | |||
| boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); | |||
| // Fork children | |||
| for (int gpu_id = 0; gpu_id < k; ++gpu_id) { | |||
| pid_t pid = fork(); | |||
| if (pid == 0) { // Child process | |||
| CHECK_CUDA(cudaSetDevice(gpu_id)); | |||
| int rows_per_gpu = n / k; | |||
| int remainder = n % k; | |||
| int start_row = gpu_id * rows_per_gpu; | |||
| int end_row = start_row + rows_per_gpu + (gpu_id == k-1 ? remainder : 0); | |||
| int rows_this_gpu = end_row - start_row; | |||
| // Allocate device memory | |||
| float *d_A, *d_B, *d_C; | |||
| CHECK_CUDA(cudaMalloc(&d_A, rows_this_gpu * n * sizeof(float))); | |||
| CHECK_CUDA(cudaMalloc(&d_B, n * n * sizeof(float))); | |||
| CHECK_CUDA(cudaMalloc(&d_C, rows_this_gpu * n * sizeof(float))); | |||
| // Copy data to device | |||
| CHECK_CUDA(cudaMemcpy(d_A, A + start_row * n, rows_this_gpu * n * sizeof(float), cudaMemcpyHostToDevice)); | |||
| CHECK_CUDA(cudaMemcpy(d_B, B, n * n * sizeof(float), cudaMemcpyHostToDevice)); | |||
| // Launch kernel | |||
| dim3 block(16, 16); | |||
| dim3 grid((n + block.x - 1) / block.x, (rows_this_gpu + block.y - 1) / block.y); | |||
| matrixMulKernel<<<grid, block>>>(d_A, d_B, d_C, n, start_row, end_row); | |||
| CHECK_CUDA(cudaDeviceSynchronize()); | |||
| // Copy results to shared memory | |||
| float* partial_C = new float[rows_this_gpu * n]; | |||
| CHECK_CUDA(cudaMemcpy(partial_C, d_C, rows_this_gpu * n * sizeof(float), cudaMemcpyDeviceToHost)); | |||
| for (int i = 0; i < rows_this_gpu; ++i) | |||
| memcpy(shm_ptr + (start_row + i) * n, partial_C + i * n, n * sizeof(float)); | |||
| // Cleanup | |||
| CHECK_CUDA(cudaFree(d_A)); | |||
| CHECK_CUDA(cudaFree(d_B)); | |||
| CHECK_CUDA(cudaFree(d_C)); | |||
| delete[] partial_C; | |||
| exit(EXIT_SUCCESS); | |||
| } | |||
| else if (pid < 0) { | |||
| perror("fork failed"); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| } | |||
| // Parent waits for children | |||
| for (int gpu_id = 0; gpu_id < k; ++gpu_id) | |||
| wait(NULL); | |||
| boost::chrono::system_clock::time_point end = boost::chrono::system_clock::now(); | |||
| //printMatrix(shm_ptr, n, n); | |||
| auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(end - start).count(); | |||
| std::cout << "Time: " << duration << " ms" << std::endl; | |||
| // Cleanup | |||
| for (int i = 0; i < n; ++i) { | |||
| delete[] A_2d[i]; | |||
| delete[] B_2d[i]; | |||
| } | |||
| delete[] A_2d; | |||
| delete[] B_2d; | |||
| delete[] A; | |||
| delete[] B; | |||
| munmap(shm_ptr, n * n * sizeof(float)); // Release mmap memory | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,187 @@ | |||
| # 💻 Matrix Computations on Diverse Platforms: A Multiprocessing Evaluation Across CPU, GPU and ARM Architectures | |||
| This project contains multiple matrix addition and multiplication implementations using different computing machines and methods. \ | |||
| The following is a layout showing how the files are distributed. | |||
| ## Implementations - File Map | |||
| ### 📁CPU | |||
| --- | |||
| <table> | |||
| <tr> | |||
| <td style="vertical-align: top; padding-right: 40px;"> | |||
| #### 📁addition | |||
| | Method | File | | |||
| |---------------------|-----------------------| | |||
| | Single Core | `single_add.cpp` | | |||
| | mmap + n forks | `mmap_add.cpp` | | |||
| | mmap + 24 forks | `mmap_24_add.cpp` | | |||
| | mmap + 100 forks | `mmap_100_add.cpp` | | |||
| | 100 Threads | `threads_add.cpp` | | |||
| </td> | |||
| <td style="padding-left: 40px;"> | |||
| #### 📁multiplication | |||
| | Method | File/Folder | | | | | |||
| |-----------------------------------|------------------------------|--------------------|-------------------------|--| | |||
| | Single Core | `single_mul.cpp` | | || | |||
| | |📁Prolific Forking |Number of Processes | File || | |||
| | | |n processes | `mmap_pr_n_mul.cpp` || | |||
| | | |24 processes | `mmap_pr_24_mul.cpp` || | |||
| | | |100 processes | `mmap_pr_100_mul.cpp` || | |||
| | |📁Collective Forking | | || | |||
| | | |n processes | `mmap_c_n_mul.cpp` || | |||
| | | |24 processes | `mmap_c_24_mul.cpp` || | |||
| | | |100 processes | `mmap_c_100_mul.cpp` || | |||
| | 100 Threads | `threads_100_mul.cpp` | | || | |||
| | n Threads | `threads_n_mul.cpp` | | || | |||
| | OpenMP | `mul_openMP.cpp` | | || | |||
| </td> | |||
| </tr> | |||
| </table> | |||
| ### 📁ARM | |||
| --- | |||
| <table> | |||
| <tr> | |||
| <td style="vertical-align: top; padding-right: 40px;"> | |||
| #### 📁addition | |||
| | Method | File | | |||
| |---------------------|-----------------------| | |||
| | Single Core | `single_add.cpp` | | |||
| | mmap + n forks | `mmap_add.cpp` | | |||
| | mmap + 24 forks | `mmap_24_add.cpp` | | |||
| | mmap + 100 forks | `mmap_100_add.cpp` | | |||
| | 100 Threads | `threads_add.cpp` | | |||
| </td> | |||
| <td style="padding-left: 40px;"> | |||
| #### 📁multiplication | |||
| | Method | File/Folder | | | | | |||
| |-----------------------------------|------------------------------|--------------------|-------------------------|--| | |||
| | Single Core | `single_mul.cpp` | | || | |||
| | |📁Prolific Forking |Number of Processes | File || | |||
| | | |n processes | `mmap_pr_n_mul.cpp` || | |||
| | | |24 processes | `mmap_pr_24_mul.cpp` || | |||
| | | |100 processes | `mmap_pr_100_mul.cpp` || | |||
| | |📁Collective Forking | | || | |||
| | | |n processes | `mmap_c_n_mul.cpp` || | |||
| | | |24 processes | `mmap_c_24_mul.cpp` || | |||
| | | |100 processes | `mmap_c_100_mul.cpp` || | |||
| | 100 Threads | `threads_100_mul.cpp` | | || | |||
| | n Threads | `threads_n_mul.cpp` | | || | |||
| | OpenMP | `mul_openMP.cpp` | | || | |||
| | OpenMPI | `mpimul_openMP.cpp` | | || | |||
| </td> | |||
| </tr> | |||
| </table> | |||
| ### 📁GPU | |||
| --- | |||
| | Operation | Method | File | | |||
| |-----------|---------------------|-------------------------------| | |||
| | Addition | GPU (CUDA) | `multi_gpu_matrix_add_mmap.cu`| | |||
| | Multiplication | GPU (CUDA) | `multi_gpu_matrix_mul_mmap.cu`| | |||
| ### 📁Roadmap | |||
| | Implementation | File | | |||
| |---------------------|-----------------| | |||
| | Summa Algorithm | `summa.cpp` | | |||
| | Strassen Algorithm | `strassen.cpp` | | |||
| | Cannon Algoritmhm | `cannon.cpp` | | |||
| | Fox Algorithm | `fox.cpp` | | |||
| ## 🛠️ Requirements | |||
|  \ | |||
| *NFS: Network File System \ | |||
| **NIS: Network Information Service | |||
| ### x86-64 | |||
| - Linux Ubuntu LTS | |||
| - C++ boost library | |||
| - OpenMP library for C++ | |||
| - OpenMPI | |||
| - C++ NVIDIA CUDA SDK any version later than 10 | |||
| ### ARM | |||
| At least 5 Raspberry Pi4 | |||
| - Linux Ubuntu Mate LTS 64bit | |||
| - 512MB | |||
| - Cloud Core | |||
| ### GPU | |||
| At least 4 GeForce GT 630 : | |||
| - 980MHz | |||
| - 981MB RAM | |||
| One GeForce RTX 3060Ti : | |||
| - 1.6GHz | |||
| - 8GB RAM | |||
| ## 🛠️ Compilation-Execution | |||
| Each folder contains a corresponding Makefile for the specific machine it is intended to run on. | |||
| After downloading the appropriate folder, the user should run | |||
| ```bash | |||
| make clean | |||
| ``` | |||
| before compiling for the first time. This ensures a clean build and helps avoid possible build errors. | |||
| Then, by running | |||
| ```bash | |||
| make | |||
| ``` | |||
| all files are compiled, and on subsequent runs, only the files that have been changed are recompiled. | |||
| In CPU and ARM versions of our code about threads and processes, the program expects the user to input the matrix size directly via the command line when starting the program (this is handled using atoi to convert the input to an integer). \ | |||
| For example, if we want to run "single_mul.cpp" with n = 1000: | |||
| ```bash | |||
| ./single_mul 1000 | |||
| ``` | |||
| With the according output : | |||
| ```bash | |||
| n,setup(us),exec*(us)1000,244,14474 | |||
| ``` | |||
| In the GPU version, once the user starts the program, it will prompt for the matrix size, followed by a prompt to select the number of available GPUs to distribute the workload across. | |||
| In the same way as in the previous versions, if we want to run "multi_gpu_matrix_mul_mmap.cu": | |||
| ```bash | |||
| ./multi_gpu_matrix_mul_mmap | |||
| ``` | |||
| After that, the following messages will be displayed, asking the user to input the matrix size(n) and the available GPUs to use: | |||
| ```bash | |||
| Enter matrix size (N): 1000 | |||
| Enter number of GPUs (K): 4 | |||
| ``` | |||
| then the according output will appear: | |||
| ```bash | |||
| Setup Time:47ms | |||
| Comp Time: 1642 ms | |||
| ``` | |||
| The OpenMP scenario works similarly to the GPU's one. The only difference is that the user is asked to enter the chunk size instead of the number of GPUs, so the message looks like this: | |||
| ```bash | |||
| Enter matrix size N: 1000 | |||
| Enter chunk size: 100 | |||
| ``` | |||
| Then, as the program is made to compute with all three OpenMP scheduling methods, the output bellow will be appeared: | |||
| ```bash | |||
| Schedule: static (chunk = 100), Time: 627 ms | |||
| Schedule: dynamic (chunk = 100), Time: 606 ms | |||
| Schedule: guided (chunk = 100), Time: 614 ms | |||
| ``` | |||
| Afterwards, MPI's compilation is done as follows: | |||
| ```bash | |||
| mpic++ mpimul_openmp.cpp -fopenmp -lboost_chrono -lboost_system -o mpimul | |||
| ``` | |||
| The user can then run the program using the following command | |||
| ```bash | |||
| mpirun --hostfile /etc/hostfile --mca btl_tcp_if_exclude docker0,lo -np 13 ./mpimul 1000 | |||
| ``` | |||
| @ -0,0 +1,228 @@ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/thread.hpp> | |||
| #include <boost/bind.hpp> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| namespace chrono = boost::chrono; | |||
| // Matrix structure to hold data and dimensions | |||
| struct Matrix { | |||
| int n; | |||
| double** data; | |||
| Matrix(int size) : n(size) { | |||
| data = new double*[n]; | |||
| for (int i = 0; i < n; ++i) { | |||
| data[i] = new double[n](); // Initialize to zero | |||
| } | |||
| } | |||
| ~Matrix() { | |||
| for (int i = 0; i < n; ++i) { | |||
| delete[] data[i]; | |||
| } | |||
| delete[] data; | |||
| } | |||
| }; | |||
| // Initialize matrix with random values | |||
| void init_matrix(Matrix& mat, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < mat.n; ++i) { | |||
| for (int j = 0; j < mat.n; ++j) { | |||
| mat.data[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Print matrix (for debugging) | |||
| void print_matrix(const Matrix& mat, const string& name) { | |||
| cout << name << ":\n"; | |||
| for (int i = 0; i < mat.n; ++i) { | |||
| for (int j = 0; j < mat.n; ++j) { | |||
| cout << mat.data[i][j] << " "; | |||
| } | |||
| cout << endl; | |||
| } | |||
| cout << endl; | |||
| } | |||
| // Worker thread for Cannon's algorithm | |||
| class CannonWorker { | |||
| int block_size; | |||
| int grid_size; | |||
| int row, col; | |||
| Matrix& a; | |||
| Matrix& b; | |||
| Matrix& c; | |||
| boost::barrier& sync_barrier; | |||
| vector<boost::barrier*>& row_barriers; | |||
| vector<boost::barrier*>& col_barriers; | |||
| public: | |||
| CannonWorker(int bs, int gs, int r, int c, Matrix& ma, Matrix& mb, Matrix& mc, | |||
| boost::barrier& sb, vector<boost::barrier*>& rb, vector<boost::barrier*>& cb) | |||
| : block_size(bs), grid_size(gs), row(r), col(c), | |||
| a(ma), b(mb), c(mc), sync_barrier(sb), row_barriers(rb), col_barriers(cb) {} | |||
| void operator()() { | |||
| // Calculate block boundaries | |||
| int row_start = row * block_size; | |||
| int row_end = row_start + block_size; | |||
| int col_start = col * block_size; | |||
| int col_end = col_start + block_size; | |||
| // Initial skewing: A left by row, B up by column | |||
| // Shift A left by row positions | |||
| for (int shift = 0; shift < row; ++shift) { | |||
| for (int i = 0; i < block_size; ++i) { | |||
| double temp = a.data[row_start + i][col_start]; | |||
| for (int j = 0; j < block_size - 1; ++j) { | |||
| a.data[row_start + i][col_start + j] = a.data[row_start + i][col_start + j + 1]; | |||
| } | |||
| a.data[row_start + i][col_start + block_size - 1] = temp; | |||
| } | |||
| } | |||
| // Shift B up by column positions | |||
| for (int shift = 0; shift < col; ++shift) { | |||
| for (int j = 0; j < block_size; ++j) { | |||
| double temp = b.data[row_start][col_start + j]; | |||
| for (int i = 0; i < block_size - 1; ++i) { | |||
| b.data[row_start + i][col_start + j] = b.data[row_start + i + 1][col_start + j]; | |||
| } | |||
| b.data[row_start + block_size - 1][col_start + j] = temp; | |||
| } | |||
| } | |||
| // Synchronize all threads after initial skewing | |||
| sync_barrier.wait(); | |||
| // Main computation loop | |||
| for (int step = 0; step < grid_size; ++step) { | |||
| // Local matrix multiplication | |||
| for (int i = 0; i < block_size; ++i) { | |||
| for (int j = 0; j < block_size; ++j) { | |||
| for (int k = 0; k < block_size; ++k) { | |||
| c.data[row_start + i][col_start + j] += | |||
| a.data[row_start + i][(col_start + k) % a.n] * | |||
| b.data[(row_start + k) % b.n][col_start + j]; | |||
| } | |||
| } | |||
| } | |||
| // Shift A left by 1 | |||
| for (int i = 0; i < block_size; ++i) { | |||
| double temp = a.data[row_start + i][col_start]; | |||
| for (int j = 0; j < block_size - 1; ++j) { | |||
| a.data[row_start + i][col_start + j] = a.data[row_start + i][col_start + j + 1]; | |||
| } | |||
| a.data[row_start + i][col_start + block_size - 1] = temp; | |||
| } | |||
| // Shift B up by 1 | |||
| for (int j = 0; j < block_size; ++j) { | |||
| double temp = b.data[row_start][col_start + j]; | |||
| for (int i = 0; i < block_size - 1; ++i) { | |||
| b.data[row_start + i][col_start + j] = b.data[row_start + i + 1][col_start + j]; | |||
| } | |||
| b.data[row_start + block_size - 1][col_start + j] = temp; | |||
| } | |||
| // Synchronize all threads before next step | |||
| sync_barrier.wait(); | |||
| } | |||
| } | |||
| }; | |||
| // Cannon's algorithm using Boost Threads | |||
| void cannon_multiply(Matrix& a, Matrix& b, Matrix& c, int grid_size) { | |||
| int block_size = a.n / grid_size; | |||
| // Create synchronization barriers | |||
| boost::barrier sync_barrier(grid_size * grid_size); | |||
| vector<boost::barrier*> row_barriers(grid_size); | |||
| vector<boost::barrier*> col_barriers(grid_size); | |||
| for (int i = 0; i < grid_size; ++i) { | |||
| row_barriers[i] = new boost::barrier(grid_size); | |||
| col_barriers[i] = new boost::barrier(grid_size); | |||
| } | |||
| // Create worker threads | |||
| boost::thread_group workers; | |||
| for (int i = 0; i < grid_size; ++i) { | |||
| for (int j = 0; j < grid_size; ++j) { | |||
| workers.create_thread(CannonWorker(block_size, grid_size, i, j, | |||
| a, b, c, sync_barrier, row_barriers, col_barriers)); | |||
| } | |||
| } | |||
| // Wait for all threads to complete | |||
| workers.join_all(); | |||
| // Clean up barriers | |||
| for (int i = 0; i < grid_size; ++i) { | |||
| delete row_barriers[i]; | |||
| delete col_barriers[i]; | |||
| } | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 3) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size> <grid_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| int grid_size = atoi(argv[2]); | |||
| if (n <= 0 || grid_size <= 0) { | |||
| cerr << "Matrix size and grid size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| if (n % grid_size != 0) { | |||
| cerr << "Matrix size must be divisible by grid size" << endl; | |||
| return 1; | |||
| } | |||
| chrono::system_clock::time_point setup_start, setup_end; | |||
| chrono::system_clock::time_point comp_start, comp_end; | |||
| setup_start = chrono::system_clock::now(); | |||
| Matrix a(n), b(n), c(n); | |||
| init_matrix(a); | |||
| init_matrix(b); | |||
| init_matrix(c, true); // Initialize to zero | |||
| setup_end = chrono::system_clock::now(); | |||
| if (n <= 8) { | |||
| print_matrix(a, "Matrix A"); | |||
| print_matrix(b, "Matrix B"); | |||
| } | |||
| comp_start = chrono::system_clock::now(); | |||
| cannon_multiply(a, b, c, grid_size); | |||
| comp_end = chrono::system_clock::now(); | |||
| if (n <= 8) { | |||
| print_matrix(c, "Result Matrix C"); | |||
| } | |||
| auto setup_time = chrono::duration_cast<chrono::milliseconds>(setup_end - setup_start); | |||
| auto comp_time = chrono::duration_cast<chrono::milliseconds>(comp_end - comp_start); | |||
| cout << "n,grid_size,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << grid_size << "," << setup_time.count() << "," << comp_time.count() << endl; | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,221 @@ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <random> | |||
| #include <vector> | |||
| #include <boost/thread.hpp> | |||
| #include <boost/bind.hpp> | |||
| #include <boost/chrono.hpp> | |||
| /*g++ -std=c++11 fox_boost.cpp -o fox_boost -lboost_thread -lboost_chrono -lboost_system | |||
| ./fox_boost 8 2 | |||
| Note that this implementation: | |||
| Uses shared memory (all threads access the full matrices) | |||
| Requires careful synchronization to avoid race conditions | |||
| May not be as efficient as MPI for large matrices due to shared memory contention | |||
| */ | |||
| using namespace std; | |||
| namespace chrono = boost::chrono; | |||
| // Matrix structure to hold data and dimensions | |||
| struct Matrix { | |||
| int n; | |||
| double** data; | |||
| Matrix(int size) : n(size) { | |||
| data = new double*[n]; | |||
| for (int i = 0; i < n; ++i) { | |||
| data[i] = new double[n](); // Initialize to zero | |||
| } | |||
| } | |||
| ~Matrix() { | |||
| for (int i = 0; i < n; ++i) { | |||
| delete[] data[i]; | |||
| } | |||
| delete[] data; | |||
| } | |||
| }; | |||
| // Initialize matrix with random values | |||
| void init_matrix(Matrix& mat, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < mat.n; ++i) { | |||
| for (int j = 0; j < mat.n; ++j) { | |||
| mat.data[i][j] = zero ? 0.0 : dis(gen); | |||
| } | |||
| } | |||
| } | |||
| // Print matrix (for debugging) | |||
| void print_matrix(const Matrix& mat, const string& name) { | |||
| cout << name << ":\n"; | |||
| for (int i = 0; i < mat.n; ++i) { | |||
| for (int j = 0; j < mat.n; ++j) { | |||
| cout << mat.data[i][j] << " "; | |||
| } | |||
| cout << endl; | |||
| } | |||
| cout << endl; | |||
| } | |||
| // Worker thread for Fox algorithm | |||
| class FoxWorker { | |||
| int block_size; | |||
| int grid_size; | |||
| int row, col; | |||
| Matrix& a; | |||
| Matrix& b; | |||
| Matrix& c; | |||
| boost::barrier& sync_barrier; | |||
| vector<boost::barrier*>& row_barriers; | |||
| public: | |||
| FoxWorker(int bs, int gs, int r, int c, Matrix& ma, Matrix& mb, Matrix& mc, | |||
| boost::barrier& sb, vector<boost::barrier*>& rb) | |||
| : block_size(bs), grid_size(gs), row(r), col(c), | |||
| a(ma), b(mb), c(mc), sync_barrier(sb), row_barriers(rb) {} | |||
| void operator()() { | |||
| // Calculate block boundaries | |||
| int row_start = row * block_size; | |||
| int row_end = row_start + block_size; | |||
| int col_start = col * block_size; | |||
| int col_end = col_start + block_size; | |||
| // Temporary storage for broadcast block | |||
| Matrix temp_block(block_size); | |||
| for (int step = 0; step < grid_size; ++step) { | |||
| // Determine broadcast root for this step | |||
| int bcast_root = (row + step) % grid_size; | |||
| // Broadcast phase - only one thread per row participates | |||
| if (col == bcast_root) { | |||
| // Copy my block of A to temp_block | |||
| for (int i = 0; i < block_size; ++i) { | |||
| for (int j = 0; j < block_size; ++j) { | |||
| temp_block.data[i][j] = a.data[row_start + i][col_start + j]; | |||
| } | |||
| } | |||
| } | |||
| // Wait for all threads in this row to reach broadcast point | |||
| row_barriers[row]->wait(); | |||
| // All threads in row now have access to temp_block | |||
| // Local multiplication phase | |||
| for (int i = 0; i < block_size; ++i) { | |||
| for (int j = 0; j < block_size; ++j) { | |||
| for (int k = 0; k < block_size; ++k) { | |||
| c.data[row_start + i][col_start + j] += | |||
| temp_block.data[i][k] * b.data[row_start + k][col_start + j]; | |||
| } | |||
| } | |||
| } | |||
| // Shift blocks of B upwards | |||
| if (row > 0) { | |||
| // Send my block of B to the neighbor above | |||
| for (int i = 0; i < block_size; ++i) { | |||
| for (int j = 0; j < block_size; ++j) { | |||
| double temp = b.data[row_start + i][col_start + j]; | |||
| b.data[row_start + i][col_start + j] = b.data[(row_start + i - block_size + a.n) % a.n][col_start + j]; | |||
| b.data[(row_start + i - block_size + a.n) % a.n][col_start + j] = temp; | |||
| } | |||
| } | |||
| } | |||
| // Synchronize all threads before next step | |||
| sync_barrier.wait(); | |||
| } | |||
| } | |||
| }; | |||
| // Fox algorithm using Boost Threads | |||
| void fox_multiply(Matrix& a, Matrix& b, Matrix& c, int grid_size) { | |||
| int block_size = a.n / grid_size; | |||
| // Create synchronization barriers | |||
| boost::barrier sync_barrier(grid_size * grid_size); | |||
| vector<boost::barrier*> row_barriers(grid_size); | |||
| for (int i = 0; i < grid_size; ++i) { | |||
| row_barriers[i] = new boost::barrier(grid_size); | |||
| } | |||
| // Create worker threads | |||
| boost::thread_group workers; | |||
| for (int i = 0; i < grid_size; ++i) { | |||
| for (int j = 0; j < grid_size; ++j) { | |||
| workers.create_thread(FoxWorker(block_size, grid_size, i, j, | |||
| a, b, c, sync_barrier, row_barriers)); | |||
| } | |||
| } | |||
| // Wait for all threads to complete | |||
| workers.join_all(); | |||
| // Clean up barriers | |||
| for (int i = 0; i < grid_size; ++i) { | |||
| delete row_barriers[i]; | |||
| } | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 3) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size> <grid_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| int grid_size = atoi(argv[2]); | |||
| if (n <= 0 || grid_size <= 0) { | |||
| cerr << "Matrix size and grid size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| if (n % grid_size != 0) { | |||
| cerr << "Matrix size must be divisible by grid size" << endl; | |||
| return 1; | |||
| } | |||
| chrono::system_clock::time_point setup_start, setup_end; | |||
| chrono::system_clock::time_point comp_start, comp_end; | |||
| setup_start = chrono::system_clock::now(); | |||
| Matrix a(n), b(n), c(n); | |||
| init_matrix(a); | |||
| init_matrix(b); | |||
| init_matrix(c, true); // Initialize to zero | |||
| setup_end = chrono::system_clock::now(); | |||
| if (n <= 8) { | |||
| print_matrix(a, "Matrix A"); | |||
| print_matrix(b, "Matrix B"); | |||
| } | |||
| comp_start = chrono::system_clock::now(); | |||
| fox_multiply(a, b, c, grid_size); | |||
| comp_end = chrono::system_clock::now(); | |||
| if (n <= 8) { | |||
| print_matrix(c, "Result Matrix C"); | |||
| } | |||
| auto setup_time = chrono::duration_cast<chrono::milliseconds>(setup_end - setup_start); | |||
| auto comp_time = chrono::duration_cast<chrono::milliseconds>(comp_end - comp_start); | |||
| cout << "n,grid_size,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << grid_size << "," << setup_time.count() << "," << comp_time.count() << endl; | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,182 @@ | |||
| /*Strassen 1D - Matrix sizes at the power of to to subdivice properly*/ | |||
| /*All matrices use 1D arrays with flattened indexing. | |||
| Recursion is threaded for the 7 Strassen multiplications up to threshold 64 using boost::bind*/ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <cstring> | |||
| #include <boost/thread.hpp> | |||
| #include <boost/bind/bind.hpp> | |||
| #include <boost/chrono.hpp> | |||
| #include <random> | |||
| using namespace std; | |||
| using namespace boost::placeholders; | |||
| double* create_matrix(int size) { | |||
| return new double[size * size](); | |||
| } | |||
| void cleanup_matrix(double* matrix) { | |||
| delete[] matrix; | |||
| } | |||
| void add(double* a, double* b, double* result, int size) { | |||
| for (int i = 0; i < size * size; ++i) | |||
| result[i] = a[i] + b[i]; | |||
| } | |||
| void subtract(double* a, double* b, double* result, int size) { | |||
| for (int i = 0; i < size * size; ++i) | |||
| result[i] = a[i] - b[i]; | |||
| } | |||
| void naive_mul(double* a, double* b, double* c, int size) { | |||
| for (int i = 0; i < size; ++i) | |||
| for (int j = 0; j < size; ++j) | |||
| for (int k = 0; k < size; ++k) | |||
| c[i * size + j] += a[i * size + k] * b[k * size + j]; | |||
| } | |||
| void strassen(double* a, double* b, double* c, int size, int threshold = 64) { | |||
| if (size <= threshold) { | |||
| naive_mul(a, b, c, size); | |||
| return; | |||
| } | |||
| int newSize = size / 2; | |||
| int block = newSize * newSize; | |||
| double *a11 = a; | |||
| double *a12 = a + newSize; | |||
| double *a21 = a + newSize * size; | |||
| double *a22 = a + newSize * size + newSize; | |||
| double *b11 = b; | |||
| double *b12 = b + newSize; | |||
| double *b21 = b + newSize * size; | |||
| double *b22 = b + newSize * size + newSize; | |||
| double *c11 = c; | |||
| double *c12 = c + newSize; | |||
| double *c21 = c + newSize * size; | |||
| double *c22 = c + newSize * size + newSize; | |||
| // Allocate temp matrices | |||
| double *m1 = create_matrix(newSize), *m2 = create_matrix(newSize), *m3 = create_matrix(newSize); | |||
| double *m4 = create_matrix(newSize), *m5 = create_matrix(newSize), *m6 = create_matrix(newSize), *m7 = create_matrix(newSize); | |||
| double *aTemp = create_matrix(newSize), *bTemp = create_matrix(newSize); | |||
| boost::thread_group group; | |||
| // M1 = (A11 + A22) * (B11 + B22) | |||
| add(a11, a22, aTemp, newSize); | |||
| add(b11, b22, bTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, aTemp, bTemp, m1, newSize, threshold)); | |||
| // M2 = (A21 + A22) * B11 | |||
| add(a21, a22, aTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, aTemp, b11, m2, newSize, threshold)); | |||
| // M3 = A11 * (B12 - B22) | |||
| subtract(b12, b22, bTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, a11, bTemp, m3, newSize, threshold)); | |||
| // M4 = A22 * (B21 - B11) | |||
| subtract(b21, b11, bTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, a22, bTemp, m4, newSize, threshold)); | |||
| // M5 = (A11 + A12) * B22 | |||
| add(a11, a12, aTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, aTemp, b22, m5, newSize, threshold)); | |||
| // M6 = (A21 - A11) * (B11 + B12) | |||
| subtract(a21, a11, aTemp, newSize); | |||
| add(b11, b12, bTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, aTemp, bTemp, m6, newSize, threshold)); | |||
| // M7 = (A12 - A22) * (B21 + B22) | |||
| subtract(a12, a22, aTemp, newSize); | |||
| add(b21, b22, bTemp, newSize); | |||
| group.create_thread(boost::bind(strassen, aTemp, bTemp, m7, newSize, threshold)); | |||
| group.join_all(); | |||
| // C11 = M1 + M4 - M5 + M7 | |||
| for (int i = 0; i < block; ++i) | |||
| c11[i] = m1[i] + m4[i] - m5[i] + m7[i]; | |||
| // C12 = M3 + M5 | |||
| for (int i = 0; i < block; ++i) | |||
| c12[i] = m3[i] + m5[i]; | |||
| // C21 = M2 + M4 | |||
| for (int i = 0; i < block; ++i) | |||
| c21[i] = m2[i] + m4[i]; | |||
| // C22 = M1 - M2 + M3 + M6 | |||
| for (int i = 0; i < block; ++i) | |||
| c22[i] = m1[i] - m2[i] + m3[i] + m6[i]; | |||
| cleanup_matrix(m1); | |||
| cleanup_matrix(m2); | |||
| cleanup_matrix(m3); | |||
| cleanup_matrix(m4); | |||
| cleanup_matrix(m5); | |||
| cleanup_matrix(m6); | |||
| cleanup_matrix(m7); | |||
| cleanup_matrix(aTemp); | |||
| cleanup_matrix(bTemp); | |||
| } | |||
| void init_matrix(double* mat, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n * n; ++i) | |||
| mat[i] = zero ? 0.0 : dis(gen); | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size (power of 2)>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if ((n & (n - 1)) != 0) { | |||
| cerr << "Matrix size must be a power of 2!" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| double* a = create_matrix(n); | |||
| double* b = create_matrix(n); | |||
| double* c = create_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| strassen(a, b, c, n); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(a); | |||
| cleanup_matrix(b); | |||
| cleanup_matrix(c); | |||
| return 0; | |||
| } | |||
| @ -0,0 +1,116 @@ | |||
| /*Scalable Universal Matrix Multiplication Algorithm) works by dividing matrices into blocks and processing them in phases.*/ | |||
| /*Matrix size n is divisible by sqrt(p) where p = number of threads.*/ | |||
| /*Flat (1D) memory is used for all matrices.*/ | |||
| /*Blocked computation is handled with explicit loop tiling. -1990*/ | |||
| /*Partitions the matrices A and B across a 2D processor grid | |||
| Performs a series of broadcasts of matrix blocks along rows and columns | |||
| Computes partial results at each processor | |||
| Accumulates results to form the final product matrix C = A × B*/ | |||
| #include <iostream> | |||
| #include <cstdlib> | |||
| #include <cmath> | |||
| #include <random> | |||
| #include <boost/thread.hpp> | |||
| #include <boost/bind/bind.hpp> | |||
| #include <boost/chrono.hpp> | |||
| using namespace std; | |||
| using namespace boost::placeholders; | |||
| const int MAX_THREADS = 100; | |||
| // Allocate 1D array matrix | |||
| double* create_matrix(int n) { | |||
| return new double[n * n]; | |||
| } | |||
| void cleanup_matrix(double* matrix) { | |||
| delete[] matrix; | |||
| } | |||
| void init_matrix(double* matrix, int n, bool zero = false) { | |||
| random_device rd; | |||
| mt19937 gen(rd()); | |||
| uniform_real_distribution<double> dis(0.0, 1.0); | |||
| for (int i = 0; i < n * n; ++i) | |||
| matrix[i] = zero ? 0.0 : dis(gen); | |||
| } | |||
| // SUMMA worker thread: computes block (i_block) of C | |||
| void summa_block(int block_row_start, int block_row_end, int n, int block_size, | |||
| double* a, double* b, double* c) { | |||
| for (int i = block_row_start; i < block_row_end; ++i) { | |||
| for (int j = 0; j < n; ++j) { | |||
| double sum = 0.0; | |||
| for (int k = 0; k < n; ++k) { | |||
| sum += a[i * n + k] * b[k * n + j]; | |||
| } | |||
| c[i * n + j] = sum; | |||
| } | |||
| } | |||
| } | |||
| int main(int argc, char** argv) { | |||
| if (argc != 2) { | |||
| cerr << "Usage: " << argv[0] << " <matrix_size>" << endl; | |||
| return 1; | |||
| } | |||
| int n = atoi(argv[1]); | |||
| if (n <= 0) { | |||
| cerr << "Matrix size must be positive" << endl; | |||
| return 1; | |||
| } | |||
| boost::chrono::system_clock::time_point t1, t2, t3, t4; | |||
| t1 = boost::chrono::system_clock::now(); | |||
| double* a = create_matrix(n); | |||
| double* b = create_matrix(n); | |||
| double* c = create_matrix(n); | |||
| init_matrix(a, n); | |||
| init_matrix(b, n); | |||
| init_matrix(c, n, true); | |||
| t2 = boost::chrono::system_clock::now(); | |||
| t3 = boost::chrono::system_clock::now(); | |||
| boost::thread_group group; | |||
| int rows_per_thread = n / MAX_THREADS; | |||
| int remaining = n % MAX_THREADS; | |||
| int current_row = 0; | |||
| for (int i = 0; i < MAX_THREADS && current_row < n; ++i) { | |||
| int rows_to_process = rows_per_thread + (i < remaining ? 1 : 0); | |||
| int end_row = current_row + rows_to_process; | |||
| group.create_thread( | |||
| boost::bind(summa_block, current_row, end_row, n, rows_to_process, a, b, c) | |||
| ); | |||
| current_row = end_row; | |||
| } | |||
| group.join_all(); | |||
| t4 = boost::chrono::system_clock::now(); | |||
| auto setup_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t2 - t1); | |||
| auto exec_time = boost::chrono::duration_cast<boost::chrono::milliseconds>(t4 - t3); | |||
| cout << "n,setup(ms),exec(ms)" << endl; | |||
| cout << n << "," << setup_time.count() << "," << exec_time.count() << endl; | |||
| cleanup_matrix(a); | |||
| cleanup_matrix(b); | |||
| cleanup_matrix(c); | |||
| return 0; | |||
| } | |||