optimizations, tried optimization via iterator callback which is slower, separating matrix multiplication

This commit is contained in:
David Holzmüller 2019-04-07 12:01:02 +02:00
parent cc78e036b2
commit 01cd5ced1b
3 changed files with 438 additions and 9 deletions

View File

@ -3,7 +3,7 @@ project(fast_sparse_interpolation CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fmessage-length=0 -std=c++11 -D_GNU_SOURCE=1 -D_REENTRANT -Dlinux -D__linux__ -Dx86_64 -D__x86_64__") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fmessage-length=0 -std=c++11 -D_GNU_SOURCE=1 -D_REENTRANT -Dlinux -D__linux__ -Dx86_64 -D__x86_64__")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -O0") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -O0")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") #-mtune=native -march=native set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -mtune=native -march=native") #-mtune=native -march=native
set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} -L/usr/local/lib") set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} -L/usr/local/lib")
link_directories(/usr/local/lib) link_directories(/usr/local/lib)

View File

@ -12,6 +12,94 @@
namespace fsi { namespace fsi {
template <size_t dim>
void iterateRecursive(size_t remaining, std::function<void(size_t)> callback) {
for (size_t val = 0; val <= remaining; ++val) {
iterateRecursive<dim - 1>(remaining - val, callback);
}
}
template <>
void iterateRecursive<0>(size_t remaining, std::function<void(size_t)> callback) {
callback(remaining);
}
template <size_t d>
class TemplateBoundedSumIterator {
size_t bound;
std::vector<size_t> index_head; // contains all entries of the index except the last one
size_t index_head_sum;
bool is_done;
public:
TemplateBoundedSumIterator(size_t bound)
: bound(bound), index_head(d - 1, 0), index_head_sum(0), is_done(false){};
void iterate(std::function<void(size_t)> callback,
std::function<void(size_t)> outerLoopCallback) {
for (size_t val = 0; val <= bound; ++val) {
outerLoopCallback(val);
iterateRecursive<d - 2>(bound - val, callback);
}
}
/**
* At the current multi-index (i_1, ..., i_{d-1}, 0), return how many multi-indices starting with
* (i_1, ..., i_{d-1}) are contained in the multi-index set, then advance to the next multi-index
* that ends with a zero.
*/
size_t lastDimensionCount() { return bound - index_head_sum + 1; }
void next() {
if (bound > index_head_sum) {
index_head_sum += 1;
index_head[d - 2] += 1;
} else {
int dim = d - 2;
for (; dim >= 0 && index_head[dim] == 0; --dim) {
// reduce dimension until entry is nonzero
}
if (dim > 0) {
index_head_sum -= (index_head[dim] - 1);
index_head[dim] = 0;
index_head[dim - 1] += 1;
} else if (dim == 0) {
index_head[dim] = 0;
index_head_sum = 0;
is_done = true;
}
}
}
size_t firstIndex() { return index_head[0]; }
size_t indexAt(size_t dim) { return index_head[dim]; }
bool done() { return is_done; }
void reset() {
index_head = std::vector<size_t>(d - 1, 0);
index_head_sum = 0;
is_done = false;
}
size_t dim() { return d; }
std::vector<size_t> indexBounds() { return std::vector<size_t>(d, bound + 1); }
/**
* Returns an iterator where the last index moves to the front. For an index set defined by a sum
* bound, nothing changes.
*/
TemplateBoundedSumIterator<d> cycle() {
TemplateBoundedSumIterator<d> it = *this;
it.reset();
return it;
}
};
class BoundedSumIterator { class BoundedSumIterator {
size_t d; size_t d;
size_t bound; size_t bound;
@ -68,6 +156,16 @@ class BoundedSumIterator {
size_t dim() { return d; } size_t dim() { return d; }
std::vector<size_t> indexBounds() { return std::vector<size_t>(d, bound + 1); } std::vector<size_t> indexBounds() { return std::vector<size_t>(d, bound + 1); }
/**
* Returns an iterator where the last index moves to the front. For an index set defined by a sum
* bound, nothing changes.
*/
BoundedSumIterator cycle() {
BoundedSumIterator it = *this;
it.reset();
return it;
}
}; };
class StandardBoundedSumIterator { class StandardBoundedSumIterator {
@ -166,6 +264,114 @@ inline std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
return os; return os;
} }
template <typename It>
void multiply_lower_triangular_inplace(It const &it,
std::vector<boost::numeric::ublas::matrix<double>> L,
MultiDimVector &v) {
size_t d = L.size();
auto n = it.indexBounds();
for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Lk = L[k];
it.reset();
size_t first_v_index = 0;
size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
size_t data_size = v.data[0].size();
while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount();
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0;
for (size_t j = 0; j <= i; ++j) {
sum += Lk(i, j) * (*(offset_data_pointer + j));
}
w.data[i].push_back(sum);
}
second_v_index += last_dim_count;
if (second_v_index >= data_size) {
second_v_index = 0;
first_v_index += 1;
data_pointer = &v.data[first_v_index][0];
data_size = v.data[first_v_index].size();
}
it.next();
}
v.swap(w);
it = it.cycle();
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
}
}
template <typename It>
void multiply_upper_triangular_inplace(It const &it,
std::vector<boost::numeric::ublas::matrix<double>> U,
MultiDimVector &v) {
size_t d = U.size();
auto n = it.indexBounds();
for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Uk = U[k];
it.reset();
size_t first_v_index = 0;
size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
size_t data_size = v.data[0].size();
while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount();
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0;
for (size_t j = i; j < last_dim_count; ++j) {
sum += Uk(i, j) * (*(offset_data_pointer + j));
}
w.data[i].push_back(sum);
}
second_v_index += last_dim_count;
if (second_v_index >= data_size) {
second_v_index = 0;
first_v_index += 1;
data_pointer = &v.data[first_v_index][0];
data_size = v.data[first_v_index].size();
}
it.next();
}
it = it.cycle();
v.swap(w);
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
}
}
template <typename Func, typename It, typename Phi, typename X> template <typename Func, typename It, typename Phi, typename X>
MultiDimVector interpolate(Func f, It it, Phi phi, X x) { MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
auto n = it.indexBounds(); auto n = it.indexBounds();
@ -241,6 +447,8 @@ MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
// std::cout << v.data[i] << "\n\n"; // std::cout << v.data[i] << "\n\n";
// } // }
size_t num_sum_op = 0;
std::cout << "First matrix multiplication\n"; std::cout << "First matrix multiplication\n";
// multiply by L^{-1} // multiply by L^{-1}
@ -249,32 +457,46 @@ MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
for (int k = d - 1; k >= 0; --k) { for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]); MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Lkinv = Linv[k]; auto &Lkinv = Linv[k];
it.reset(); it.reset();
size_t first_v_index = 0; size_t first_v_index = 0;
size_t second_v_index = 0; size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
size_t data_size = v.data[0].size();
while (not it.done()) { while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount(); size_t last_dim_count = it.lastDimensionCount();
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) { for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0; double sum = 0.0;
for (size_t j = 0; j <= i; ++j) { for (size_t j = 0; j <= i; ++j) {
// std::cout << Lkinv(i, j) << " * " << v.data[first_v_index][second_v_index + j] << "\n"; sum += Lkinv(i, j) * (*(offset_data_pointer + j));
sum += Lkinv(i, j) * v.data[first_v_index][second_v_index + j]; ++num_sum_op;
} }
w.data[i].push_back(sum); w.data[i].push_back(sum);
} }
second_v_index += last_dim_count; second_v_index += last_dim_count;
if (second_v_index >= v.data[first_v_index].size()) { if (second_v_index >= data_size) {
second_v_index = 0; second_v_index = 0;
first_v_index += 1; first_v_index += 1;
data_pointer = &v.data[first_v_index][0];
data_size = v.data[first_v_index].size();
} }
it.next(); it.next();
} }
v.swap(w); v.swap(w);
it = it.cycle();
// for (size_t i = 0; i < v.data.size(); ++i) { // for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n"; // std::cout << v.data[i] << "\n\n";
// } // }
@ -288,30 +510,44 @@ MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
for (int k = d - 1; k >= 0; --k) { for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]); MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Ukinv = Uinv[k]; auto &Ukinv = Uinv[k];
it.reset(); it.reset();
size_t first_v_index = 0; size_t first_v_index = 0;
size_t second_v_index = 0; size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
size_t data_size = v.data[0].size();
while (not it.done()) { while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount(); size_t last_dim_count = it.lastDimensionCount();
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) { for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0; double sum = 0.0;
for (size_t j = i; j < last_dim_count; ++j) { for (size_t j = i; j < last_dim_count; ++j) {
sum += Ukinv(i, j) * v.data[first_v_index][second_v_index + j]; sum += Ukinv(i, j) * (*(offset_data_pointer + j));
++num_sum_op;
} }
w.data[i].push_back(sum); w.data[i].push_back(sum);
} }
second_v_index += last_dim_count; second_v_index += last_dim_count;
if (second_v_index >= v.data[first_v_index].size()) { if (second_v_index >= data_size) {
second_v_index = 0; second_v_index = 0;
first_v_index += 1; first_v_index += 1;
data_pointer = &v.data[first_v_index][0];
data_size = v.data[first_v_index].size();
} }
it.next(); it.next();
} }
it = it.cycle();
v.swap(w); v.swap(w);
// for (size_t i = 0; i < v.data.size(); ++i) { // for (size_t i = 0; i < v.data.size(); ++i) {
@ -319,6 +555,184 @@ MultiDimVector interpolate(Func f, It it, Phi phi, X x) {
// } // }
} }
std::cout << "Total number of summation operations: " << num_sum_op << "\n";
return v;
}
template <typename Func, typename It, typename Phi, typename X>
MultiDimVector interpolate2(Func f, It it, Phi phi, X x) {
auto n = it.indexBounds();
size_t d = it.dim();
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> Matrix;
std::vector<Matrix> Linv, Uinv;
// create matrices and inverted LU decompositions
for (size_t k = 0; k < d; ++k) {
// std::cout << "Matrix creation loop\n";
Matrix Mk(n[k], n[k]);
for (size_t i = 0; i < n[k]; ++i) {
for (size_t j = 0; j < n[k]; ++j) {
Mk(i, j) = phi[k](j)(x[k](i));
}
}
// std::cout << "Matrices:\n";
// std::cout << Mk << "\n";
ublas::lu_factorize(Mk);
// std::cout << Mk << "\n";
Matrix Lkinv = ublas::identity_matrix<double>(n[k]);
Matrix Ukinv = ublas::identity_matrix<double>(n[k]);
ublas::inplace_solve(Mk, Lkinv, ublas::unit_lower_tag());
ublas::inplace_solve(Mk, Ukinv, ublas::upper_tag());
// std::cout << Lkinv << "\n";
// std::cout << Ukinv << "\n";
Linv.push_back(Lkinv);
Uinv.push_back(Ukinv);
}
MultiDimVector v(n[0]);
std::cout << "Compute function values\n";
// compute function values
it.reset();
std::vector<double> point(d);
while (not it.done()) {
size_t last_dim_count = it.lastDimensionCount();
for (size_t dim = 0; dim < d - 1; ++dim) {
point[dim] = x[dim](it.indexAt(dim));
}
for (size_t last_dim_idx = 0; last_dim_idx < last_dim_count; ++last_dim_idx) {
point[d - 1] = x[d - 1](last_dim_idx);
double function_value = f(point);
v.data[it.firstIndex()].push_back(function_value);
}
it.next();
}
size_t number = 0;
for (size_t dim = 0; dim < n[0]; ++dim) {
number += v.data[dim].size();
}
std::cout << "number of points: " << number << "\n";
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
size_t num_sum_op = 0;
std::cout << "First matrix multiplication\n";
// multiply by L^{-1}
// the multiplication is based on a cyclic permutation of the indices: the last index of v becomes
// the first index of w
for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Lkinv = Linv[k];
it.reset();
size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
it.iterate(
[&](size_t last_dim_count) {
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0;
for (size_t j = 0; j <= i; ++j) {
sum += Lkinv(i, j) * (*(offset_data_pointer + j));
++num_sum_op;
}
w.data[i].push_back(sum);
}
second_v_index += last_dim_count;
},
[&](size_t first_dim_index) {
second_v_index = 0;
data_pointer = &v.data[first_dim_index][0];
});
v.swap(w);
it = it.cycle();
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
}
std::cout << "Second matrix multiplication\n";
// multiply by U^{-1}
// the multiplication is based on a cyclic permutation of the indices: the last index of v becomes
// the first index of w
for (int k = d - 1; k >= 0; --k) {
MultiDimVector w(n[k]);
for (size_t idx = 0; idx < n[k]; ++idx) {
// TODO: make compatible with other iterators
w.data[idx].reserve(v.data[idx].size());
}
auto &Ukinv = Uinv[k];
it.reset();
size_t second_v_index = 0;
double *data_pointer = &v.data[0][0];
it.iterate(
[&](size_t last_dim_count) {
double *offset_data_pointer = data_pointer + second_v_index;
for (size_t i = 0; i < last_dim_count; ++i) {
double sum = 0.0;
for (size_t j = i; j < last_dim_count; ++j) {
sum += Ukinv(i, j) * (*(offset_data_pointer + j));
++num_sum_op;
}
w.data[i].push_back(sum);
}
second_v_index += last_dim_count;
},
[&](size_t first_dim_index) {
second_v_index = 0;
data_pointer = &v.data[first_dim_index][0];
});
v.swap(w);
it = it.cycle();
// for (size_t i = 0; i < v.data.size(); ++i) {
// std::cout << v.data[i] << "\n\n";
// }
}
std::cout << "Total number of summation operations: " << num_sum_op << "\n";
return v; return v;
} }

View File

@ -52,11 +52,26 @@ inline std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
return os; return os;
} }
// TODO: refactoring:
/**
* - Interface for MultiDimVector
* - Separate Functions for L- and U-Multiplication, Creation of LU decomposition, computation of
* function values
* - Pass a callback function to iterator instead of calling it.next() - this might improve the
* vector functions (could be made recursive or even loops for fixed dimension implementation)
* - Typed interface?
* - Tests?
* - More point distributions / Basis functions?
* - Forward evaluation?
* - Computation of derivatives?
*/
// using namespace fsi; // using namespace fsi;
int main() { int main() {
size_t d = 12; constexpr size_t d = 30;
size_t bound = 16; size_t bound = 8;
fsi::BoundedSumIterator it(d, bound); fsi::TemplateBoundedSumIterator<d> it(bound);
// fsi::BoundedSumIterator it(d, bound);
std::vector<MonomialFunctions> phi(d); std::vector<MonomialFunctions> phi(d);
std::vector<GoldenPointDistribution> x(d); std::vector<GoldenPointDistribution> x(d);
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();