SIMD інструкцій у JS. Що, де, коли і навіщо?

Уявіть собі майбутнє, коли важкі математичні пакети будуть написані на php, при цьому не будуть поступатися по продуктивності нативним. Гарні динамічні ігри прямо в браузері, при цьому тримають стабільні 60 fps, складна арифметика, сайти на реакте, зрештою, перестануть гальмувати. Щоб це стало можливим, мови доводиться динамічно розвиватися і включати в себе досить несподівані речі, як нещодавно гучний web-assembly, asm.js, typed arrays, так і одна технологія, про яку піде мова в цій статті.
ES2017 обіцяє багато цікавого, але більшість з цього мають позначку draft, щодня придумують щось нове і відмовляються від чогось старого. Однак, схоже, одна експериментальна специфікація все таки доросте до стандарту і дозволить робити швидкі математичні розрахунки на js. Зустрічайте — SIMD — single instruction multi data. Кому цікаво що це таке, як воно себе веде і що це технологія обіцяє — ласкаво просимо під кат!
Теорія
Припустимо, ви пишете програму на C, і у вас в коді є щось типу:
int32 a = 5;
int32 b = 6;
int32 c = a + b;

Якщо ви запустите цей код на 32-бітної архітектури — все добре, цілочисельна арифметика, що збігається по розрядності з розрядністю системи. Однак якщо запустити цей код на 64-розрядній архітектурі, 32 старших біта будуть заповнені нулями і ганятися даремно, коли можна було б умістити туди ще одне 32-бітове число і скласти дві пари чисел за раз. Приблизно так подумали багато років тому (у 70-х роках) десь у надрах Texas Instruments і CDC, зробивши перші векторні супер-комп'ютери. Однак до них якийсь Майкл Флінн запропонував свою таксономію (класифікацію) комп'ютерів, однією з яких були SIMD. На жаль, на цьому нитка історії втрачається, але ми тут не для цього зібралися.
Таким чином, у 70-х роках минулого сторіччя з'явилися перші процесори, що дозволяли за раз вважати кілька чисел меншої розрядності, ніж та, якою оперує машина. Пізніше це перетягнули до себе практично все у вигляді розширеного набору інструкцій.
Графічно класична архітектура виглядає наступним чином:
image
Нам потрібно скласти 4 пари чисел, тому ми змушені 4 рази викликати інструкцію add в процесорі.
Векторна операція виглядає наступним чином:
image
Коли нам потрібно скласти 4 пари чисел, ми просто викликаємо одну інструкцію, яка складає їх за один такт.
Невелика ремарка з приводу "векторна операція" і SIMD-операція. Справа в тому, що SIMD — більш загальне поняття, що припускає під собою виконання в один і той же момент часу одного або декількох однакових операцій над різними даними. У CUDA в кожен момент часу нитки виконують одну і ту ж операцію над різними даними, але цих операцій виконується стільки, скільки доступно потоків у відеокарті. Векторна арифметика має на увазі те, що виконується саме одна операція, причому, фактично, вона виконується просто над двома додатковими даними, складовими з себе впорядковано лежать кілька чисел в одній клітинці. Таким чином, векторні операції входять як підмножина в SIMD-операції, проте в ES2017 йдеться саме про векторної арифметиці, не знаю, чому вони так вирішили узагальнити, далі ми будемо вважати ці два поняття одним і тим же в рамках цієї статті.
Так що, виходить, ми можемо збільшити продуктивність своїх js додатків в 4 рази? (Протяжно)Нууу не зовсім. По-перше, більшість програм не просто перемелюють величезні обсяги даних, а так само, крім арифметики, мають розгалуження, очікування вводу-виводу, які-небудь інструкції, не які полягають у складанні або перемножении чисел. По-друге, дані потрібно спочатку звідкись завантажся, як правило, з оперативної пам'яті, що займає набагато більше часу, ніж зробити ці самі 4 операції. Тут рятує кеш, конвеєр і багатоканальний пам'ять, але приріст все одно нелінійний і не такий вражаючий, якщо тільки дана система, спеціально розроблена під векторну обробку даних. По-третє, доки ми пишемо не на асемблері і навіть не на досить низкоуровневом C++, на ініціалізацію самих цих чисел так само витрачається деякий час. Власне, у цій статті я покажу який приріст продуктивності це дає.
Практика
Отже, векторні операції скоро з'являться в js. Наскільки скоро? В даний момент їх підтримує firefox nightly, edge з прапором "експериментальні можливості" і chrome з прапором при запуску
--js-flags="--harmony-simd"
, тобто хоч у якомусь вигляді, але всі браузери. Крім цього є полифилл, так що можна використовувати вже прямо зараз.
Невеликий приклад як використовувати SIMD в js-код:
const someValue = SIMD.Float32x4(1,0,0,0);
const otherValue = SIMD.Float32x4(0,1,0,0);

const summ = SIMD.Float32x4.add(someValue, otherValue);

Повний список доступних функцій дивіться на MDN. Хочу звернути увагу, що SIMD.Float32x4 не є конструктором і запис
new SIMD.Float32x4(0, 0, 0, 0);
не є валидной.
Не буду розписувати всі можливості по використанню, їх не дуже багато, в цілому — арифметика та завантаження з вивантаженням даних, ще трохи прикладів все на тому ж MDN, відразу перейду до тестів.
Майбутнє тут? Продуктивність
Не буду витрачати ваш час на порожню, відразу перейду до найцікавішого: продуктивності та висновків. Невеликий jsfiddle, в якому я накидав кілька порівняльних тестів. Відкриваємо консоль і дивимося числа, чим менше, тим краще. В результаті у мене вийшов ось такий графік (час бралося середнє за 5-і запусків)
image
  • Під "чисте" мається на увазі, що поверх SIMD (або масиву) немає ніякої обгортки, код написаний безпосередньо. В реальності такий код вкрай незручно підтримувати, але якісь високонавантажених місця можуть бути переписані на подібний.
Перше, що кидається в очі — час кожен раз не стабільно. То SIMD трохи швидше, то ні, то класи швидше, то прототипи швидше. Кожен раз різний результат. Однак деякі закономірності уловлюються.
  1. В хромі звичайні масиви практично завжди виграють у SIMD в продуктивності. Таке відчуття, що в google chrome всередині взагалі варто полифил, ніякої нативної підтримки немає.
  2. Edge сьогодні не в дусі, тотальний провал за показниками, не знаю, що з ним сталося. Однак якщо ближче до теми статті: Edge чисте використання (без класів, прототипів, змішувань з звичайної арифметикою і масивами) дає відчутну перевагу. Як тільки з'являється завдання періодично отримувати або записувати одиничні значення в SIMD-структури — йде величезний провал по швидкості.
  3. Firefox SIMD показали себе непогано, навіть коли доводиться прочитувати якісь окремі значення з його структур, однак обгортка його в клас і приховування механізмів роботи з ним зіграло злий жарт.
  4. ООП через прототипи в js працює набагато швидше, ніж через конструкцію class, що може бути критичним у різних задачах, пов'язаних з графікою або математичними розрахунками. Взагалі, в графіках це не відображено, але причиною такого падіння продуктивності класів є полифил babel, якщо ж цей код вставити у консоль в такому вигляді, в якому він є — класи навіть обганяють прототипи, але чи готові ви пожертвувати користувачами, чий браузер не підтримує класи, або писати в прототипном стилі — вирішувати вам.
4. Майже скрізь SIMD показав найгірший результат, як наслідок — я точно не вмію його грамотно використовувати.
Висновки
Головний висновок: технологія SIMD в рамках мови javascript ще дуже і дуже сира. Переваги її використання зовсім не очевидні, а графіки нехай і на досить дурних тести показують що в більшості випадків ви тільки втратите час і продуктивність системи в цілому. Насправді не дуже зрозуміло навіщо взагалі в цей язик тягнути такі низькорівневі речі, оскільки всі переваги, які воно дає, губляться на стику технологій при пересиланні даних з одного формату в іншій. У браузерах, де все таки підтримується ця технологія більш-менш штатно, є сенс використовувати тільки в чистому вигляді, не змішуючи з іншими типами даних. Це може бути корисно в криптографії або вузькоспеціалізованих місцях, однак дає сміхотворний приріст у випадку моделювання, доки в задачах мат. моделювання визначення модуля вектора і таких його характеристик — завдання не така вже рідкісна, а вона на корені псує всю ідею. На жаль, API для роботи з SIMD не дуже вдале, а як тільки з'являються обгортки, відразу ж спостерігається величезний провал у швидкості роботи, причому, у всіх браузерах на даний момент.
Дуже хотілося б побачити приклад, коли дана технологія в застосуванні до js дійсно дасть великий приріст, однак, на мій погляд, продовжуємо чекати web-assembly, SIMD ж не зробить революцію в світі js. У прикладах на MDN наводиться пакетна обробка даних, проте в даний момент на синтетичних тестах ніякого помітного приросту продуктивності не спостерігається, зате код стає бруднішим і менш читабельним.
p.s. Якщо у вас є edge, firefox nightly або не лінь прописати прапор в ярлику google chrome — поділіться своїми результатами тестів.
P. P. S. На скільки мені відомо, саму технологію активно просувають Intel під свої цілі, гадаю, під Intel XDK в першу чергу. Там, безумовно, це може принести якусь користь, проте широкого застосування за межами їх інструментів навряд чи знайде.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.