AMGCL库的Backends及使用示例
AMGCL是一个用于解决大型稀疏线性方程组的C++库,它提供了多种后端(backends)实现,允许用户根据不同的硬件和性能需求选择合适的计算后端。
AMGCL支持的主要Backends
内置Backends:
builtin
- 默认的纯C++实现block
- 支持块状矩阵
并行计算Backends:
openmp
- 基于OpenMP的并行实现cuda
- NVIDIA CUDA后端vexcl
- 基于VexCL的异构计算后端(支持OpenCL和CUDA)
外部库集成Backends:
eigen
- Eigen库后端ublas
- Boost.uBLAS后端
使用示例
1. 使用内置后端(builtin)
#include <amgcl/backend/builtin.hpp>
#include <amgcl/make_solver.hpp>
#include <amgcl/amg.hpp>
#include <amgcl/coarsening/smoothed_aggregation.hpp>
#include <amgcl/relaxation/spai0.hpp>
#include <amgcl/solver/bicgstab.hpp>
int main() {
// 矩阵大小
const int n = 100;
// 使用内置后端创建矩阵
typedef amgcl::backend::builtin<double> Backend;
// 设置矩阵(这里用对角线矩阵作为示例)
std::vector<int> ptr = {0, 1, 2, 3, ..., n};
std::vector<int> col = {0, 1, 2, 3, ..., n-1};
std::vector<double> val(n, 2.0); // 对角线值为2
// 创建AMG层次结构
typedef amgcl::make_solver<
amgcl::amg<
Backend,
amgcl::coarsening::smoothed_aggregation,
amgcl::relaxation::spai0
>,
amgcl::solver::bicgstab<Backend>
> Solver;
// 设置求解器参数
Solver::params prm;
prm.solver.max_iter = 100;
// 初始化求解器
Solver solve(std::tie(n, ptr, col, val), prm);
// 创建右侧向量和初始解
std::vector<double> rhs(n, 1.0), x(n, 0.0);
// 求解
int iters;
double error;
std::tie(iters, error) = solve(rhs, x);
std::cout << "Iterations: " << iters << std::endl;
std::cout << "Error: " << error << std::endl;
return 0;
}
2. 使用OpenMP后端
#include <amgcl/backend/builtin.hpp>
#include <amgcl/value_type/static_matrix.hpp>
#include <amgcl/adapter/block_matrix.hpp>
#include <amgcl/make_solver.hpp>
#include <amgcl/amg.hpp>
#include <amgcl/coarsening/smoothed_aggregation.hpp>
#include <amgcl/relaxation/ilu0.hpp>
#include <amgcl/solver/cg.hpp>
#include <amgcl/profiler.hpp>
int main() {
// 启用OpenMP后端
typedef amgcl::backend::builtin<double> Backend;
// 设置OpenMP线程数
omp_set_num_threads(4);
// 创建矩阵(这里用随机矩阵作为示例)
const int n = 1000;
std::vector<int> ptr(n+1), col;
std::vector<double> val;
// 填充矩阵(5对角矩阵)
for(int i = 0; i < n; ++i) {
ptr[i] = col.size();
for(int j = std::max(0, i-2); j < std::min(n, i+3); ++j) {
col.push_back(j);
val.push_back(i == j ? 10.0 : -1.0);
}
}
ptr[n] = col.size();
// 创建AMG求解器
typedef amgcl::make_solver<
amgcl::amg<
Backend,
amgcl::coarsening::smoothed_aggregation,
amgcl::relaxation::ilu0
>,
amgcl::solver::cg<Backend>
> Solver;
// 设置参数
Solver::params prm;
prm.solver.tol = 1e-6;
// 初始化求解器
Solver solve(std::tie(n, ptr, col, val), prm);
// 创建右侧向量和初始解
std::vector<double> rhs(n, 1.0), x(n, 0.0);
// 求解
int iters;
double error;
std::tie(iters, error) = solve(rhs, x);
std::cout << "Iterations: " << iters << std::endl;
std::cout << "Error: " << error << std::endl;
return 0;
}
3. 使用CUDA后端
#include <amgcl/backend/cuda.hpp>
#include <amgcl/make_solver.hpp>
#include <amgcl/amg.hpp>
#include <amgcl/coarsening/smoothed_aggregation.hpp>
#include <amgcl/relaxation/ilu0.hpp>
#include <amgcl/solver/bicgstab.hpp>
#include <thrust/device_vector.h>
int main() {
// 使用CUDA后端
typedef amgcl::backend::cuda<double> Backend;
// 矩阵大小
const int n = 1000;
// 在主机上创建矩阵(CSR格式)
std::vector<int> ptr_host(n+1), col_host;
std::vector<double> val_host;
// 填充矩阵(5对角矩阵)
for(int i = 0; i < n; ++i) {
ptr_host[i] = col_host.size();
for(int j = std::max(0, i-2); j < std::min(n, i+3); ++j) {
col_host.push_back(j);
val_host.push_back(i == j ? 10.0 : -1.0);
}
}
ptr_host[n] = col_host.size();
// 将矩阵拷贝到设备
thrust::device_vector<int> ptr = ptr_host;
thrust::device_vector<int> col = col_host;
thrust::device_vector<double> val = val_host;
// 创建AMG求解器
typedef amgcl::make_solver<
amgcl::amg<
Backend,
amgcl::coarsening::smoothed_aggregation,
amgcl::relaxation::ilu0
>,
amgcl::solver::bicgstab<Backend>
> Solver;
// 设置参数
Solver::params prm;
prm.solver.tol = 1e-6;
// 初始化求解器
Solver solve(
amgcl::adapter::csr(
thrust::raw_pointer_cast(ptr.data()),
thrust::raw_pointer_cast(col.data()),
thrust::raw_pointer_cast(val.data()),
n, n
),
prm
);
// 创建右侧向量和初始解(在设备上)
thrust::device_vector<double> rhs(n, 1.0), x(n, 0.0);
// 求解
int iters;
double error;
std::tie(iters, error) = solve(
thrust::raw_pointer_cast(rhs.data()),
thrust::raw_pointer_cast(x.data())
);
std::cout << "Iterations: " << iters << std::endl;
std::cout << "Error: " << error << std::endl;
return 0;
}
选择Backend的考虑因素
问题规模:
- 小规模问题: 内置后端或Eigen后端
- 大规模问题: OpenMP或CUDA后端
硬件环境:
- 多核CPU: OpenMP后端
- NVIDIA GPU: CUDA后端
- 异构系统: VexCL后端
矩阵特性:
- 标量问题: 基本后端
- 块状矩阵: 块状后端
AMGCL的设计允许你通过简单地更改模板参数来切换后端,而无需重写大部分代码,这为性能调优提供了极大的灵活性。