#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;
|
|
}
|