Сравнение скорости работы языков программирования на примере решения задачи обучения нейронной сети

Цель работы


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

Железо и ОС


Для тестирования по Ubuntu и Windows ( ноутбук DELL Inspiron-7577):

RAM: 16Gb

CPU: Intel Core i7-7700HQ @ 8x 3.8GHz


рис. 1 (вывод команды screenfetch на ноутбуке DELL Inspiron-7577 под ОС Ubuntu)

Для тестирования на под MAC:

RAM: 8Gb

CPU: Intel Core i7 2.7GHz

Так-же мы провели тесты на Raspberry pi 4: 

RAM: 4Gb

CPU: ARMv7 rev 3 (v7l) @ 4x  1,5Ghz


рис. 2 (вывод команды screenfetch на raspberry pi 4)

Программа для тестирования


Для проведения тестов была написана программа имитирующая сеть из 5 нейронов, целью программы является научится правильно решать задачу нахождения исключающего или с точностью delta = 0.01. Все параметры и свойства нейросети, а также алгоритм работы и обучения были взяты из этих 2 постов:

https://habr.com/ru/post/312450/

https://habr.com/ru/post/313216/

Единственные изменения были внесены в коэффиценты E (эпсилон) — скорость обучения, α (альфа) — момент (E = 0.3, α = 0.5). При использовании значений указанных в статье нейросеть в течении длительного времени (8 ч.) не могла найти решения.

По своей структуре программа представляет из себя некую ООП модель, в  которой класс NeuronNet оперирует массивами объектов класса Neuron и Sinaps. Объект класса Sinaps содержит в себе ссылки на 2 объекта класса Neuron. Для расчетов с плавающей точкой применяется тип данных double.

Алгоритм тестирования: 

  1. Создается объект класса NeuronNet
  2. Проходит тестовый обучающий сет. Результаты работы нейросети до обучения и после выводятся в консоль и используются для сравнения с эталонными.
  3. Программа входит в бесконечный цикл где раз в  100000 итераций происходит сравнение результатов работы нейросети с эталонными результатами. В случае если погрешность составляет менее delta = 0.01 бесконечный цикл завершается. Каждое такое сравнение в дальнейшем буду называть эпохой. При прохождении эпохи в консоль выводится время которое она заняла и результаты расчета нейросети для каждого значения таблицы исключающего или.
  4. После выхода из цикла программа распечатывает время прошедшее с 0 до последней эпохи и завершается.

Программа изначально была написана на языке python3 (https://gitlab.com/di.romanov/neuron1.git), и впоследствии переведена на Java, Kotlin, C++(gcc), php7, ruby


рис. 3 (пример вывода программы написанной на языке Kotlin, запущенной под ОС Ubuntu)

Результаты тестирования


При работе программы написанные на Kotlin, Java, php, ruby и Python давали одинаковые ответы после обучающего сета, вывод после обучающего сета программы написанной  на С++ был другим, что повлекло за собой изменение количества эпох которое ей потребовалось для должного обучения. По этой причине будут приведены как сравнения времени работы всей программы так и времени которое потребовалось для прохождения одной эпохи.

Время обучения [мc.]
Ubuntu Windows Raspbian MAC
Python 104569 204239 521112 335621
Kotlin 4968 4877 19963 7775
Java 4892 5994 17973 7652
Ruby 79684 90524 457229
C++ 100990 212000 505377
php 75591 131170 513996

таб. 1(время прохождения всех эпох до обучения нейросети) 

Время прохождения одной эпохи [мc.]
Ubuntu Windows Raspbian MAC
Python 8713 16942 43315 27576
Kotlin 392 405 1631 625
Java 395 485 1434 635
Ruby 6667 7566 38040
C++ 4185 8834 21057
php 6381 10012 43168

таб. 2(время прохождения одной эпохи) 

Анализ результатов



граф. 1 (время прохождения всех эпох  для программ запущенных на ОС Ubuntu)

Как и ожидалось Kotlin и Java показали одинаковую скорость работы обогнав Python примерно в 20 раз. Рассмотрим некоторые не столь очевидные наблюдения.

Неожиданно медленными оказались результаты работы программы написанной на C++. Отчасти  это можно объяснить большим количеством эпох которое ей потребовалось для нахождения правильного ответа. Однако даже с учетом этого  (см граф. 2) она отстает по быстродействию от Java программ. 


граф. 2 (время прохождения одной эпохи  для программ запущенных на ОС Ubuntu)

Еще одной причиной подобных результатов может быть различное использование ресурсов процессора (см рис. 4, рис. 5)


рис. 4 (вывод монитора порта Ubuntu во время выполнения программы написанной на Kotlin)




рис. 5 (вывод монитора порта Ubuntu во время выполнения программы написанной на C++)

Как можно видеть, Java единовременно использует минимум 4 ядра, в то время как программа на C++ — одно. Однако, этим нельзя объяснить превосходство в скорости в 8 раз, так как Java не задействует все ядра на 100%.

Существенные различия в скорости работы программы написанной на Python в зависимости от ОС.  При запуске программ на Java на разных ОС различия в скорости работы составили не более 40% (даже на разных машинах, за исключением raspberry), однако при запуске програмы на Python были получены следующий значения: Ubuntu — 104c, Windows — 204c, MAC — 335c.  Отношение скорости работы программы на Kotlin к скорости работы программе на Python составляет 21 для ОС Ubuntu, 26 для Raspberry и аж 43 для Mac.

Все интерпретируемые языки программирования показали одинаковую скорость работы на Raspbery

Авторы перевода с Python3


Ruby, php

https://vk.com/silverlumen

Java

https://vk.com/id365699827

Комментарии 36

    +6
    А код где?
      +9
      Как можно видеть, Java единовременно использует минимум 4 ядра, в то время как программа на C++ — одно.

      Стесняюсь спросить, с каких это пор за использование ядер отвечает язык, а не программист? Ну и кто вообще в наше время обучает нейросети на процессоре?
        –5

        С тех пор как гошечка вышла)

          +1
          Простите, а как компилятор распаралелит линейный код?
          Гошечка может упрощать, но работа все равно за программистом
            0
            Если код написан «с умом», то и компилятор может распараллелить. Тем более, что код там не линейный по определению, иначе его было бы невозможно на GPU ускорять. Если по классике делать, то каждый нейрон на слое не зависит от остальных нейронов на этом слое, поэтому в пределах одного слоя всё паралелится замечательно. Матричные умножения тоже параллелятся. Так что тут 100% корявый код. Скорее всего ещё и реализация с ошибками, раз на С++ результат обучения иной получился. Один и тот же алгоритм должен давать один и тот же результат, если обучающие данные в одном и том же порядке подаются.
            0

            Да как бы и там надо писать код, go и так далее, да ещё и за памятью следить.
            В общем это так не работает.

          +13
          Стоп-стоп-стоп.
          Во-первых, пожалуйста, опубликуйте код на всех языках и со всеми параметрами запуска.
          Во-вторых — если алгоритм одинаковый, почему где-то он использует несколько ядер, а где-то — одно?
          В-третьих, опять же, если алгоритм одинаковый, чем объясняется
          вывод после обучающего сета программы написанной на С++ был другим, что повлекло за собой изменение количества эпох которое ей потребовалось для должного обучения
          ? Может, в коде C++ просто-напросто баг?
            +2
            Да, там и на Java/Kotlin процессор не загружен и всё время разные ядра нагружены. Задача обучения нейросети и процессор не нагружен на 100% всё время? А что тогда вообще измеряется? И что-то с планированием потоков там же — мало того, что это дико не эффективно (ибо, кэши), так ещё и указывает на ошибку в коде.
            C C++ вообще трэш и угар. Везде используется несколько потоков, а на C++ — один, т.е. вообще разная структура программ — что вообще сравнивает автор? И загрузка сети (соотношение принято/отправлено) на C++ не соответствует таковой на Java — т.е. там не просто структура программы разная, но и алгоритмы разные, и, как говорит сам автор, результаты (штоа?!!!)
            +4
            У вас какая-то хрень с Java/Kotlin под Windows — что-то с параметрами запуска.
            И у вас серьёзные ошибки в программах на Java и C++. Это по видно по совершенно ненормальным графикам процессора и сети (сети ?!). Они не могут так выглядеть на этой задаче.
            Ну, и кроме того, у вас все программы используют разные подходы.
            Такой громкий заголовок, интересная тема, и, уж простите, такое низкое качество по факту.
            А вот это:
            Неожиданно медленными оказались результаты работы программы написанной на C++. Отчасти это можно объяснить большим количеством эпох которое ей потребовалось для нахождения правильного ответа.

            вообще не понятно, как комментировать. Вы точно понимаете, о чём написали?
              0

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

                0
                Тогда о чём пост, что тестирует автор и почему у него такие результаты?
                  0
                  на мой взгляд, пост ни о чем, автор тестирует ненаучным способом какую-то чушь, не относящуюся к реальным задачам и поэтому у него такие результаты.
              +7
              Чего-то протестировали, какой-то результат получили… Классика.
                +5
                Ахахаха!

                Неожиданно медленными оказались результаты работы программы написанной на C++. Отчасти это можно объяснить большим количеством эпох которое ей потребовалось для нахождения правильного ответа.


                А вода не стекает с плоской земли, отчасти из за бортиков по краям :)))
                Спасибо! Вы сделали мой день более веселым.
                Я еще затра сюда загляну, посмотрю в камменты.

                  0

                  JavaScript бы ещё сюда

                    +1
                    Ага… на плейстешне… и он окажется быстрее всех. Как можно давать какие-то графики, не показав код и параметры, я тоже не понял.
                    –1
                    gitlab.com/di.romanov/neuron1kotlin.git
                    gitlab.com/di.romanov/neuron1cpp.git
                    Kotlin, остальное выложу ближе к вечеру
                      +2
                      Заглянул в neuron1cpp… Вопросов больше не имею :)
                        0
                        А зря, я бы еще про ключи компиляции узнал у ТС
                          0
                          А не надо… «кода» хватило :)
                        +1
                        c++
                        Заменив «string» на «const string &» время работы уменьшилось с 13 до 10 секунд.
                        Повсеместно передаются std::vector по значению, это тоже сильно влияет на производительность. По быстрому сделал возврат константной ссылки на статический вектор из тела функции (при каждом вызове делаю clear), теперь отрабатывает за 1 секунду.
                        За 10 минут ускорил программу в 13 раз! Распаралелить не получилось, надо дольше думать и без статических векторов код писать (передавать через аргумент по ссылке).

                        Для std::string можно сравнивать через ==, без strcmp. Это не влияет на производительность, просто их можно сравнивать напрямую.
                          0
                          Уже ближе к истине «Питон проигрывает С в 100 раз» (с) Гвидо.

                          В 5 раз ускоряет включение оптимизации, в 2 раза больше эпох, и твоя замена еще в 13 раз.

                          5*2*13

                          Но, честно говоря, без распараллеливания всерьез говорить не о чем.
                            0
                            У меня было 1200000 итераций, во всех попытках. Компилировал в Windows MSVS-2019. Очень смущает, что зависит от компилятора, где-то прям серьёзная ошибка, наверное.
                            0
                            Зачем сравнивать строки и передавать вектора?!
                            Мы же не хотим измерять, как работает strcmp и менеджер памяти.
                            Так как вектора используются просто для выборки — переделал на for по коллекции с нужным условием.
                            С фиксом для количества эпох время обучения сократилось со 102 до 1 секунды.
                            Для сравнения — kotlin на этой же машине выдаёт 7 секунд.
                              0
                              Я не разбирался в логике программы и что там и зачем, а просто починил" очевидные проблему. Зачем сравнивал, зачем передавал — вопросы к автору.
                              Как и ожидалось, автор тестировал непонятно что, написанное непонятно как. Возможно, в debug сборке вообще.
                            0
                            Вечер уже наступил? Мне бы было интересно посмотреть Java-код)
                            0

                            Было бы интересно увидеть C# в этом сравнении

                              0
                              Не факт, что в ближайшее время будет время для написания. Если есть желание, можете перевести на C# своими силами, я добавлю результаты в таблицу.
                              0
                              Я так понял, что статья написана ради того, чтобы похвастаться своим ноутбуком.
                              • НЛО прилетело и опубликовало эту надпись здесь
                                  +1
                                  даже на одной выборке, на одном ПК, под одной ОС сеть может найти решение, а может и не найти...

                                  Это как? Там случайные числа используются или таймстампы?

                                    0
                                    если подавать 2 одинаковые обучающие последовательности 2 на 2 одинаковые нейронки, почему бы ей не найти 2 одинаковых решения и не иметь одинаковые веса синапсов в конце обучения. Рандом же не используется.
                                    Меня признаться наоборот смущает, что результаты работы программы на СПП отличаются от результатов работы программы на той же яве.
                                      0
                                      При одинаковых начальных весах.
                                      Но уже одно то, что вариант на C++ по скорости сопоставим с вариантом на Python говорит, что тут что-то не так.
                                      0
                                      Выше уже есть ссылки на код, а ценности как не было, так и нет…
                                      +4
                                      Запустил у себя на чем было под рукой
                                      gcc 5.2 win32
                                      C++ -O2 45c
                                      C++ 212c
                                      2 400 000 итераций

                                      pyton 2.7.8 -O 223с
                                      1 200 000 итераций o_O

                                      Самое интересное, что CPU при этом не занят. 4 ядра по 20% и для Питона и для С++
                                      Причем программы однопоточные.

                                      В общем отличный тест, рановато еще автору писать на Хабре =)
                                        –3
                                        Неплохой тест получился.
                                        Приятно, когда берёшь исходник и он компилируется и работает.
                                        К тому же вышло неплохое сравнение именно реализации на разных языках программирования.
                                        Конечно качество кода хромает и сложно говорить о сравнении производительности между языками — всё-таки для одних языков используются ссылочные типы, а для с++ доступ по индексу.

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое