Когда я должен использовать шаблоны выражений C ++ в вычислительной науке, а когда я * не * должен их использовать?

24

Предположим, что я работаю над научным кодом на C ++. В недавнем обсуждении с коллегой было высказано мнение, что шаблоны выражений могут быть очень плохой вещью, потенциально делая программное обеспечение компилируемым только на определенных версиях gcc. Предположительно, эта проблема затронула несколько научных кодов, о чем упоминалось в подзаголовках этой пародии на «Падение» . (Это единственные известные мне примеры, отсюда и ссылка.)

Однако другие люди утверждают, что шаблоны выражений полезны, потому что они могут повысить производительность, как в этой статье в SIAM Journal of Scientific Computing , избегая хранения промежуточных результатов во временных переменных.

Я не очень много знаю о метапрограммировании шаблонов в C ++, но я знаю, что это один из подходов, используемых в автоматическом дифференцировании и интервальной арифметике, и именно так я начал обсуждать шаблоны выражений. Учитывая как потенциальные преимущества в производительности, так и потенциальные недостатки в обслуживании (если это даже правильное слово), когда я должен использовать шаблоны выражений C ++ в вычислительной науке, а когда я должен их избегать?

Джефф Оксберри
источник
Ах, видео слишком смешное. Я не знал, что это существует. Кто это сделал, ты знаешь?
Вольфганг Бангерт
Без понятия; Несколько человек из PETSc прислали мне ссылки в один момент. Я думаю, что разработчик FEniCS сделал это.
Джефф Оксберри
Ссылка на видео не работает, и я умираю от любопытства. Новая ссылка?
Praxeolitic
О, черт, неважно, я вижу, что YouTube пришел на наши видео Гитлера.
Praxeolitic

Ответы:

17

Моя проблема с шаблонами выражений заключается в том, что они являются очень утечкой абстракции. Вы тратите много времени на написание очень сложного кода, чтобы выполнить простую задачу с более приятным синтаксисом. Но если вы хотите изменить алгоритм, вам придется возиться с грязным кодом, и если вы ошибаетесь с типами или синтаксисом, вы получаете совершенно неразборчивые сообщения об ошибках. Если ваше приложение отлично отображается в библиотеке на основе шаблонов выражений, возможно, стоит подумать об этом, но если вы не уверены, я бы порекомендовал просто написать нормальный код. Конечно, высокоуровневый код менее красив, но вы можете просто делать то, что нужно. В качестве преимущества время компиляции и размеры двоичных файлов значительно сократятся, и вам не придется сталкиваться с огромными отклонениями в производительности из-за выбора компилятора и флага компиляции.

Джед браун
источник
Да, я сам видел некоторые длинные сообщения об ошибках, когда мне приходилось переносить код с gcc 2.95 на gcc 4.x, и компилятор выдавал всевозможные ошибки о шаблонах. Мой напарник разрабатывает шаблонную библиотеку для интервальной арифметики в C ++ (добавляя новые функции, отсутствующие в Boost :: Interval, чтобы выполнить больше исследований), и я не хочу, чтобы код стал кошмаром Скомпилировать.
Джефф Оксберри
12

Другие прокомментировали вопрос о том, насколько сложно писать программы ET, а также о сложности понимания сообщений об ошибках. Позвольте мне прокомментировать вопрос о компиляторах: это правда, что некоторое время назад одной из больших проблем был поиск компилятора, который достаточно совместим со стандартом C ++, чтобы все работало и работало переносимо. Как следствие, мы нашли много ошибок - у меня есть 2-300 отчетов об ошибках на мое имя, распределенных по gcc, Intel icc, IBM xlC и pgicc в Portland. Следовательно, скрипт конфигурации deal.II является хранилищем большого количества тестов ошибок компилятора, в основном в области шаблонов, объявлений друзей, пространств имен и т. Д.

Но оказывается, что производители компиляторов действительно сработались: сегодня gcc и icc сегодня проходят все наши тесты, и легко написать код, который переносим между ними. Я бы сказал, что PGI не сильно отстает, но у него есть ряд причуд, которые, похоже, не исчезнут за эти годы. С другой стороны, xlC - это совсем другая история - они исправляют ошибку каждые 6 месяцев, но, несмотря на то, что они годами регистрируют отчеты об ошибках, прогресс продвигается крайне медленно, и xlC никогда не была в состоянии успешно скомпилировать сделку. II.

Все это означает следующее: если вы придерживаетесь двух больших компиляторов, вы можете ожидать, что они просто работают сегодня. Поскольку сегодня большинство компьютеров и операционных систем обычно имеют хотя бы один из них, этого достаточно. Единственная платформа, где все сложнее, - это BlueGene, где системным компилятором обычно является xlC со всеми его ошибками.

Вольфганг Бангерт
источник
Просто из любопытства, вы пытались компилировать с новыми компиляторами xlc в / Q?
Арон Ахмадиа
Я признаю, что отказался от xlC.
Вольфганг Бангерт
5

Я немного поэкспериментировал с ET, когда, как вы упомянули, компиляторы все еще боролись с ними. Я использовал блиц библиотеку для линейной алгебры в своем коде. Тогда проблема заключалась в получении хорошего компилятора, и, поскольку я не идеальный программист на C ++, интерпретировал сообщения об ошибках компилятора. Последний был просто неуправляемым. Компилятор в среднем генерирует около 1000 строк сообщений об ошибках. Я никак не мог быстро найти свою ошибку в программировании.

Вы можете найти более подробную информацию о цифрах веб-странице, (материалы двух семинаров ET).

Но я бы держался подальше от них ....

GertVdE
источник
Сообщения об ошибках компилятора действительно являются одной из моих проблем. С некоторыми шаблонными кодами C ++, которые я компилирую для создания библиотек для своих проектов, компилятор может генерировать сотни строк предупреждающих сообщений. Однако это не мой код, я его не понимаю, и, вообще говоря, он работает, поэтому я оставляю его в покое. Длинные, загадочные сообщения об ошибках не сулят ничего хорошего для отладки.
Джефф Оксберри
4

Проблема уже начинается с термина «шаблоны выражений (ET)». Я не знаю, есть ли точное определение для этого. Но в обычном использовании он как-то сочетает «как вы кодируете выражения линейной алгебры» и «как это вычисляется». Например:

Вы кодируете векторную операцию

v = 2*x + 3*y + 4*z;                    // (1)

И это вычисляется циклом

for (int i=0; i<n; ++i)                 // (2)
    v(i) = 2*x(i) + 3*y(i) + 4*z(i);

На мой взгляд, это две разные вещи, и их необходимо отделить: (1) интерфейс и (2) одна возможная реализация. Я имею в виду, что это обычная практика в программировании. Конечно, (2) может быть хорошей реализацией по умолчанию, но в целом я хочу иметь возможность использовать специализированную, выделенную реализацию. Например, я хочу, чтобы такая функция, как

myGreatVecSum(alpha, x, beta, y, gamma, z, result);    // (3)

мне звонят, когда я кодирую (1). Возможно (3) просто использует цикл как в (2). Но в зависимости от размера вектора другие реализации могут быть более эффективными. В любом случае, некоторые специалисты по высокой производительности могут реализовать и настроить (3) как можно больше. Поэтому, если (1) не может быть сопоставлено с вызовом (3), тогда я скорее избегу синтаксического сахара (1) и сразу же вызову (3).

То, что я описываю, не является чем-то новым. Напротив, это идея BLAS / LPACK:

  • Все критичные к производительности операции в LAPACK выполняются путем вызова функций BLAS.
  • BLAS просто определяет интерфейс для тех выражений линейной алгебры, которые обычно необходимы.
  • Для BLAS существуют различные оптимизированные реализации.

Если область действия BLAS недостаточна (например, она не обеспечивает функцию, подобную (3)), тогда можно расширить область действия BLAS. Таким образом, этот динозавр 60-х и 70-х годов с помощью своего инструмента каменного века реализует чистое и ортогональное разделение интерфейса и реализации. Забавно, что (большинство) числовых библиотек C ++ не достигают такого уровня качества программного обеспечения. Хотя сам язык программирования намного сложнее. Поэтому неудивительно, что BLAS / LAPACK все еще жив и активно развивается.

Так что, по моему мнению, инопланетяне сами по себе не являются злом. Но то, как они обычно используются в числовых библиотеках C ++, принесло им очень плохую репутацию в кругах научных компьютеров.

Майкл Лен
источник
Майкл, я думаю, что тебе не хватает одного из пунктов шаблонов выражения. Ваш пример кода (1) на самом деле не соответствует каким-либо оптимизированным вызовам BLAS. Фактически, даже когда подпрограмма BLAS существует, накладные расходы на вызов функции BLAS делают ее довольно ужасной для небольших векторов и матриц. Сложные библиотеки шаблонов выражений, такие как Blaze и Eigen, могут использовать отложенную оценку выражений, чтобы избежать использования временных значений, но я убежден, что почти все, что связано с предметно-ориентированным языком, сможет превзойти линейную алгебру, свернутую вручную.
Арон Ахмадиа
Нет, я думаю, что вы упускаете суть. Вы должны различать (а) BLAS как спецификацию некоторых часто используемых операций линейной алгебры (б) реализацию BLAS, такую ​​как ATLAS, GotoBLAS и т. Д. Кстати, как это работает в FLENS: по умолчанию выражение типа (1) будет оценивать, вызывая axpy из BLAS три раза. Но без изменения (1) я также мог бы оценить это, как в (2). Поэтому логически происходит следующее: если важна операция, подобная (1), то набор указанных операций BLAS (a) может быть расширен.
Майкл Лен
Таким образом, ключевой момент: обозначения типа 'v = x + y + z' и то, как они в конечном итоге вычисляются окончательно, должны быть разделены. Eigen, MTL, BLITZ, Blaze-Lib полностью терпят неудачу в этом отношении.
Майкл Лен
1
Правильно, но число часто необходимых операций линейной алгебры комбинаторно. Если вы собираетесь использовать такой язык, как C ++, у вас есть выбор: либо реализовать по мере необходимости, используя шаблоны выражений (это подход Eigen / Blaze), разумно комбинируя подблоки и алгоритмы с использованием отложенной оценки, либо реализуя массивный Библиотека всевозможных рутины. Я не поддерживаю ни один из подходов, так как недавняя работа в Numba и Cython показывает, что мы можем получить аналогичную или более высокую производительность, работая на языках сценариев высокого уровня, таких как Python.
Арон Ахмадия
Но опять же, на что я жалуюсь, так это на то, что такие сложные (в смысле сложных, но негибких) библиотеки, как Eigen, тесно связаны между собой механизмом записи и оценки и даже считают, что это хорошо. Если я использую такой инструмент, как Matlab, я просто хочу кодировать вещи и полагать, что Matlab делает все возможное. Если я использую такой язык, как C ++, я хочу контролировать. Поэтому оцените, если существует механизм оценки по умолчанию, но его можно изменить. В противном случае я возвращаюсь и вызываю функции в C ++ напрямую.
Майкл Лен