Eigen Tensor Code ужасно медленный - PullRequest
0 голосов
/ 24 мая 2018

Я новичок в Eigen Tensors, поэтому, вероятно, я делаю что-то ужасно неправильно.У меня есть код, который рассчитывает Z-баллы разницы между двумя матрицами с плавающей точкой.Я обнаружил, что код работает в 500 раз медленнее, чем тот же код в Python и numpy.Что я делаю не так?

Код на C ++

  int scale = atoi(argv[1]);
  Eigen::array<int, 2> bbcast({scale, 1});
  long startTime = get_nanos();
  Eigen::Tensor<float, 2> a(2, 5);
  a.setRandom();
  Eigen::Tensor<float, 2> b(2, 5);
  b.setRandom();
  Eigen::Tensor<float, 2> scaled_a = a.broadcast(bbcast);  
  Eigen::Tensor<float, 2> scaled_b = b.broadcast(bbcast);  

  Eigen::array<int, 1> dims({0 /* dimension to reduce */});
  Eigen::array<int, 2> good_dims{{1,(int)scaled_a.dimension(1)}};
  auto means = (scaled_a - scaled_b).mean(dims).reshape(good_dims);
  std::cout << means << std::endl;
  printf("Calculated means, took %f seconds\n",(float)(get_nanos() - startTime) / 1000000000L);

  Eigen::array<int, 2> bcast({(int)scaled_a.dimension(0), 1});
  auto submean = (scaled_a - scaled_b) - means.broadcast(bcast);
  auto stds = submean.mean(dims).reshape(good_dims).abs().square().mean(dims).reshape(good_dims).sqrt();
  std::cout << stds << std::endl;
  printf("Calculated std, took %f seconds\n",(float)(get_nanos() - startTime) / 1000000000L);

Это работает около 3 секунд на моей виртуальной машине Linux с 20000 на 5 матриц поплавка

Код на Python:

import numpy as np
import time
start = time.time()
a = np.random.rand(2*10000,5)
b = np.random.rand(2*10000,5)
stds = np.std(a - b, axis = 0)
means = np.mean(a - b, axis = 0)
#diffs = np.sum(np.abs(net_out - correct_out)/stds,axis=1)
diffs = np.abs(a - b - means)/stds
print(diffs)
print("Took", time.time() - start )

На той же виртуальной машине она работает 0,0068 секунды.

Большое спасибо, Моше

1 Ответ

0 голосов
/ 24 мая 2018

Для 2D-тензоров лучше использовать Matrix или Array, это приведет к более простому коду:

ArrayXXd a = ArrayXXd::Random(2*10000,5);
ArrayXXd b = ArrayXXd::Random(2*10000,5);
auto means = (a-b).colwise().mean().eval();
auto stds = (((a-b).rowwise()-means).square().colwise().sum() / (a.rows()-1)).sqrt().eval();
ArrayXXd diffs = abs((a-b).rowwise() - means).rowwise()/stds;

Обратите внимание на .eval() для строк, использующих auto, см. почему .

Этот код занимает 0.000324919s при компиляции с gcc и -O3 на среднем ноутбуке (без учета генерации случайных чисел, которая, вероятно, намного дороже, но не репрезентативна).

Вот версия Tensor, с которой я столкнулся, снова обратите внимание на вызовы eval():

int n = a.dimension(0);
Eigen::array<int, 1> dims({0 /* dimension to reduce */});
Eigen::array<int, 2> good_dims{{1,(int)a.dimension(1)}};
Eigen::array<int,2> bc({n,1});

auto means = (a - b).mean(dims).eval();
auto submean = (a - b) - means.reshape(good_dims).broadcast(bc);
auto stds = (submean.square().eval().sum(dims) * 1.f/(float(n-1))).sqrt().eval();
diffs = submean.abs() / stds.reshape(good_dims).broadcast(bc);

, но, похоже, она медленнее, около 0,007 с здесь.Чтобы просмотреть Tensor как Array, вы можете использовать Map:

Map<const ArrayXXf> a(tensor_a.data(), tensor_a.dimension(0), tensor_a.dimension(1));
...