/*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;
|
|
}
|
|
|