Pull to refresh

Comments 29

А вот какие: вдумчивое написание JS-кода позволяет получать достаточно высокую производительность и не требует перехода на другие языки программирования.

да, да и ещё раз да. Очень это сложно объяснить людям кто судит о техе по хайповости и рекламе. Когда я начал профессионально писать на JS несколько лет назад я был удивлён его реально огромной производительностью. Да 20 лет назад он был тормозым, 10 лет назад его обгонял флеш, сегодня он невероятно быстр. А тормозит обычно дом и прочие внешние относительно программы апи или плохие алгоритмы. А они от другого языка не ускорятся.
как я понимаю, отставание JS от Си будет сильно зависеть от представления данных и алгоритма. вот алгоритм из статьи хорошо зашел.

Скорость wasm — второстепенная характеристика, приятный бонус. Основная цель — снизить потребление памяти, сетевого трафика и процессорного времени веб-приложений, чтобы повысить общую энергоэффективность на мобильных устройствах.

скорость == процессорного времени веб-приложений. Память может быть, трафик не понятно, сжатые скрипты весят не много
процессорного времени веб-приложений

тогда надо добавить время на всякие repaint, context switch, GC, парсинг и компиляцию, кучку фоновых задач по общению с сетью, системой и прочие мелочи без которых контекст приложения не может жить. Как вы понимаете только небольшая часть из этого имеет непосредственное отношению к работе алгоритма и бенчмаркать весь кусок процессорного времени странно неэффективно и закономерно скатывается к грубой лжи из той поговорки.


трафик не понятно

не слишком силен в математике сжатия, но словари для wasm вроде вполне могут побить словари для JS/CSS, и содержать больше функционала на условный килобайт данных. Плюс, имея стриминг выполнения, можно разделять приложения на подгружаемые модули и качать/выполнять их лениво. В случае с JS такое провернуть дороговато. Но на данный момент это пока что моя фантазия, модули для wasm еще не завезли.

если не ошибаюсь, разработчики wasm говорили, что главная фишка — это предсказуемо высокая скорость работы в любом браузере. ну и плюс разработка на языке отличным от JS.

Предсказуемая производительнось != высокая производительность. Но для массового пользователя она должна быть не меньше чем имеет тот же JS, у которого уже довольно высокая производительность. Опять же — увеличение производительности — приятный бонус, а не цель. Основная цель сократить затраты на обработку и выполнение и как результат оптимизация энергопотребления. В чем браузеры, кстати, давненько соревнуются. Помню еще во времена 12 оперы они промо выпускали, мол глядите что будет с батареей, если стирать обычным порошком смотреть ютуб 8 часов в конкурентном браузере и сколько еще протянет второй такой же ноутбук если смотреть ютуб через оперу.

Предсказуемая производительнось != высокая производительность

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

Все не так.

Мода и хайп – это у реакт и подобных фреймворков.
А вот Blazor и C# – это другое

/sarcasm

Ну хорошо, после того, как JIT включился, производительность WASM и JS примерно одинакова. А как на счёт времени парсинга, размера бинарников, производительности без JIT? Ведь WASM по идее должен весьма тривиально транслироваться в нативный код по сравнению с JavaScript, который сначала распарсить надо, как минимум.

размер wasm значительно больше, чем js, поэтому в современных браузерах wasm сначала компилируется быстрым, но «тупым» компилятором, а потом медленным, но «умным». кроме того, файл начинает компилироваться еще до окончания своей загрузки.
размер wasm значительно больше, чем js

ну и шутки у вас

почему шутки? на практике так и есть.

Минимальный hello world на wasm будет около 300Кб, а на JS в этот размер можно запихать полноценный проект

Это если вы его каким-нибудь блазором крафтите или обвязку на JS, которую emscripten генерирует для asm.js в рассчет берете. В обычной ситуации даже при всей избыточности синтаксиса размер бинарного представления wasm заметно меньше чем сопоставимый код на js.

Как это у вас выходит?

Высокоуровневый язык по определению более компактный. Там где на Javascript будет написано `c=a+b`, на ассемблере (в том числе и WASM), будет намного более длинный набор низкоуровневых инструкций
Минимальный hello world на wasm будет около 300Кб

Ну нет. У меня получилось 48 KiB на wasm, и около 3-4 KiB на TS обвязку (в основном конвертация строк из JS формата в uint8array для C. И это ДО gzip-а. Но да, на чистом JS это всё весит меньше.

Мы, кроме того, заметили, что Wasm-сборки работают медленнее, чем нативные варианты кода, используемого для создания таких сборок. Разница в производительности составляет 20-50%.

конечно будут медленнее. производительность сильно зависит от распределения регистров процессора. на это нужно много времени. у компилятора Си это время есть, у компилятора WASM — нет. если не ошибаюсь, в Firefox один и тот же код распределяет регистры в JS и WASM. Мозила давно пилит новый компилятор WASM на Rust-е, но конца этому не видно.

кроме того, набор команд WASM не особо богат. например, нет операций над блоками памяти (копирование, сравнение, заполнение). нужно эмулировать их отдельными wasm-инструкциями (медленно если блок большой) или вызывать код JS (медленно если блок маленький).

По воле случая понадобилось мне в браузере запускать числодробилку. Опущу все детали, остановлюсь на сути — надо перебирать сотни тысяч sha1 хешей в поисках правильного. Реализация на JS с использованием window.crypto оказалась невыносимо медленной. Причём не сама, а её promise обвязка. Из-за того что нет синхронной версии большую часть времени браузер гоняет туда сюда свою promise-обвязку. Ок, думаю, воткну тогда какие-нибудь синхроные JS реализации. Оказалось побыстрее. В итоге остановился на библиотеке rusha. При переиспользовании 1-го объекта скорость оказалась весьма и весьма годной (в десяток разов быстрее). Под капотом там preallocation uintXarrays и asm.js. Везде всё протестил и пустил в бой.


Затем выяснилось, что с 73-й версии Firefox у себя что-то так наоптимизировал, что то, что раньше отнимало 1-4 секунды теперь стало отъедать до 60. Поигрался и понял, что нет, asm.js не отвалился. Но явно какие-то JS оптимизации пошли лесом. Другие библиотеки для sha1 тоже нужного результата не дали.


Ну думаю попробую в деле webAssembly. Думаю, возьму Go, вроде язык простой — быстро решу проблему. Ан нет. Пишут что минимальный wasm файл 2 MiB. Я пас. Выбирая между C и C++ решил остановиться на C, думая что будет проще. Вспомнив, все какие знаю, непристойные выражения собрал на коленке рабочую версию. Бинарно из терминала она работает гораздо быстрее любых других вариантов. Ок. Собрал WASM, цепляю в браузере… Сколько вы там говорите потеря производительности в WASM? 20-30%? У меня получилось в 10 раз. Да, да, в 10 раз. 700ms vs 7sec. Уж не знаю почему так медленно. Но уже не 40 секунд в 12 потоков (webworker-ы), как стало после обновления Firefox. Ну и запустив всё в параллельных webWorker-ах стало умеренно быстро.


Вот думаю, что я сделал не так. Пробовал компилировать C файл с -o3 — на результате никак не отразилось. Ничего не понимаю в системном программировании. Для меня это всё тёмный лес. emscripten собрал 48 KiB файл, что приятно. Но к нему полагается здоровенный JS файл с обвязкой (там тонны всяких костылей). Если её избегать, то, к примеру, возникает проблема с передачей строк в wasm-машину, т.к. нужно руками работать с памятью. Бррр. Причём не с абы какой памятью а с WASM emscripten.

в WASM был перенесен весь JS-код или только расчет SHA? на переходы JS <--> WASM нужно время.

В очень упрощённом виде:


  • есть основной JS thread который порождает cpuCount webWorker-ов. И каждому worker-у по мере его готовности выдаёт диапазон [start, end]. Где end - start = 10^4. Пробовал и 10^5, 10^6 — суть не меняется.
  • каждый worker внутри себя запускает WASM
  • WASM, написанный на C, условно делает вот это:

function task(start: number, end: number): number {
  for (let i = start; i <= end; ++ i) {
    const sha1 = generateSha1(something + i);
    if (isCorrect(sha1)) {
      return i; // task is solved
    }
  }
  return 0;  // solution was not found, request another pair [start, end];
}

Т.е. переходов между JS <--> WASM минимум. По сути 1 вызов на 200-1000ms в зависимости от того как я подкручу числа.


В изначальных тестах я запускал всё без webWorker-ов в основном JS thread-е сразу с 1 диапазоном (покрывающим всё сразу). Т.е. вообще без переходов на JS <--> WASM. Разница с таким же запуском в 1 поток просто c-программы из терминала в 10 раз.


По сути мне удалось приблизиться к скорости работы однопоточной c-программы только когда я распараллелил в 12 webWorker-ов тот же самый код в WASM-е.

на вскидку проблем не вижу. но я писал код wasm руками, не на языке высокого уровня. возможно, есть какая-то особенность emscripten, например с выделением памяти. разницы в 10 раз быть не должно, явно где-то есть косяк.

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

возьмите исходный код Си и посмотрите на предмет спец. инструкций типа sse, avx, intrinsics и т.д.
отключите их в компиляторе, выбрав старую модель процессора.
спец. инструкций процессора специально для sha я не знаю (есть для aes, crc, rnd).

отстал я от прогресса. мой камень sha ext. не поддерживает.

А почему Chrome в тестах такой старый? Когда тестирование проводилось?
Так и NodeJS 12.9.1 прошлогодний. Оригинал опубликован 12 июня, а фраза «As Max De Marzi said on his blog last summer in 2019» однозначно указывает, что тестировали они в этом году. Видимо где-то в начале этого года тестировали.
Какие выводы мы можем сделать из этих испытаний? А вот какие: вдумчивое написание JS-кода позволяет получать достаточно высокую производительность и не требует перехода на другие языки программирования.

А я не хочу писать вдумчиво, я хочу писать простой, прямолинейный код и сразу получать приемлемую производительность. К тому же в статье приведена числодробительная задача, которая хорошо JIT-ится. Если добавить более сложную логику, то производительность может довольно резко упасть, и, так как у вас нет контроля над виртуальной машиной, на которой JS исполняется, вы не можете сказать, когда этот момент наступит.

Думал речь идет о диалекте ассемблера, пора на свалку истории…
Sign up to leave a comment.