Коли я повинен використовувати шаблони виразів C ++ в обчислювальній науці, а коли я повинен * не * використовувати їх?


24

Припустимо, я працюю над науковим кодом на C ++. У недавній дискусії з колегою було стверджено, що шаблони виразів можуть бути дійсно поганою справою, що потенційно робить програмне забезпечення компільованим лише для певних версій gcc. Нібито ця проблема торкнулася декількох наукових кодексів, про які йдеться у підзаголовках цієї пародії на Downfall . (Це єдині мені відомі приклади, звідси посилання.)

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

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


Ах, відео занадто смішне. Я не знав, що воно існує. Хто це зробив, ти знаєш?
Вольфганг Бангерт

Не маю уявлення; пара людей PETSc надіслала мені посилання в один момент. Я думаю, що це зробив розробник FEniCS.
Джефф Оксберрі

Відеопосилання порушено, і я вмираю від цікавості. Нове посилання?
Праксеоліт

О, драт, ніколи не бачу, я бачу, що youtube прийшов для наших відео про Гітлера.
Праксеоліт

Відповіді:


17

Моя проблема з шаблонами виразів полягає в тому, що вони є дуже хиткою абстракцією. Ви витрачаєте багато роботи над написанням дуже складного коду, щоб виконати просте завдання з приємнішим синтаксисом. Але якщо ви хочете змінити алгоритм, вам доведеться возитися з брудним кодом, і якщо ви підкинете типи або синтаксис, ви отримаєте абсолютно нерозбірливі повідомлення про помилки. Якщо ваша програма ідеально відображається в бібліотеці на основі шаблонів виразів, то, можливо, варто подумати, але якщо ви не впевнені, рекомендую просто написати звичайний код. Звичайно, код високого рівня менш гарний, але ви можете просто робити те, що потрібно зробити. Як користь, час компіляції та двійкові розміри зменшуватимуться, і вам не доведеться справлятися з величезною дисперсією продуктивності завдяки вибору прапорця компілятора та прапорця.


Так, я бачив кілька тривалих повідомлень про помилки, коли мені довелося перенести код з gcc 2,95 на gcc 4.x, і компілятор кидав всілякі помилки щодо шаблонів. Моя лабораторія розробляє шаблонову бібліотеку для арифметики інтервалу в C ++ (додаючи нові функції, які не є в Boost :: Інтервал для того, щоб провести більше досліджень), і я не хочу, щоб код став кошмаром складати.
Джефф Оксберрі

12

Інші прокоментували питання про те, як складно писати програми ET, а також складність розуміння повідомлень про помилки. Дозвольте мені прокоментувати проблему компіляторів: Це правда, що на той час однією з головних проблем було пошук компілятора, який достатньо сумісний зі стандартом C ++, щоб все працювало і змушувало його працювати портативно. Як наслідок, ми знайшли багато помилок - у мене на моє ім’я є 2-300 звітів про помилки, які розповсюджуються на gcc, Intel icc, IBM xlC та pgicc Portland. Отже, скрипт конфігурації deal.II - це сховище великої кількості тестів помилок компілятора, насамперед в області шаблонів, оголошень друзів, просторів імен тощо.

Але виявляється, що виробники компіляторів дійсно зібралися разом: сьогодні gcc та icc сьогодні проходять усі наші тести, і легко написати код, який є переносним між ними. Я б сказав, що PGI не відстає, але він має ряд химерностей, які, здається, не зникають протягом багатьох років. З іншого боку, xlC - це зовсім інша історія - вони виправляють помилку кожні 6 місяців, але, незважаючи на подання звітів про помилки протягом багатьох років, прогрес надзвичайно повільний, і xlC ніколи не змогла скласти deal.II успішно.

Все це означає, що це: якщо ви дотримуєтесь двох великих компіляторів, ви можете розраховувати, що вони просто працюють сьогодні. Оскільки сьогодні більшість комп'ютерів і ОС зазвичай мають принаймні один з них, цього достатньо. Єдиною платформою, де все складніше, є BlueGene, де компілятором системи є xlC, з усіма його помилками.


Щойно з цікавості ви спробували компілювати проти нових компіляторів xlc на / Q?
Арон Ахмадія

Ні. Я визнаю, що відмовився від xlC.
Вольфганг Бангерт

5

Я давно експериментував з ET's, коли, як ви вже згадували, компілятори все ще боролися з ними. Я використав бібліотеку бліц для лінійної алгебри в моєму коді. Проблема тоді полягала в тому, щоб отримати хороший компілятор, і оскільки я не досконалий програміст на C ++, інтерпретуючи повідомлення про помилки компілятора. Останнє було просто некерованим. Компілятор в середньому генерує близько 1000 рядків повідомлень про помилки. Ні в якому разі мені не вдалося швидко знайти свою помилку програмування.

Додаткову інформацію можна знайти на веб-сторінці з оонумеріки (там розглядаються роботи двох семінарів з ЕТ).

Але я б залишився далеко від них….


Повідомлення про помилки компілятора - це справді одна з моїх проблем. З деяким шаблоновим кодом 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 ще живий і активно розвинений.

Тож, на мою думку, ПТ самі по собі не є злими. Але те, як їх зазвичай використовують у чисельних бібліотеках С ++, заслужило їм дуже погану репутацію в наукових обчислювальних колах.


Майкл, я думаю, вам не вистачає однієї з точок виразних шаблонів. Ваш приклад коду (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.
Арон Ахмадія

Але знову ж таки, на що я скаржуся, це той факт, що такі складні (у сенсі складні, але негнучкі) бібліотеки, як Ейген, щільно поєднують позначення та механізм оцінювання і навіть вважають, що це добре. Якщо я використовую такий інструмент, як Matlab, я просто хочу кодувати речі і покладатися на те, що Matlab робить найкраще. Якщо я використовую таку мову, як C ++, тоді я хочу контролювати. Тому цінуйте, якщо існує механізм оцінювання за замовчуванням, але його потрібно мати можливість змінити. В іншому випадку я повертаюсь назад і викликую функції в C ++ безпосередньо.
Майкл Лен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.