Питання:
Консенсус індустрії програмного забезпечення полягає в тому, що чистий і простий код є основоположним для довгострокової життєздатності кодової бази та організації, яка їй належить. Ці властивості призводять до зниження витрат на обслуговування та збільшення ймовірності продовження бази коду.
Однак код 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-коду в більш пізній версії, в результаті чого сильні регресії продуктивності.
- Бібліотеки з відкритим кодом здаються теплими для SIMD.
Питання цього програміста: чи інколи код з низькою затримкою повинен бути "некрасивим"? це пов'язано, і я раніше писав відповідь на це питання, щоб пояснити свої точки зору кілька років тому.
Однак ця відповідь в значній мірі "умиротворення" до точки зору "передчасної оптимізації", тобто до точки зору, яка:
- Усі оптимізації за визначенням є передчасними (або короткостроковими за своєю природою ) та
- Єдина оптимізація, яка має довгострокову користь, - це простота.
Але такі точки зору оскаржуються в цій статті АСМ .
Все це змушує мене запитати:
SIMD-код відрізняється від загального коду програми, і я хотів би знати, чи існує аналогічний галузевий консенсус щодо значення чистого та простого коду для коду SIMD.