tensorflow 特征::Tensor和特征::矩阵性能比较

rkue9o1l  于 2023-01-26  发布在  其他
关注(0)|答案(3)|浏览(221)

我想用一个三维特征::Tensor来替换代码中的一系列矩阵。考虑到这一点,我尝试比较Tensor和矩阵的性能。
下面的函数“tensorContractTest”将(n,n,n)秩为3的Tensor与大小为n(n = 500)的秩为1的Tensor进行收缩,该收缩计算n**2个点积,因此就运算次数而言,它等效于两个(n,n)矩阵的乘法(下面的函数“matrixProductTest”)。
当在Visual Studio 2013上运行时,函数“TensorContractTest”的运行速度比“MatrixProductTest”慢40倍。可能是我漏掉了什么。请提供帮助。

#include <unsupported/Eigen/CXX11/Tensor>
using namespace Eigen;

// Contracts 3-dimensional (n x n x n) tensor with 1-dimensional (n) tensor. 
// By the number of operations, it's equivalent to multiplication of 
// two (n, n) matrices (matrixProdTest).
Tensor<double, 2>  tensorContractTest(int n)
{
  Tensor<double, 3> a(n, n, n);     a.setConstant(1.);  
  Tensor<double, 1> b(n);           b.setConstant(1.);
  auto indexPair            = array<IndexPair<int>, 1>{IndexPair<int>(2,0)}; 
  Tensor<double, 2> result  = a.contract(b, indexPair); 
  return result;
}

MatrixXd  matrixProductTest(int n)
{ 
  MatrixXd a = MatrixXd::Ones(n, n), result = a * a;
  return result;
}
cpjpxq1n

cpjpxq1n1#

虽然浮点运算的次数是相同的,但存取模式却完全不同,因此两者的运算根本无法比较,一般而言,矩阵对矩阵的运算总是比较快(就FLOPS而言)比矩阵-向量或向量-向量运算更有效,因为前者可以更好地利用高速缓存,从而使CPU的ALU得到近乎最佳的利用。在您的情况下,一方面,你必须读取一个n^3Tensor和两个n^2矩阵,所以内存占用根本不可比。
在内部,Tensor::contract在可能的情况下回退到Eigen的矩阵乘积内核,因此性能应该成对。

klsxnrf1

klsxnrf12#

因为Tensor收缩和矩阵乘法不一样。
存在矩阵乘法的专用算法,如斯特拉森算法,减少了运算总数。此外,矩阵库多年来已得到高度优化,因此它们通常专用于使用矢量化指令(SIMD或AVX),具体取决于平台(Intel、AMD、ARM)。对于小尺寸或稀疏模式的矩阵,与非专用代码相比,速度增益巨大。
相比之下,Tensor库往往没有那么优化,所以如果你能把你的Tensor数学转换成矩阵代数,速度有很多机会会提高。

4uqofj5v

4uqofj5v3#

我可以用谷歌的基准测试程序重现这种缓慢的速度,正如我测试的那样,如果数据量很小,特征矩阵乘法比特征Tensor收缩更快,但结果是一样的。

所以我不确定后备计划
在内部,Tensor::合同在可能的情况下回退到特征矩阵乘积内核,因此性能应该成对。
@ggael在这里提到。
下面是我的测试代码:

#include <Eigen/Dense>
#include <benchmark/benchmark.h>
#include <unsupported/Eigen/CXX11/Tensor>

constexpr std::size_t I = ;
constexpr std::size_t J = ;
constexpr std::size_t K = ;

static void bmForLoop(benchmark::State &state) {
  // Perform setup here
  Eigen::Matrix<double, I, J> m_0 = Eigen::Matrix<double, I, J>::Random();
  Eigen::Matrix<double, J, K> m_1 = Eigen::Matrix<double, J, K>::Random();
  Eigen::Matrix<double, I, K> m_2;

  for (auto _ : state) {
    // This code gets timed
    m_2 = Eigen::Matrix<double, I, K>::Zero();
    for (std::size_t i = 0; i < I; ++i) {
      for (std::size_t k = 0; k < K; ++k) {
        for (std::size_t j = 0; j < J; ++j) {
          benchmark::DoNotOptimize(m_2.coeffRef(i, k) +=
                                   m_0.coeff(i, j) * m_1.coeff(j, k));
        }
      }
    }
  }
}

static void bmEigenMatrix(benchmark::State &state) {
  // Perform setup here
  Eigen::Matrix<double, I, J> m_0 = Eigen::Matrix<double, I, J>::Random();
  Eigen::Matrix<double, J, K> m_1 = Eigen::Matrix<double, J, K>::Random();
  Eigen::Matrix<double, I, K> m_2;

  for (auto _ : state) {
    // This code gets timed
    benchmark::DoNotOptimize(m_2 = m_0 * m_1);
  }
}

static void bmEigenMatrixDynamic(benchmark::State &state) {
  // Perform setup here
  Eigen::MatrixXd m_0(I, J);
  m_0 = Eigen::Matrix<double, I, J>::Random();
  Eigen::MatrixXd m_1(J, K);
  m_1 = Eigen::Matrix<double, J, K>::Random();
  Eigen::MatrixXd m_2;

  for (auto _ : state) {
    // This code gets timed
    benchmark::DoNotOptimize(m_2 = m_0 * m_1);
  }
}

static void bmEigenTensorFixed(benchmark::State &state) {
  // Perform setup here
  Eigen::Matrix<double, I, J> m_0 = Eigen::Matrix<double, I, J>::Random();
  Eigen::Matrix<double, J, K> m_1 = Eigen::Matrix<double, J, K>::Random();
  Eigen::TensorMap<Eigen::TensorFixedSize<double, Eigen::Sizes<I, J>>> t_0(
      m_0.data(), I, J);
  Eigen::TensorMap<Eigen::TensorFixedSize<double, Eigen::Sizes<J, K>>> t_1(
      m_1.data(), J, K);
  Eigen::TensorFixedSize<double, Eigen::Sizes<I, K>> t_2;

  for (auto _ : state) {
    // This code gets timed
    benchmark::DoNotOptimize(
        t_2 = t_0.contract(
            t_1, Eigen::array<Eigen::IndexPair<Eigen::Index>, 1>{{{1, 0}}}));
  }
}

static void bmEigenTensor(benchmark::State &state) {
  // Perform setup here
  Eigen::Matrix<double, I, J> m_0 = Eigen::Matrix<double, I, J>::Random();
  Eigen::Matrix<double, J, K> m_1 = Eigen::Matrix<double, J, K>::Random();
  Eigen::TensorMap<Eigen::Tensor<double, 2>> t_0(m_0.data(), I, J);
  Eigen::TensorMap<Eigen::Tensor<double, 2>> t_1(m_1.data(), J, K);
  Eigen::Tensor<double, 2> t_2;

  for (auto _ : state) {
    // This code gets timed
    benchmark::DoNotOptimize(
        t_2 = t_0.contract(
            t_1, Eigen::array<Eigen::IndexPair<Eigen::Index>, 1>{{{1, 0}}}));
  }
}

BENCHMARK(bmForLoop);
BENCHMARK(bmEigenMatrix);
BENCHMARK(bmEigenMatrixDynamic);
BENCHMARK(bmEigenTensorFixed);
BENCHMARK(bmEigenTensor);
BENCHMARK_MAIN();

,其中I、J和K

constexpr std::size_t I = ;
constexpr std::size_t J = ;
constexpr std::size_t K = ;

用相同的数字填充,范围从1到8。
我用Bazel编译,编译命令是build --cxxopt=-std=c++17 --cxxopt=-march=native --cxxopt=-O3 --cxxopt=-DNDEBUG,Eigen的版本是3.4.0。

相关问题