Вартість обслуговування бази програмного коду SIMD


14

Питання:

Консенсус індустрії програмного забезпечення полягає в тому, що чистий і простий код є основоположним для довгострокової життєздатності кодової бази та організації, яка їй належить. Ці властивості призводять до зниження витрат на обслуговування та збільшення ймовірності продовження бази коду.

Однак код SIMD відрізняється від загального коду програми, і я хотів би знати, чи існує аналогічна консенсус щодо чистого та простого коду, що застосовується спеціально до коду SIMD.


Передумови мого запитання.

Я пишу багато SIMD (однонавчальний, кілька даних) код для різних завдань обробки та аналізу зображень. Нещодавно мені також довелося перенести невелику кількість цих функцій від однієї архітектури (SSE2) до іншої (ARM NEON).

Код написаний для скороченого програмного забезпечення, тому він не може залежати від власних мов без необмежених прав на перерозподіл, таких як MATLAB.

Приклад типової структури коду:

  • Використання типу матриці OpenCV ( Mat) для управління всією пам'яттю, буфером та життям.
  • Після перевірки розміру (розмірів) вхідних аргументів приймаються покажчики на стартову адресу кожного ряду пікселів.
  • Кількість пікселів та початкові адреси кожного ряду пікселів з кожної вхідної матриці передаються в деякі функції C ++ низького рівня.
  • Ці низькорівневі функції C ++ використовують вбудовані SIMD (для архітектури Intel та ARM NEON ), завантажуючи з них і зберігаючи їх до сирої адреси вказівника.
  • Характеристики цих низькорівневих функцій C ++:
    • Виключно одновимірний (послідовний в пам'яті)
    • Не займається розподілом пам'яті.
      (Кожен розподіл, включаючи тимчасові, обробляється зовнішнім кодом за допомогою засобів OpenCV.)
    • Діапазон довжин імен символів (внутрішні символи, назви змінних тощо) приблизно становить 10 - 20 символів, що є досить надмірним.
      (Читає, як техно-бабл.)
    • Повторне використання змінних SIMD не рекомендується використовувати, оскільки компілятори досить помилкові в правильному аналізі коду, який не записаний у стилі кодування "єдине призначення".
      (Я подав кілька звітів про помилки компілятора.)

Які аспекти програмування SIMD можуть спричинити різницю дискусії від загальної справи? Або чому SIMD відрізняється?

З точки зору початкової вартості розробки

  • Загальновідомо, що початкова вартість розробки SIM-коду C ++ з хорошою продуктивністю становить приблизно 10x - 100x (з широким відривом) порівняно з випадково написаним кодом C ++.
  • Як зазначено у відповідях на вибір між продуктивністю та читанням / чистішим кодом? , більшість кодів (у тому числі випадково написаний код та код SIMD) спочатку не є чистим та швидким .
  • Еволюційні покращення продуктивності коду (як скалярного, так і SIMD-коду) не перешкоджають (оскільки це сприймається як певна переробка програмного забезпечення ), а вартість та вигода не відстежуються.

З точки зору схильності
(наприклад, принцип Парето, відомий також як правило 80-20 )

  • Навіть якщо обробка зображень містить лише 20% програмної системи (як за розміром коду, так і за функціональністю), обробка зображень є порівняно повільною (якщо розглядати її як відсоток витраченого часу процесора), займаючи більше 80% часу.
    • Це пов’язано з ефектом розміру даних: типовий розмір зображення вимірюється в мегабайтах, тоді як типовий розмір даних, що не мають зображення, вимірюється в кілобайтах.
  • У коді для обробки зображень програміст SIMD навчається автоматично розпізнавати 20% -ний код, що містить точки доступу, шляхом ідентифікації структури циклу в коді C ++. Таким чином, з точки зору програміста SIMD, 100% "важливого коду" - це вузьке місце.
  • Часто в системі обробки зображень існує кілька точок доступу і займають порівнянні пропорції часу. Наприклад, може бути 5 гарячих точок, кожне з яких займає (20%, 18%, 16%, 14%, 12%) загального часу. Щоб досягти високого підвищення продуктивності, усі точки доступу повинні бути переписані на SIMD.
    • Це узагальнено як правило, що спливає на повітряній кулі: повітряна куля не може вискакуватися двічі.
    • Припустимо, є кілька куль, скажімо, 5 з них. Єдиний спосіб їх порізати - це попу їх по черзі.
    • Після того, як перший повітряний куля спливе, решта 4 повітряних кульок тепер складає більший відсоток від загального часу виконання.
    • Щоб досягти подальших здобутків, потрібно випустити іншу кулю.
      (Це суперечить правилу оптимізації 80-20: хорошого економічного результату можна досягти після того, як буде зібрано 20% плодів, що висіли найменше.)

З точки зору читабельності та обслуговування

  • SIMD-код очевидно важко читати.

    • Це справедливо навіть у тому випадку, якщо слід дотримуватися будь-якої найкращої практики інженерії програмного забезпечення, наприклад, іменування, інкапсуляція, правильність const (та надання побічних ефектів очевидним), розкладання функції тощо.
    • Це справедливо навіть для досвідчених програмістів SIMD.
  • Оптимальний код SIMD дуже викривлений (див. Зауваження) порівняно з його еквівалентним кодом прототипу C ++.

    • Існує багато способів підкреслити код SIMD, але лише 1 з 10 таких спроб досягає прийнятно швидких результатів.
    • (Тобто, в мелодії 4-х-10-кратного підвищення продуктивності, щоб виправдати високу вартість розвитку. На практиці спостерігаються ще більші досягнення).

(Зауваження)
Це головна теза проекту MIT Halide - цитуючи дослівну назву статті:

"алгоритми роз'єднання від графіків для легкої оптимізації трубопроводів обробки зображень"

З точки зору застосовності вперед

  • SIMD-код суворо прив’язаний до єдиної архітектури. Кожна нова архітектура (або кожне розширення регістрів SIMD) потребує перезапису.
  • На відміну від більшості програмного забезпечення, кожен фрагмент коду SIMD, як правило, пишеться з єдиною метою, яка ніколи не змінюється.
    (За винятком перенесення до інших архітектур.)
  • Деякі архітектури підтримують ідеальну зворотну сумісність (Intel); деякі не вистачають на тривіальну суму (ARM AArch64, замінюючи vtblна vtblq), але цього достатньо, щоб якийсь код не вдався зібрати.

З точки зору навичок та підготовки

  • Незрозуміло, які передумови знань потрібні, щоб правильно навчити нового програміста писати та підтримувати SIMD-код.
  • Випускники коледжів, які вивчили програмування SIMD у школі, схоже, зневажають і відкидають це як непрактичний шлях кар’єри.
  • Розбирання читання та профілювання низької продуктивності цитуються як дві основні навички написання високоефективного коду SIMD. Однак незрозуміло, як систематично навчати програмістів за цими двома навичками.
  • Сучасна архітектура процесора (яка значно відрізняється від того, що викладається в підручниках) робить навчання ще складнішим.

З точки зору правильності та витрат, пов'язаних з дефектами

  • Функція обробки однієї SIMD фактично є згуртованою, щоб можна було встановити правильність шляхом:
    • Застосування формальних методів (з ручкою та папером) та
    • Перевірка цілих діапазонів виводу (з кодом прототипу та виконується поза часом виконання) .
  • Процес перевірки, однак, дуже затратний (витрачає 100% часу на перегляд коду і 100% часу на перевірку моделі прототипу), що втричі збільшує вже дорогу вартість розробки SIMD-коду.
  • Якщо помилку якимось чином вдасться проскочити цей процес перевірки, "неможливо" відновити (виправити) хіба що замінити (переписати) підозрювану несправну функцію.
  • Код SIMD страждає від тупості дефектів компілятора C ++ (оптимізація генератора коду).
    • SIMD-код, згенерований за допомогою шаблонів виразів C ++, також сильно страждає від дефектів компілятора.

З точки зору руйнівних нововведень

  • Багато рішень було запропоновано в наукових колах, але мало хто бачить широке комерційне використання.

    • MIT Halide
    • Стенфорд Темний зал
    • NT2 (Numerical Template Toolbox) і пов'язаний Boost.SIMD
  • Бібліотеки з широким комерційним використанням, здається, не мають сильної SIMD-функції.

    • Бібліотеки з відкритим кодом здаються теплими для SIMD.
      • Нещодавно я про це спостерігаю з перших рук після профілювання великої кількості функцій API OpenCV, починаючи з версії 2.4.9.
      • Багато інших бібліотек обробки зображень, які я профілював, також не використовують сильно SIMD, або вони пропускають справжні точки доступу.
    • Комерційні бібліотеки взагалі уникають SIMD.
      • У кількох випадках я навіть бачив, що бібліотеки обробки зображень повертають оптимізований SIMD-код у попередній версії до не-SIMD-коду в більш пізній версії, в результаті чого сильні регресії продуктивності.
        (Відповідь продавця полягає в тому, що потрібно уникати помилок компілятора.)

Питання цього програміста: чи інколи код з низькою затримкою повинен бути "некрасивим"? це пов'язано, і я раніше писав відповідь на це питання, щоб пояснити свої точки зору кілька років тому.

Однак ця відповідь в значній мірі "умиротворення" до точки зору "передчасної оптимізації", тобто до точки зору, яка:

  • Усі оптимізації за визначенням є передчасними (або короткостроковими за своєю природою ) та
  • Єдина оптимізація, яка має довгострокову користь, - це простота.

Але такі точки зору оскаржуються в цій статті АСМ .


Все це змушує мене запитати:
SIMD-код відрізняється від загального коду програми, і я хотів би знати, чи існує аналогічний галузевий консенсус щодо значення чистого та простого коду для коду SIMD.


2
Чи є вимоги до продуктивності? Чи можете ви задовольнити свої вимоги до продуктивності, не використовуючи SIMD? Якщо ні, то питання суперечить.
Чарльз Е. Грант

4
Це питання занадто довго для запитання, швидше за все, тому, що його хороший фрагмент - це спроба відповісти на запитання, і довго навіть відповісти (почасти тому, що це стосується набагато більше аспектів, ніж більшість розумних відповідей).

3
Мені подобається мати чистий / простий / повільний код (для початкового підтвердження концепції та подальших цілей документації) на додаток до оптимізованої альтернативи. Це полегшує розуміння (оскільки люди можуть просто прочитати чистий / простий / повільний код) і легко перевірити (порівнявши оптимізовану версію з чистою / простою / повільною версією вручну та в одиничних тестах)
Брендан

2
@Brendan Я був у подібному проекті і використовував тестовий підхід з простим / повільним кодом. Хоча це варіант, який варто розглянути, він також має обмеження. По-перше, різниця в продуктивності може виявитися непомітною: тести з використанням неоптимізованого коду можуть працювати протягом декількох годин ... днів. По- друге, для обробки зображення може виявитися, що біт за бітом порівняння просто не буде працювати, коли оптимізований код виробляє трохи різні результати - так , що можна було б використовувати більш складні порівняння, як еф середньоквадратичне дифф
комара

2
Я голосую, щоб закрити це питання поза темою, оскільки це не концептуальна проблема програмування, як описано в довідковому центрі .
durron597

Відповіді:


6

Я не писав багато SIMD-коду для себе, але багато коду асемблера кілька десятиліть тому. AFAIK, що використовує SIMD-intrinsics, по суті, є програмою асемблеру, і все ваше питання можна перефразувати лише замінивши "SIMD" на слово "Assembly". Наприклад, бали, які ви вже згадали, подобаються

  • розробка коду займає від 10 до 100 разів, ніж "код високого рівня"

  • вона прив’язана до конкретної архітектури

  • код ніколи не є "чистим" і не є легким для рефактора

  • вам потрібні експерти для написання та підтримки

  • налагодження та підтримка важко, еволюціонує дуже важко

жодним чином не є "спеціальними" для SIMD - ці пункти справедливі для будь-якої мови асемблери, і всі вони "галузевий консенсус". І висновок у галузі програмного забезпечення також такий же, як і для асемблера:

  • не пишіть його, якщо не потрібно - використовуйте мову високого рівня, де це можливо, і дозвольте компіляторам робити важку роботу

  • якщо компіляторів недостатньо, принаймні інкапсулюйте частини "низького рівня" в деяких бібліотеках, але уникайте поширення коду по всій програмі

  • оскільки написати асемблер "самодокументування" або код SIMD майже неможливо, спробуйте збалансувати це за великою кількістю документації.

Звичайно, справді є різниця в ситуації з "класичною" збіркою або машинним кодом: сьогодні сучасні компілятори зазвичай виробляють високоякісний машинний код з мови високого рівня, який часто краще оптимізується, ніж код асемблера, написаний вручну. Для популярних сьогодні архітектур SIMD якість доступних компіляторів є AFAIK значно нижчою за це - і, можливо, цього ніколи не досягти, оскільки автоматична векторизація все ще є темою наукових досліджень. Дивіться, наприклад, цю статтю яка описує відмінності оптимізації між компілятором і людиною, даючи поняття, що створити хороші компілятори SIMD може бути дуже важко.

Як ви вже описували у своєму запитанні, існує також проблема якості з сучасними сучасними бібліотеками. Тож IMHO найкраще, на що ми можемо сподіватися, це те, що в наступні роки якість компіляторів і бібліотек підвищиться, можливо, апаратне забезпечення SIMD доведеться змінити, щоб стати більш "зручним для компіляторів", можливо, спеціалізованими мовами програмування, що підтримують більш просту векторизацію (наприклад, Halide, який Ви вже згадували двічі) стануть популярнішими (чи не це вже було силою Фортран?). Згідно з Вікіпедією , SIMD став "масовим продуктом" приблизно 15-20 років тому (а Галіде менше 3 років, коли я правильно трактую документи). Порівняйте це з компіляторами часу для "класичної" мови складання, необхідної для зрілості. Відповідно до цієї статті у Вікіпедіїпройшло майже 30 років (від ~ 1970 до кінця 1990-х), поки компілятори не перевищили показники роботи людських експертів (у виробництві непаралельного машинного коду). Тож нам, можливо, доведеться чекати ще 10–15 років, поки те ж не станеться з компіляторами, що підтримують SIMD.


в моєму читанні статті Вікіпедії , там , здається, загальний промисловості консенсусу , що код , оптимізований на низькому рівні «вважається важко використовувати з - за численних технічних деталей , які необхідно пам'ятати»
комар

@gnat: так, абсолютно, але я думаю, що якщо я додам це до своєї відповіді, я мав би ще десяток речей, про які вже згадував ОП іншими словами у своєму занадто довгому питанні.
Док Браун

погодьтеся, аналіз у вашій відповіді виглядає досить добре, як і є, додавши, що посилання несе ризик "перевантажити" це
gnat

4

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

Ми «вирішили» (а може «вирішили») проблему, написавши власний компілятор. Це не настільки шалено, як це здається спочатку. У нього обмежений набір входів. Ми знаємо, що весь код працює на зображеннях, в основному на зображеннях RGBA. Ми встановлюємо деякі обмеження, наприклад, буфери вводу та виводу ніколи не можуть перетинатися, тому немає згладжування вказівника. Такі речі.

Потім ми пишемо наш код у OpenGL Shading Language (glsl). Він компілюється в скалярний код, SSE, SSE2, SSE3, AVX, Neon і, звичайно, фактичний glsl. Коли нам потрібно підтримувати нову платформу, ми оновлюємо компілятор для виведення коду для цієї платформи.

Ми також робимо плитку зображень, щоб поліпшити когерентність кешу та подібні речі. Але, зберігаючи обробку зображення невеликим ядром і використовуючи glsl (який навіть не підтримує покажчики), ми значно зменшуємо складність компілювання коду.

Цей підхід не для всіх, і у нього є свої проблеми (вам потрібно забезпечити правильність компілятора, наприклад). Але це у нас вийшло досить добре.


Це звучить 🔥🔥! Це товар, який ви продаєте, чи доступний для самостійного продажу? (Також "AVC" = AVX?)
Ахмед Фасіх

Вибачте, так, я мав на увазі AVX (я це виправлю.). Зараз ми не продаємо компілятор як самостійний продукт, хоча це може статися в майбутньому.
користувач1118321

Не жартуйте, це звучить справді акуратно. Найближче, що я бачив так, - це те, як компілятор CUDA раніше міг робити "послідовні" програми, які працюють на процесорі для налагодження - ми сподівалися, що це узагальнить спосіб написання багатопотокового та SIMD-коду CPU, але на жаль Наступне найближче, що я можу придумати, - це OpenCL - чи оцінювали ви OpenCL, і чи вважаєте ви її поступальною вашому компілятору GLSL для всіх?
Ахмед Фасіх

1
Ну, OpenCL не існував, коли ми починали, я не думаю. (Або, якщо це було, це було досить новим.) Тож воно насправді не ввійшло в рівняння.
користувач1118321

0

Схоже, це не надто велике накладне обслуговування, якщо ви плануєте використовувати мову вищого рівня:

Vector<float> values = GetValues();
Vector<float> increment = GetIncrement();

// Perform addition as a vector operation:
List<float> result = (values + increment).ToList();

проти

List<float> values = GetValues();
List<float> increment = GetIncrement();

// Perform addition as a monadic sequence operation:
List<float> result = values.Zip(increment, (v, i) => v + i).ToList();

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

http://blogs.msdn.com/b/dotnet/archive/2014/04/07/the-jit-finally-proposed-jit-and-simd-are-getting-married.aspx

http://blogs.msdn.com/b/dotnet/archive/2014/05/13/update-to-simd-support.aspx


на мій прочитання, можливість використання зовнішніх бібліотек вже досліджена та звернена до запитання: "Бібліотеки з широким комерційним використанням не здаються сильно включеними SIMD ..."
gnat

@gnat Я фактично прочитав цілий абзац, а не лише пункти кулі верхнього рівня, а на плакаті не згадуються жодні бібліотеки SIMD загального призначення, лише комп'ютерне бачення та обробка зображень. Не кажучи вже про те, що аналіз програм мов вищого рівня повністю відсутній, незважаючи на тег C ++ і відсутність специфіки C ++, відображеної в заголовку питання. Це приводить мене до думки, що, хоча моє питання не вважатиметься основним, воно, ймовірно, додасть цінності, даючи людям знати про інші варіанти.
День

1
Наскільки я розумію, ОП запитує, чи існують рішення з широким комерційним використанням. Хоча я ціную ваш натяк (можливо, я можу використати ліб для проекту тут), але те, що я бачу, RyuJIT - це далеко не «широкий загальноприйнятий галузевий стандарт».
Док Браун

@DocBrown, можливо, але його фактичне питання сформульовано як більш загальне: "... галузевий консенсус щодо значення чистого та простого коду для SIMD-коду ...". Я сумніваюся, що взагалі існує якийсь офіційний консенсус, але я стверджую, що мови вищого рівня можуть зменшити різницю між "звичайним" та SIMD-кодом, як і C ++, давайте забудемо про збірку, тим самим зменшивши витрати на обслуговування.
День

-1

Раніше я робив програмування монтажу, а не програмування SIMD останнім часом.

Чи плануєте ви використовувати компілятор, відомий SIMD, як Intel? Чи цікавий посібник з векторизації за допомогою компіляторів Intel® C ++ ?

Деякі з ваших коментарів, як-от "спливає повітряна куля", пропонують використовувати компілятор (щоб отримати переваги протягом усього, якщо у вас немає жодної гарячої точки).


за моїм читанням цей підхід випробував Аскер, див. згадки про помилки / дефекти компілятора у запитанні
gnat

ОП не сказала, чи пробували вони компілятор Intel , що також є предметом цієї теми Programmers.SE . Більшість людей не пробували цього. Це не для всіх; але це може відповідати бізнесу / питання ОП (кращі показники для зниження кодування / проектування / обслуговування).
ChrisW

добре , що я прочитав в цьому питанні передбачає , що запитувач знає про компіляторів Intel і інших архітектур: «Деякі архітектури підтримують ідеальну зворотну сумісність (Intel), а деякі не дотягують ...»
комара

"Intel" в цьому реченні означає дизайнер Intel-the-chip, а не Intel-компілятор-автор.
ChrisW
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.