用户定义的 Eigen 函数使用的内存是预期的两倍
User defined Eigen function using twice as much memory as expected
我定义了以下函数(MWE)
(注意公式是对这个公式的改编:How to Build a Distance Matrix without a Loop (Vectorization)?, as well as http://nonconditional.com/2014/04/on-the-trick-for-computing-the-squared-euclidian-distances-between-two-sets-of-vectors/)
#include <stdlib.h>
#include <chrono>
#include <Eigen/Dense>
#include <iostream>
using MyMatrix = Eigen::MatrixXd;
using MyMatrix1D = Eigen::VectorXd;
//Calculates e^(scale * ||x-y||_2^2), where ||x-y|| is euclidean distatnce
MyMatrix get_kernel_matrix(const Eigen::Ref<const MyMatrix> x, const Eigen::Ref<const MyMatrix> y)
{
const double scale = 0.017;
const MyMatrix1D XX = x.array().square().rowwise().sum().matrix();
const MyMatrix1D YY = y.array().square().rowwise().sum().matrix();
return (((((-2*x)*y.transpose()).colwise() + XX).rowwise() + YY.transpose()).array() * scale).exp().matrix();
}
int main(int argc, char** argv) {
const int num_x = 2500;
const int num_y = 2500;
const MyMatrix X = MyMatrix::Random(num_x, 2);
const MyMatrix Y = MyMatrix::Random(num_y, 2);
const auto t_b_gen = std::chrono::high_resolution_clock::now();
const MyMatrix k_xp_x(std::move(get_kernel_matrix(X, Y)));
const auto t_a_gen = std::chrono::high_resolution_clock::now();
long t_gen = std::chrono::duration_cast<std::chrono::nanoseconds>(t_a_gen - t_b_gen).count();
std::cout << "Time: " << t_gen << std::endl;
}
哪一个会占用 2500*2500*8bytes = 50MB 内存。但是,运行 /usr/bin/time -v kern_double
报告:Maximum resident set size (kbytes): 103288
.
运行 通过 Massif 的程序表明 50MB 块被分配了两次,一次是在 functin 调用中,一次是 Eigen::internal::cal_dense_assignment。我尝试使用和不使用 std::move
来强制复制省略,但是我无法减少内存占用。
我哪里做错了,我该如何解决这个问题以仅使用所需的内存而不是双倍的内存?
这是因为默认情况下,矩阵乘积 x*y.transpose()
在临时值内进行计算以提高效率。您仍然可以通过按如下方式拆分最后一个表达式来重用此临时文件:
MyMatrix tmp = -2*x*y.transpose();
tmp = ((((tmp).colwise() + XX).rowwise() + YY.transpose()).array() * scale).exp();
return tmp;
请注意,这里既不需要 .matrix()
,也不需要 std::move
。
我定义了以下函数(MWE)
(注意公式是对这个公式的改编:How to Build a Distance Matrix without a Loop (Vectorization)?, as well as http://nonconditional.com/2014/04/on-the-trick-for-computing-the-squared-euclidian-distances-between-two-sets-of-vectors/)
#include <stdlib.h>
#include <chrono>
#include <Eigen/Dense>
#include <iostream>
using MyMatrix = Eigen::MatrixXd;
using MyMatrix1D = Eigen::VectorXd;
//Calculates e^(scale * ||x-y||_2^2), where ||x-y|| is euclidean distatnce
MyMatrix get_kernel_matrix(const Eigen::Ref<const MyMatrix> x, const Eigen::Ref<const MyMatrix> y)
{
const double scale = 0.017;
const MyMatrix1D XX = x.array().square().rowwise().sum().matrix();
const MyMatrix1D YY = y.array().square().rowwise().sum().matrix();
return (((((-2*x)*y.transpose()).colwise() + XX).rowwise() + YY.transpose()).array() * scale).exp().matrix();
}
int main(int argc, char** argv) {
const int num_x = 2500;
const int num_y = 2500;
const MyMatrix X = MyMatrix::Random(num_x, 2);
const MyMatrix Y = MyMatrix::Random(num_y, 2);
const auto t_b_gen = std::chrono::high_resolution_clock::now();
const MyMatrix k_xp_x(std::move(get_kernel_matrix(X, Y)));
const auto t_a_gen = std::chrono::high_resolution_clock::now();
long t_gen = std::chrono::duration_cast<std::chrono::nanoseconds>(t_a_gen - t_b_gen).count();
std::cout << "Time: " << t_gen << std::endl;
}
哪一个会占用 2500*2500*8bytes = 50MB 内存。但是,运行 /usr/bin/time -v kern_double
报告:Maximum resident set size (kbytes): 103288
.
运行 通过 Massif 的程序表明 50MB 块被分配了两次,一次是在 functin 调用中,一次是 Eigen::internal::cal_dense_assignment。我尝试使用和不使用 std::move
来强制复制省略,但是我无法减少内存占用。
我哪里做错了,我该如何解决这个问题以仅使用所需的内存而不是双倍的内存?
这是因为默认情况下,矩阵乘积 x*y.transpose()
在临时值内进行计算以提高效率。您仍然可以通过按如下方式拆分最后一个表达式来重用此临时文件:
MyMatrix tmp = -2*x*y.transpose();
tmp = ((((tmp).colwise() + XX).rowwise() + YY.transpose()).array() * scale).exp();
return tmp;
请注意,这里既不需要 .matrix()
,也不需要 std::move
。