Почему valarray работает медленно в Visual Studio 2015? - PullRequest
16 голосов
/ 09 мая 2019

Чтобы ускорить вычисления в моей библиотеке, я решил использовать класс std::valarray.Документация гласит:

Классы std :: valarray и helper определены как свободные от определенных форм псевдонимов, что позволяет оптимизировать операции над этими классами аналогично эффекту.ключевого слова restrict в языке программирования C.Кроме того, функциям и операторам, принимающим аргументы valarray, разрешено возвращать прокси-объекты, чтобы компилятор мог оптимизировать выражение, например v1 = a * v2 + v3;как один цикл, который выполняет v1 [i] = a * v2 [i] + v3 [i];избегая любых временных или многократных проходов.

Это именно то, что мне нужно.И это работает, как описано в документации, когда я использую компилятор g ++.Я разработал простой пример для проверки производительности std::valarray:

void check(std::valarray<float>& a)
{
   for (int i = 0; i < a.size(); i++)
      if (a[i] != 7)
         std::cout << "Error" << std::endl;
}

int main()
{
   const int N = 100000000;
   std::valarray<float> a(1, N);
   std::valarray<float> c(2, N);
   std::valarray<float> b(3, N);
   std::valarray<float> d(N);

   auto start = std::chrono::system_clock::now();
   d = a + b * c;
   auto end = std::chrono::system_clock::now();

   std::cout << "Valarr optimized case: "
      << (end - start).count() << std::endl;

   check(d);

   // Optimal single loop case
   start = std::chrono::system_clock::now();
   for (int i = 0; i < N; i++)
      d[i] = a[i] + b[i] * c[i];
   end = std::chrono::system_clock::now();
   std::cout << "Optimal case: " << (end - start).count() << std::endl;

   check(d);
   return 0;
}

На g ++ я получил:

Valarr optimized case: 1484215
Optimal case: 1472202

Кажется, что все операции d = a + b * c; действительно помещены в однуцикл, который упрощает код при сохранении производительности.Однако это не работает, когда я использую Visual Studio 2015. Для того же кода я получаю:

Valarr optimized case: 6652402
Optimal case: 1766699

Разница почти в четыре раза;нет оптимизации!Почему std::valarray не работает должным образом в Visual Studio 2015?Я все делаю правильно?Как я могу решить проблему, не отказываясь от std::valarray?

1 Ответ

20 голосов
/ 09 мая 2019

Я все делаю правильно?

Вы все делаете правильно.Проблема в реализации Visual Studio std::valarray.

Почему std::valarray не работает должным образом в Visual Studio 2015?

Просто откройте реализацию любогоvalarray оператор, например operator+.Вы увидите что-то вроде (после расширения макроса):

   template<class _Ty> inline
      valarray<_Ty> operator+(const valarray<_Ty>& _Left,
         const valarray<_Ty>& _Right)
   {
      valarray<TYPE> _Ans(_Left.size());
      for (size_t _Idx = 0; _Idx < _Ans.size(); ++_Idx)
         _Ans[_Idx] = _Left[_Idx] + _Right[_Idx];
      return (_Ans)
   }

Как видите, создается новый объект, в который копируется результат операции.Там действительно нет оптимизации.Я не знаю почему, но это факт.Похоже, что в Visual Studio std::valarray был добавлен только для совместимости.

Для сравнения рассмотрим реализацию GNU .Как видите, каждый оператор возвращает класс шаблона _Expr , который содержит только операцию , но не содержит данных.Реальные вычисления выполняются в операторе присваивания и, более конкретно, в функции __ valarray_copy .Таким образом, пока вы не выполните назначение, все действия выполняются с прокси-объектом _Expr.Только когда вызывается operator=, операция, сохраненная в _Expr, выполняется в одном цикле.Вот почему вы получаете такие хорошие результаты с g ++.

Как мне решить проблему?

Вам нужно найти подходящую реализацию std::valarray наИнтернет или вы можете написать свой собственный.Вы можете использовать реализацию GNU в качестве примера.

...