Думаю, что одного этого скриншота реально существующего замера производительности хватит, чтобы донести смысл статьи, но, если читателю интересны мои мысли на этот счет, то добро пожаловать.
Программисты помешаны на скорости исполнения программ. Мы следим за скоростью даже там, где эта скорость не очень-то и важна. Иногда вопреки здравому смыслу и логике. Даже не до конца понимая, что слова «скорость» или «перформанс» в действительности означают в каждом конкретном случае. Мы все равно хотим самое быстрое железо, самый быстрый язык и самый легковесный фреймворк.
Охотно верю, что ты, username, не такой. Что ты сам способен написать правильный бенчмарк, знаешь, как работает тот или иной рантайм, ненавидишь оптимизацию скорости просто ради оптимизации скорости и знаешь толк в железе. Но людей из предыдущего параграфа больше. Проверял.
Увидев пример на картинке выше, я сначала поперхнулся кофе…
– Как может typescript работать быстрее, чем javascript, и при этом кушать в несколько раз меньше памяти?!
– Никак!
Typescript не имеет собственного рантайма. То есть нельзя нигде или почти нигде запустить typescript. Нужно сначала скомпилировать его в javascript, который потом запустить в рантайме, который этот самый javascript понимает. В данном случае в качестве такого рантайма выступила node v11.3434. По счастливому совпадению, на том же рантайме бежит и пример на javascript.
Вместо сравнения языков мы получаем микросоревнование по спортивному программированию.
> Код на typescript
> Код на javascript
Получается, время выполнения, потребление памяти и прочие характеристики критично зависят от того, кто и как этот проверочный код писал. Конечно, здесь можно притянуть за уши аргумент о том, что typescript заставляет писать «правильный код». Но никто не помешает скомпилировать typescript в javascript, да и мест с оптимизациями я тоже не увидел.
Кстати, давно ли вы видели, чтобы кто-то писал подобный код, и он проходил ревью? Solid, ООП и ФП здесь и не пахнет. Код написан явно в процедурном стиле. Потому что у него задача другая. Поймите правильно, код хоть и решает задачу, но требует рефакторинга перед продакшном. И неизвестно, как рефакторинг повлияет на производительность. Но это уже, наверное, придирка.
С примером разобрались. Очевидно, что пример неадекватный.
Но давайте посмотрим, как этот же тест проходили другие языки программирования.
Как думаете, насколько правильно делать вывод о том, что Swift быстрее Go? Думаю, что неправильно. Достаточно посмотреть, что две реализации на Rust (2 и 6 строка) по времени отличаются в 2 раза.
И здесь проблема выглядит уже серьезнее. Если сравнение typescript и javascript воспринимается как шутка, то в случае других языков проблема уже не так очевидна. А вы стали ли бы разбираться, в чем дело, если бы увидели такой отчет?
Получается, что код бенчмарков нужно пристально проверять.
Вопрос: Когда вы последний раз видели человека, который лезет перепроверять, как написан код бенчмарков сразу после того, как увидел стандартные красивые графики производительности?
Я таких встречаю очень редко. Думаю, что таких инженеров исчезающе мало.
С другой стороны, перед глазами много примеров, когда на основании подобных тестов делается вывод о производительности и легковесности технологии. И, кстати, формируется общественное мнение.
Вот здесь можно сравнить производительность основных фреймворков на javascript.
Вы все еще доверяете результатам? Я – нет.
На этом все. Но есть и хорошие новости. Мастерство программиста по-прежнему сильно влияет на производительность программы.
Вывод:
Микробенчмарки ничего вам не покажут, если вы не профессионал в производительности для конкретной платформы. А еще лучше, чтобы вы сами эти бенчмарки писали с учетом именно ваших требований и условий, при этом понимая, что делаете.
P.S.:
Пост написан как ответ на поспешные сравнения и выводы о произвоительности в будущем. Конечно, все выводы и доводы из статьи можно приводить и без этого примера, но с цифрами и конкретикой оно веселее и нагляднее.
Комментарии (52)
ReklatsMasters
14.12.2018 08:15Проблема правильная, только пример не удачный. В бенчмарках сравнивается тёплое с белым, а именно скорость работы потоков с процессами.
Пишу микробенчмарки, когда нужно протестить скорость конкретной фичи. Но для тестов производительности всего приложения лучше использовать специализированные тулзы.
ad1Dima
14.12.2018 09:18Не проверяю корректность маркетинговых бенчмарков, потому что всё равно бессмысленно. Ибо сформировать нужную картину можно не подтасовывая факты, а выдав их в нужном порядке и количестве.
inv2004
14.12.2018 09:26Предположительно ТС просто изначально не очень понял что сравниваются формально похожие, но всё же разные реализации. т.е. выдать эффективную, но соответствующую требованиям реализацию — задача сообщества языка.
Этот тест открыт для всех, если есть уверенность в том что JS должен не отставать от TS, то надо просто отправить туда свой вариант и его добавят. И несправедливость будет исправлена.
Чтобы удобно следить — для этого там нумеруют конкретную реализацию, так как из-за смены компилятора или библиотеки или ещё чего-то, можно обнаружить, что конкретная реализация неожиданно разогналась или отстала.
По-пунктам:
1) TS vs JS, надо просто отправить код, которому вы верите и он будет принят. В статье много говорится об этом тесте, но причина почему так — не раскрывается. О каком рефакторинге идёт речь — не очень понятно: если задача — поддержать код будущими интернами — то эти тесты не про это.
2) Rust vs Rust, просто игнорируйте версию, которая в данный момент отстала, если она не имеет для вас значение.
3) Swift vs Go, из данного теста очевидно что в настоящий момент Swift опережает Go в реализации mandelbrot и ничего больше, чтобы ~ представить производительность на более широком кругу задач — то надо посмотреть на и на другие тесты. Какие-то из них завязаны на реализаций hashmap, другие — на деревья, большинство — на параллельное выполнение. Все в большинстве относительно простые. Если интересуют более комплексные тесты — то их надо искать не на этом сайте, но тем не менее какое-то относительно точное представление о производительности языков он даёт.
Мало того, финальный документ на их странице показывает свечу с распределением результатов всех тестов для конкретного языка, где можно заметить, что в целом производительность не так сильно отличается, как кажется если смотреть только на топовые цифры.zolotyh Автор
14.12.2018 16:30TS vs JS, надо просто отправить код, которому вы верите и он будет принят.
Как может TS быть быстрее JS? Что это сравнение вообще показывает? Я не вижу причины существование этого теста. Полчить правку просто, достаточно TS скопилировать в JS.
В статье много говорится об этом тесте, но причина почему так — не раскрывается
Причина в том, что не нужно такие тесты вообще писать и публиковать. По факту идет сравнение двух скриптов на JS
Rust vs Rust, просто игнорируйте версию, которая в данный момент отстала, если она не имеет для вас значение.
Не могу проигнорировать. Если посмотреть внимательно, то самая быстрая версия в 2 раза быстрее чем предыдущая. А swift быстрее go меньше чем в два раза. Где гарантия, что кто-то кто понимает в go не напишет тест, который ускорит версию Go в два раза? Ведь это уже было с Rust.
Swift vs Go, из данного теста очевидно что в настоящий момент Swift опережает Go в реализации mandelbrot и ничего больше
Вот это как раз неочевидно. Про это и статья. Очевидно только то, что программист на Swift написал более быстрый код.
inv2004
14.12.2018 19:24+1достаточно TS скопилировать в JS
Я согласен, именно это и надо сделать и отправить, к сожалению, вопрос JS vs TS меня не сильно волнует, чтобы заняться исправлением этого самому.
Где гарантия, что кто-то кто понимает в go не напишет тест, который ускорит версию Go в два раза
Как это устроено вполне понятно — чем больше вариантов решений для конкретного языка — тем больше можно сделать вывод, что код _приближается_ к оптимальному. Не очень понятно кто кому какие гарантии, в ежедневно меняющимся IT, тут должен давать. Тест показывает то что показывает на настоящий момент. «Где гарантии, что через 5 лет не выйдет специальный процессор под JS, на котором JS обгонит C?» — нет таких гарантий.
Очевидно только то, что программист на Swift написал более быстрый код.
На данном сайте говорится, что это не соревнование алгоритмов, для каждой задачи предоставляет описание каким именно образом это должно решаться, другой вопрос что даже в рамках этого описания есть варианты для оптимизации, как например — в C++ hashmap на шаблонах гораздо эффективнее для небольших числовых ключей.
Я ещё раз добавлю — тест показывает то, что показывает в настоящий момент и ничего больше, но даже из этого можно сделать довольно много выводов — посмотреть на понятность кода, на активность сообщества, на скорость каких-то основных библиотек и тд и тп.mayorovp
15.12.2018 09:12Тест показывает, что в настоящий момент конкретная программа на swift быстрее конкретной программы на go. Но тест не показывает, что в настоящий момент swift быстрее go.
Zoolander
15.12.2018 03:07вы подняли хорошую тему. Действительно, TS должен превращаться в аналогичный JS. И все-таки разница в скорости появляется — из-за того, что транспайлер TypeScript пытается оптимизировать производительность кода. Я написал одинаковый код на TS и JS, и первый оказался быстрее. Причина — в инлайнинге функций.
Подробности, код и скриншоты — в короткой заметке
habr.com/post/433230
Буду рад, если вы проверите тесты, и убедитесь сами.Zoolander
15.12.2018 10:08Вкратце: я был неправ и сонный, смущал народ, выдавал непроверенные теории за факты.
Ту оптимизацию, что я описал, проводит webpack
Другими словами, если workflow один и тот же, разницы между тестами на разных языках быть не должно.
andreymal
14.12.2018 11:14-1Кстати, Go быстрее чем Rust
PsyHaSTe
14.12.2018 16:01Хорошая статья в тему: www.reddit.com/r/golang/comments/8ym8lf/why_is_this_simple_benchmark_3_times_faster_in
Который, собственно, является очередным подтверждением темы статьи.
andreymal
14.12.2018 18:52А, простите, чего заминусовали-то? Это ж тоже про веру бенчмаркам
humbug
14.12.2018 18:54Кстати, Go быстрее чем Rust
Это был сарказм?))
domix32
14.12.2018 19:55Он забыл скинуть статью-опровержение, в которой автор частично чинит код на расте выносом компиляции регулярки за пределы цикла и еще чем-то и в финале просто включает свой мак к шнуру питания и получает чуть ли не двукратный прирост. Так что видимо действительно сарказм.
PsyHaSTe
15.12.2018 02:32Он — это человек, которому вы отвечаете на сообщение? :)
А, выше уже написали. В общем, вышло забавно.
Fengol
14.12.2018 12:00А чему тут удивляться? Кто-то, кто имел достаточно опыта, но не достаточно ума, где-то, на вопрос новичка, посмотревшего лишь несколько уроков на youtube, «что делать дальше», ответил — идти в опенсорс или создавать свой сервис. И все как один это подхватили и словно боты начали повторять слова не лишенных смысла. И вот результат.
Nagg
14.12.2018 19:22Бенчмарки между языками обычно знак скудоумия и бенчмаркер обычно силен в одном своем маленьком мирке=языке, отсюда и идут перлы аля Свифт быстрее Go, Go быстрее Java. Вот когда "медленная" Java на ваших глазах развернет цикл, векторизует в AVX, выравняет данные для лучшей загрузки в регистры и все это в произойдет в момент выполнения когда программа сама поймет что это имеет смысл (tiered jit) в отличие от АОТ — вы все еще будете бенчмаркать Go vs Java?
Xandrmoro
14.12.2018 19:29+2Программисты помешаны на скорости исполнения программ. Мы следим за скоростью даже там, где эта скорость не очень-то и важна.
Ах, если бы…
К сожалению, мы живём в мире, где всем (ладно, почти) глубочайше насрать на производительность порождаемых ими монструозных уродцев.Lexicon
15.12.2018 01:47О да, последние несколько проектов(так повезло) спрашиваю себя,
почему на рынке еще не появилось ниши "читателей кода".
"Аудит самих себя" не так часто работает. На последних местах — одно и то же:
Прихожу и сижу денек, сжимаясь в страхе от желания все бросить, признав собственную неорганизованность под бесконечным потоком громких слов: "ревью", "тесты", "стейджинг", "митап", "рефакторинг". Потом оборудование, бумажки, неловкая наладка окружения, знакомство с коллективом — сплоченая команда, зовут в бар, отмечать пополнение состава новым коллегой.
Наконец, приходит время смотреть проект:
читать код, "выписывать свои мысли, чтобы сформировать задачи".
И совершенно неочевидно, что делать, если все "очень плохо". В голове две мысли — плевать начальника и стучаться к боссу повыше, либо бежать.
За оклад балансировать между корпоративной этикой и здравым смыслом может не каждый.
PsyHaSTe
15.12.2018 02:35Не знаю, я постоянно встречаю обратную ситуацию. Человеку нужно какой-нибудь веб-сервер написать, с БД и сетевыми запросами, так он начинает экономить на лишних копированиях восьмибайтных структур, «не, деление это дорого, надо сдвигами только», начинают спрашивать, как вынести запросы ДНС в отдельный поток, чтобы они быстрее работали… А клиент всё сидит и ждет, когда софтину напишут.
zolotyh Автор
15.12.2018 13:22Про мир в целом согласен. Один electron чего стоит. Но это не значит, что людей не беспокоит эта проблема. Так что все по классике. Чем больше беспокоимся, тем медленней работает.
cgi-bin
17.12.2018 12:52Посмотрел реализации на Java для пары задач — вынес для себя один вывод:
Если нужно много операций с числами без потери точности то вместо BigInteger нужно использовать стороннюю библиотеку — так заметно быстрее и меньше памяти требует.
В части сравнений между языками / платформами — видно как рантаймы отличаются между языками
RPG18
Не очень понимаю чему тут удивляться. Видя разницу между TypeScript и JavaScript, сразу приходит на ум JIT. Asm.js тоже JavaScript, только сделан что-бы лучше джитился.
Ну какой рантайм в числодробилках? После JIT-компиляции получаем машинный код.
Тоже мне бином Ньютона. Из названия бенчмарка видно, что это числодробилка.
Под Swift находится LLVM, в котором заложены оптимизации, а компилятор Go не использует все современные инструкции процесса.
В числодробилках да.
mayorovp
А TypeScript не сделан что бы лучше джитился.
Ну вот этот самый JIT, к примеру, в понятие "рантайма" входит.
RPG18
Это не значит, что он в принципе не может этого делать. Одна из идей заложенных в asm.js информация о типах
mayorovp
Он по построению не может исполняться быстрее чем Javascript. В отличии от asm.js, в рантайме у него информация о типах полностью теряется. Следующие три строчки будут преобразованы в один и тот же Javascript:
Единственный возможный путь к ускорению за счет статической типизации — интерпретация Typescript напрямую, без преобразования в Javascript. Но так никто делать не умеет, и пока что не собирается.
vintage
Тут есть другой нюанс — на яваскрипте легко случайно сделать полиморфный код, который будет много раз деоптимизироваться, пока компилятор не забьёт и не оставит лишь интерпретацию. А статическая типизиция мотивирует к мономорфизму, который может позволить компилятору эффективно всё заинлайнить. Правда микробенчмарки эту разницу не покажут, ибо в них идиоматичность реализации зачастую на втором месте. А вот на реальных больших проектах разница видна. Достаточно сравнить как быстр VSCode и как медлителен Atom.
zolotyh Автор
Я думаю что это только предположение, которое невозможно доказать. Интуитивно с вами согласен.
PsyHaSTe
Эмм… Не совсем
mayorovp
Так там же все равно в итоге V8 используется. А значит, и преобразование в Javascript есть, и типы никто не использует для оптимизаций.
PsyHaSTe
Да. И в итоге в тот же ассемблер компилируется.
Leap of logic, этого оттуда не следует.
Pappageno
Как вы объясните это:
PsyHaSTe
Тем, что это не следует из «компилируется тем же V8»
Ответить могу тем, что да — сейчас не используется, но в будущем почему нет? Часть кода компилируем не в жс, в сразу в машинный код. Процесс-то не мгновенный. Потому что иначе во всем проекте мало смысла.
kahi4
Технически говоря, покуда мы не знаем (или знаем и я что-то просмотрел? Там есть мистическая строчка tsc, но все же) что там этот ts компилировало, можно предположить, что так же этот компилятор использовал какие-то трюки для оптимизации, например, заинлайнив метод doCacl, использовал SIMD или вообще переписал все под asm.js или вообще wasm.
Правда никто не мешает тоже самое сделать и с js (типы только самому придется выводить).
P.S. Как заметили ниже, в коде на ts просто используются воркеры вместо процессов в js.
Zoolander
верно, эти три строчки будут преобразованы в один и тот же JavaScript
но это будет не тот JavaScript, который бы написал человек. Я взял ваши функции, прогнал через миллиард циклов и скормил TS-транспайлеру, а копию запустил просто как JS. Версия из транспайлера оказалась быстрее за счет оптимизаций — функция не вызывается в цикле, а просто записана как ряд операторов.
Кроме того, const в TS для данных практически всегда означает подстановку этих данных по месту использования, что дает крохотный, но не гарантированный бонус, особенно если константа используется во множестве алгоритмов — так как вызов локальных переменных в функции происходит быстрее, чем обращение к глобальным.
mayorovp
Какой ключ у tsc делает инлайнинг константных функций? Я почему-то не наблюдаю подобного поведения.
Zoolander
Вы правы. Оптимизацию провел webpack. Написал я полный бред!
Ну что же, впредь мне наука — проверяй гипотезы тщательнее и не спеши писать посты поздно ночью!
vintage
webpack тоже ничего подобного не умеет.
Zoolander
репозиторий с настройками
Тест уверенно воспроизводится на моей машине, при установке флага minimize: false в конфиге вебпака — инлайнинга нет. Если же в optimization вообще ничего нет, как в репо, то инлайнинг есть
vintage
webpack.js.org/configuration/optimization/#optimization-minimize
Zoolander
причем делает это webpack по дефолту
vintage
Это не webpack делает, а внешняя библиотека terser.
Zoolander
это уже схоластика. В документации terser-plugin описан как внешняя зависимость и есть даже указание на его install
Но по факту, посмотрите package.json, мы заказываем инсталл только webpack, webpack-server и webpack-cli, и внезапно оказывается, что у нас скрыто подключается terser. Почему тогда мы должны говорить «это делает не вебпак?» Ведь в депенденси нет терсера?
тогда давайте сделаем следующий шаг — «этот комментарий набираю не я, а мои пальцы». )
vintage
Этот комментарий набираю не я, а человечество.
Zoolander
знаете, если вы успели прочитать мою удаленную статью, то я нашел причину разрыва в скорости. Там инлайнинг играет очень крохотную роль. Разница в том, что ES5 на моих браузерах исполнялась быстрее, чем ES6. Это ввело меня в заблуждение и в цепь поспешных логических выводов.
А насчет философии иерархических включений — надеюсь, что я как человечество, рано или поздно перестану бить сам себе морду, а то получается какое-то сумасшествие. Наверное, поэтому, другие человечества и не спешат заходить в мою солнечную систему и здороваться с таким соседом )
vintage
А что там было из es6?
Zoolander
— обновил коммент выше, хватило времени ---
staticlab
Реализация на TS сделана через потоки, реализация на JS сделана через процессы.