typescript работает быстрее javascript и занимает меньше памяти. Скриншот с сайта с микробенчмарками
Думаю, что одного этого скриншота реально существующего замера производительности хватит, чтобы донести смысл статьи, но, если читателю интересны мои мысли на этот счет, то добро пожаловать.


Программисты помешаны на скорости исполнения программ. Мы следим за скоростью даже там, где эта скорость не очень-то и важна. Иногда вопреки здравому смыслу и логике. Даже не до конца понимая, что слова «скорость» или «перформанс» в действительности означают в каждом конкретном случае. Мы все равно хотим самое быстрое железо, самый быстрый язык и самый легковесный фреймворк. 


Охотно верю, что ты, 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 воспринимается как шутка, то в случае других языков проблема уже не так очевидна. А вы стали ли бы разбираться, в чем дело, если бы увидели такой отчет?
Сравнение go и swift. Скриншот. Выглядит так, что swift быстрее


Получается, что код бенчмарков нужно пристально проверять.


Вопрос: Когда вы последний раз видели человека, который лезет перепроверять, как написан код бенчмарков сразу после того, как увидел стандартные красивые графики производительности?
Я таких встречаю очень редко. Думаю, что таких инженеров исчезающе мало.


С другой стороны, перед глазами много примеров, когда на основании подобных тестов делается вывод о производительности и легковесности технологии. И, кстати, формируется общественное мнение.
Вот здесь можно сравнить производительность основных фреймворков на javascript.
результаты сравнение js фреймворков. Скриншот
Вы все еще доверяете результатам? Я – нет.
На этом все. Но есть и хорошие новости. Мастерство программиста по-прежнему сильно влияет на производительность программы. 


Вывод:
Микробенчмарки ничего вам не покажут, если вы не профессионал в производительности для конкретной платформы. А еще лучше, чтобы вы сами эти бенчмарки писали с учетом именно ваших требований и условий, при этом понимая, что делаете.


P.S.:
Пост написан как ответ на поспешные сравнения и выводы о произвоительности в будущем. Конечно, все выводы и доводы из статьи можно приводить и без этого примера, но с цифрами и конкретикой оно веселее и нагляднее.

Комментарии (52)


  1. RPG18
    14.12.2018 08:10
    -3

    Не очень понимаю чему тут удивляться. Видя разницу между TypeScript и JavaScript, сразу приходит на ум JIT. Asm.js тоже JavaScript, только сделан что-бы лучше джитился.


    Нужно сначала скопилировать его в javascript, который потом запустить в рантайме, который этот самый javascript понимает.

    Ну какой рантайм в числодробилках? После JIT-компиляции получаем машинный код.


    А вы стали ли бы разбираться, в чем дело, если бы увидели такой отчет?

    Тоже мне бином Ньютона. Из названия бенчмарка видно, что это числодробилка.


    Как думаете, насколько правильно делать вывод о том, что Swift быстрее Go?

    Под Swift находится LLVM, в котором заложены оптимизации, а компилятор Go не использует все современные инструкции процесса.


    В числодробилках да.


    1. mayorovp
      14.12.2018 08:42
      +1

      Не очень понимаю чему тут удивляться. Видя разницу между TypeScript и JavaScript, сразу приходит на ум JIT. Asm.js тоже JavaScript, только сделан что-бы лучше джитился.

      А TypeScript не сделан что бы лучше джитился.


      Ну какой рантайм в числодробилках? После JIT-компиляции получаем машинный код.

      Ну вот этот самый JIT, к примеру, в понятие "рантайма" входит.


      1. RPG18
        14.12.2018 09:58

        Это не значит, что он в принципе не может этого делать. Одна из идей заложенных в asm.js информация о типах


        1. mayorovp
          14.12.2018 10:18

          Он по построению не может исполняться быстрее чем Javascript. В отличии от asm.js, в рантайме у него информация о типах полностью теряется. Следующие три строчки будут преобразованы в один и тот же Javascript:


          const fn = (x : number, y : number) => x+y;
          const fn = (x : string, y : string) => x+y;
          const fn = (x : number | string, y : number | string) => x+y;

          Единственный возможный путь к ускорению за счет статической типизации — интерпретация Typescript напрямую, без преобразования в Javascript. Но так никто делать не умеет, и пока что не собирается.


          1. vintage
            14.12.2018 11:51

            Тут есть другой нюанс — на яваскрипте легко случайно сделать полиморфный код, который будет много раз деоптимизироваться, пока компилятор не забьёт и не оставит лишь интерпретацию. А статическая типизиция мотивирует к мономорфизму, который может позволить компилятору эффективно всё заинлайнить. Правда микробенчмарки эту разницу не покажут, ибо в них идиоматичность реализации зачастую на втором месте. А вот на реальных больших проектах разница видна. Достаточно сравнить как быстр VSCode и как медлителен Atom.


            1. zolotyh Автор
              14.12.2018 18:27

              Я думаю что это только предположение, которое невозможно доказать. Интуитивно с вами согласен.


          1. PsyHaSTe
            14.12.2018 17:06

            Единственный возможный путь к ускорению за счет статической типизации — интерпретация Typescript напрямую, без преобразования в Javascript. Но так никто делать не умеет, и пока что не собирается.

            Эмм… Не совсем


            1. mayorovp
              14.12.2018 17:36

              Так там же все равно в итоге V8 используется. А значит, и преобразование в Javascript есть, и типы никто не использует для оптимизаций.


              1. PsyHaSTe
                14.12.2018 18:00
                +1

                Так там же все равно в итоге V8 используется.

                Да. И в итоге в тот же ассемблер компилируется.
                А значит, и преобразование в Javascript есть, и типы никто не использует для оптимизаций.

                Leap of logic, этого оттуда не следует.


                1. Pappageno
                  14.12.2018 22:52
                  +1

                  Leap of logic, этого оттуда не следует.

                  Как вы объясните это:

                  For deno to execute typescript, it must first compile it to JS. A warm startup is when deno has a cached JS output already, so it should be fast because it bypasses the TS compiler. A cold startup is when deno must compile from scratch.


                  1. PsyHaSTe
                    15.12.2018 02:31
                    -1

                    Тем, что это не следует из «компилируется тем же V8»

                    Ответить могу тем, что да — сейчас не используется, но в будущем почему нет? Часть кода компилируем не в жс, в сразу в машинный код. Процесс-то не мгновенный. Потому что иначе во всем проекте мало смысла.


          1. kahi4
            14.12.2018 22:52

            Технически говоря, покуда мы не знаем (или знаем и я что-то просмотрел? Там есть мистическая строчка tsc, но все же) что там этот ts компилировало, можно предположить, что так же этот компилятор использовал какие-то трюки для оптимизации, например, заинлайнив метод doCacl, использовал SIMD или вообще переписал все под asm.js или вообще wasm.


            Правда никто не мешает тоже самое сделать и с js (типы только самому придется выводить).


            P.S. Как заметили ниже, в коде на ts просто используются воркеры вместо процессов в js.


          1. Zoolander
            15.12.2018 03:12

            верно, эти три строчки будут преобразованы в один и тот же JavaScript

            но это будет не тот JavaScript, который бы написал человек. Я взял ваши функции, прогнал через миллиард циклов и скормил TS-транспайлеру, а копию запустил просто как JS. Версия из транспайлера оказалась быстрее за счет оптимизаций — функция не вызывается в цикле, а просто записана как ряд операторов.

            Кроме того, const в TS для данных практически всегда означает подстановку этих данных по месту использования, что дает крохотный, но не гарантированный бонус, особенно если константа используется во множестве алгоритмов — так как вызов локальных переменных в функции происходит быстрее, чем обращение к глобальным.


            1. mayorovp
              15.12.2018 09:10

              Какой ключ у tsc делает инлайнинг константных функций? Я почему-то не наблюдаю подобного поведения.


              1. Zoolander
                15.12.2018 10:06

                Вы правы. Оптимизацию провел webpack. Написал я полный бред!

                Ну что же, впредь мне наука — проверяй гипотезы тщательнее и не спеши писать посты поздно ночью!


                1. vintage
                  15.12.2018 10:18

                  webpack тоже ничего подобного не умеет.


                  1. Zoolander
                    15.12.2018 10:23

                    репозиторий с настройками

                    Тест уверенно воспроизводится на моей машине, при установке флага minimize: false в конфиге вебпака — инлайнинга нет. Если же в optimization вообще ничего нет, как в репо, то инлайнинг есть


                    1. vintage
                      15.12.2018 11:19
                      +1

                      1. Zoolander
                        15.12.2018 11:38

                        причем делает это webpack по дефолту


                        1. vintage
                          15.12.2018 11:48

                          Это не webpack делает, а внешняя библиотека terser.


                          1. Zoolander
                            15.12.2018 11:53
                            +1

                            это уже схоластика. В документации terser-plugin описан как внешняя зависимость и есть даже указание на его install

                            Но по факту, посмотрите package.json, мы заказываем инсталл только webpack, webpack-server и webpack-cli, и внезапно оказывается, что у нас скрыто подключается terser. Почему тогда мы должны говорить «это делает не вебпак?» Ведь в депенденси нет терсера?

                            тогда давайте сделаем следующий шаг — «этот комментарий набираю не я, а мои пальцы». )


                            1. vintage
                              15.12.2018 12:00

                              Этот комментарий набираю не я, а человечество.


                              1. Zoolander
                                15.12.2018 12:07

                                знаете, если вы успели прочитать мою удаленную статью, то я нашел причину разрыва в скорости. Там инлайнинг играет очень крохотную роль. Разница в том, что ES5 на моих браузерах исполнялась быстрее, чем ES6. Это ввело меня в заблуждение и в цепь поспешных логических выводов.

                                А насчет философии иерархических включений — надеюсь, что я как человечество, рано или поздно перестану бить сам себе морду, а то получается какое-то сумасшествие. Наверное, поэтому, другие человечества и не спешат заходить в мою солнечную систему и здороваться с таким соседом )


                                1. vintage
                                  15.12.2018 12:32

                                  А что там было из es6?


                  1. Zoolander
                    15.12.2018 10:46

                    — обновил коммент выше, хватило времени ---


    1. staticlab
      14.12.2018 09:35

      Не очень понимаю чему тут удивляться. Видя разницу между TypeScript и JavaScript, сразу приходит на ум JIT.

      Реализация на TS сделана через потоки, реализация на JS сделана через процессы.


  1. ReklatsMasters
    14.12.2018 08:15

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


  1. ad1Dima
    14.12.2018 09:18

    Не проверяю корректность маркетинговых бенчмарков, потому что всё равно бессмысленно. Ибо сформировать нужную картину можно не подтасовывая факты, а выдав их в нужном порядке и количестве.


  1. inv2004
    14.12.2018 09:26

    Предположительно ТС просто изначально не очень понял что сравниваются формально похожие, но всё же разные реализации. т.е. выдать эффективную, но соответствующую требованиям реализацию — задача сообщества языка.

    Этот тест открыт для всех, если есть уверенность в том что JS должен не отставать от TS, то надо просто отправить туда свой вариант и его добавят. И несправедливость будет исправлена.

    Чтобы удобно следить — для этого там нумеруют конкретную реализацию, так как из-за смены компилятора или библиотеки или ещё чего-то, можно обнаружить, что конкретная реализация неожиданно разогналась или отстала.

    По-пунктам:
    1) TS vs JS, надо просто отправить код, которому вы верите и он будет принят. В статье много говорится об этом тесте, но причина почему так — не раскрывается. О каком рефакторинге идёт речь — не очень понятно: если задача — поддержать код будущими интернами — то эти тесты не про это.
    2) Rust vs Rust, просто игнорируйте версию, которая в данный момент отстала, если она не имеет для вас значение.
    3) Swift vs Go, из данного теста очевидно что в настоящий момент Swift опережает Go в реализации mandelbrot и ничего больше, чтобы ~ представить производительность на более широком кругу задач — то надо посмотреть на и на другие тесты. Какие-то из них завязаны на реализаций hashmap, другие — на деревья, большинство — на параллельное выполнение. Все в большинстве относительно простые. Если интересуют более комплексные тесты — то их надо искать не на этом сайте, но тем не менее какое-то относительно точное представление о производительности языков он даёт.

    Мало того, финальный документ на их странице показывает свечу с распределением результатов всех тестов для конкретного языка, где можно заметить, что в целом производительность не так сильно отличается, как кажется если смотреть только на топовые цифры.


    1. zolotyh Автор
      14.12.2018 16:30

      TS vs JS, надо просто отправить код, которому вы верите и он будет принят.
      Как может TS быть быстрее JS? Что это сравнение вообще показывает? Я не вижу причины существование этого теста. Полчить правку просто, достаточно TS скопилировать в JS.

      В статье много говорится об этом тесте, но причина почему так — не раскрывается
      Причина в том, что не нужно такие тесты вообще писать и публиковать. По факту идет сравнение двух скриптов на JS

      Rust vs Rust, просто игнорируйте версию, которая в данный момент отстала, если она не имеет для вас значение.
      Не могу проигнорировать. Если посмотреть внимательно, то самая быстрая версия в 2 раза быстрее чем предыдущая. А swift быстрее go меньше чем в два раза. Где гарантия, что кто-то кто понимает в go не напишет тест, который ускорит версию Go в два раза? Ведь это уже было с Rust.

      Swift vs Go, из данного теста очевидно что в настоящий момент Swift опережает Go в реализации mandelbrot и ничего больше
      Вот это как раз неочевидно. Про это и статья. Очевидно только то, что программист на Swift написал более быстрый код.


      1. inv2004
        14.12.2018 19:24
        +1

        достаточно TS скопилировать в JS

        Я согласен, именно это и надо сделать и отправить, к сожалению, вопрос JS vs TS меня не сильно волнует, чтобы заняться исправлением этого самому.

        Где гарантия, что кто-то кто понимает в go не напишет тест, который ускорит версию Go в два раза

        Как это устроено вполне понятно — чем больше вариантов решений для конкретного языка — тем больше можно сделать вывод, что код _приближается_ к оптимальному. Не очень понятно кто кому какие гарантии, в ежедневно меняющимся IT, тут должен давать. Тест показывает то что показывает на настоящий момент. «Где гарантии, что через 5 лет не выйдет специальный процессор под JS, на котором JS обгонит C?» — нет таких гарантий.

        Очевидно только то, что программист на Swift написал более быстрый код.

        На данном сайте говорится, что это не соревнование алгоритмов, для каждой задачи предоставляет описание каким именно образом это должно решаться, другой вопрос что даже в рамках этого описания есть варианты для оптимизации, как например — в C++ hashmap на шаблонах гораздо эффективнее для небольших числовых ключей.

        Я ещё раз добавлю — тест показывает то, что показывает в настоящий момент и ничего больше, но даже из этого можно сделать довольно много выводов — посмотреть на понятность кода, на активность сообщества, на скорость каких-то основных библиотек и тд и тп.


        1. mayorovp
          15.12.2018 09:12

          Тест показывает, что в настоящий момент конкретная программа на swift быстрее конкретной программы на go. Но тест не показывает, что в настоящий момент swift быстрее go.


      1. Zoolander
        15.12.2018 03:07

        вы подняли хорошую тему. Действительно, TS должен превращаться в аналогичный JS. И все-таки разница в скорости появляется — из-за того, что транспайлер TypeScript пытается оптимизировать производительность кода. Я написал одинаковый код на TS и JS, и первый оказался быстрее. Причина — в инлайнинге функций.

        Подробности, код и скриншоты — в короткой заметке
        habr.com/post/433230

        Буду рад, если вы проверите тесты, и убедитесь сами.


        1. Zoolander
          15.12.2018 10:08

          Вкратце: я был неправ и сонный, смущал народ, выдавал непроверенные теории за факты.

          Ту оптимизацию, что я описал, проводит webpack

          Другими словами, если workflow один и тот же, разницы между тестами на разных языках быть не должно.


  1. andreymal
    14.12.2018 11:14
    -1

    1. a_e_tsvetkov
      14.12.2018 11:46
      +1

      «Грузины лучше чем Армяне»


    1. humbug
      14.12.2018 15:53
      +1

      Хорошая шютка :D


      // Автор статьи "пруфлинка"


    1. PsyHaSTe
      14.12.2018 16:01

      Хорошая статья в тему: www.reddit.com/r/golang/comments/8ym8lf/why_is_this_simple_benchmark_3_times_faster_in

      Который, собственно, является очередным подтверждением темы статьи.


    1. snuk182
      14.12.2018 16:22

      — Windows лучше, чем Linux!
      — Чем?
      — Чем Linux!
      ©


    1. andreymal
      14.12.2018 18:52

      А, простите, чего заминусовали-то? Это ж тоже про веру бенчмаркам


      1. humbug
        14.12.2018 18:54

        Кстати, Go быстрее чем Rust

        Это был сарказм?))


        1. domix32
          14.12.2018 19:55

          Он забыл скинуть статью-опровержение, в которой автор частично чинит код на расте выносом компиляции регулярки за пределы цикла и еще чем-то и в финале просто включает свой мак к шнуру питания и получает чуть ли не двукратный прирост. Так что видимо действительно сарказм.


          1. andreymal
            14.12.2018 21:30
            +1

            Попробуйте почитать пост по моей ссылке дальше заголовка. А для пущего веселья сравните автора поста по ссылке с автором комментария, на который вы ответили)


            1. domix32
              14.12.2018 22:38

              Oh shi~, я думал это исходная статья про mail :D Минусы не мои если что


          1. PsyHaSTe
            15.12.2018 02:32

            Он — это человек, которому вы отвечаете на сообщение? :)

            А, выше уже написали. В общем, вышло забавно.


  1. Fengol
    14.12.2018 12:00

    А чему тут удивляться? Кто-то, кто имел достаточно опыта, но не достаточно ума, где-то, на вопрос новичка, посмотревшего лишь несколько уроков на youtube, «что делать дальше», ответил — идти в опенсорс или создавать свой сервис. И все как один это подхватили и словно боты начали повторять слова не лишенных смысла. И вот результат.


  1. Nagg
    14.12.2018 19:22

    Бенчмарки между языками обычно знак скудоумия и бенчмаркер обычно силен в одном своем маленьком мирке=языке, отсюда и идут перлы аля Свифт быстрее Go, Go быстрее Java. Вот когда "медленная" Java на ваших глазах развернет цикл, векторизует в AVX, выравняет данные для лучшей загрузки в регистры и все это в произойдет в момент выполнения когда программа сама поймет что это имеет смысл (tiered jit) в отличие от АОТ — вы все еще будете бенчмаркать Go vs Java?


  1. Xandrmoro
    14.12.2018 19:29
    +2

    Программисты помешаны на скорости исполнения программ. Мы следим за скоростью даже там, где эта скорость не очень-то и важна.

    Ах, если бы…
    К сожалению, мы живём в мире, где всем (ладно, почти) глубочайше насрать на производительность порождаемых ими монструозных уродцев.


    1. Lexicon
      15.12.2018 01:47

      О да, последние несколько проектов(так повезло) спрашиваю себя,
      почему на рынке еще не появилось ниши "читателей кода".
      "Аудит самих себя" не так часто работает. На последних местах — одно и то же:


      Прихожу и сижу денек, сжимаясь в страхе от желания все бросить, признав собственную неорганизованность под бесконечным потоком громких слов: "ревью", "тесты", "стейджинг", "митап", "рефакторинг". Потом оборудование, бумажки, неловкая наладка окружения, знакомство с коллективом — сплоченая команда, зовут в бар, отмечать пополнение состава новым коллегой.


      Наконец, приходит время смотреть проект:
      читать код, "выписывать свои мысли, чтобы сформировать задачи".


      И совершенно неочевидно, что делать, если все "очень плохо". В голове две мысли — плевать начальника и стучаться к боссу повыше, либо бежать.


      За оклад балансировать между корпоративной этикой и здравым смыслом может не каждый.


    1. PsyHaSTe
      15.12.2018 02:35

      Не знаю, я постоянно встречаю обратную ситуацию. Человеку нужно какой-нибудь веб-сервер написать, с БД и сетевыми запросами, так он начинает экономить на лишних копированиях восьмибайтных структур, «не, деление это дорого, надо сдвигами только», начинают спрашивать, как вынести запросы ДНС в отдельный поток, чтобы они быстрее работали… А клиент всё сидит и ждет, когда софтину напишут.


    1. zolotyh Автор
      15.12.2018 13:22

      Про мир в целом согласен. Один electron чего стоит. Но это не значит, что людей не беспокоит эта проблема. Так что все по классике. Чем больше беспокоимся, тем медленней работает.


  1. cgi-bin
    17.12.2018 12:52

    Посмотрел реализации на Java для пары задач — вынес для себя один вывод:
    Если нужно много операций с числами без потери точности то вместо BigInteger нужно использовать стороннюю библиотеку — так заметно быстрее и меньше памяти требует.
    В части сравнений между языками / платформами — видно как рантаймы отличаются между языками