Сравнительный анализ языков программирования. Обзор языков программирования

Язык программирования - это система обозначений, служащая для точного описания программ или алгоритмов для ЭВМ. Языки программирования являются искусственными языками. От естественных языков они отличаются ограниченным числом “слов” и очень строгими правилами записи команд (операторов). Поэтому при применении их по назначению они не допускают свободного толкования выражений, характерного для естественного языка.

Basic. Он был разработан в 1963 году профессорами Дартмутского колледжа Томасом Курцем и Джоном Кемени. Язык предназначался для обучения программированию и получил широкое распространение в виде различных диалектов, прежде всего как язык для домашних компьютеров. При проектировании языка использовались следующие восемь принципов: 1.Быть простым в использовании для начинающих 2.Быть языком программирования общего назначения 3.Предоставлять возможность расширения функциональности, доступную опытным программистам 4.Быть интерактивным 5.Предоставлять ясные сообщения об ошибках 6.Быстро работать на небольших программах 7.Не требовать понимания работы аппаратного обеспечения 8.Защищать пользователя от операционной системы. Язык был основан частично на Фортран II и частично на Алгол-60, с добавлениями, делающими его удобным для работы в режиме разделения времени и, позднее, обработки текста и матричной арифметики. Синтаксис языка напоминает Fortran, и многие элементы - явные заимствования из него. Язык задумывался для обучения, поэтому его конструкции максимально просты. Как и в других языках программирования, ключевые слова взяты из английского языка. Основных типов данных два: строки и числа. Объявление переменных не требует специальной секции (в отличие, например, от Паскаля). Объявление переменной - это первое её использование.

C Sharp - C# (произносится си шарп) - объектно-ориентированный язык программирования. Разработан в 1998-2001 годах группой инженеров под руководством Андерса Хейлсберга в компании Microsoft как язык разработки приложений для платформы Microsoft.NET Framework. C# относится к семье языков с C-подобным синтаксисом, из них его синтаксис наиболее близок к C++ и Java. Язык имеет статическую типизацию, поддерживает полиморфизм, перегрузку операторов (в том числе операторов явного и неявного приведения типа), делегаты, атрибуты, события, свойства, обобщённые типы и методы, итераторы, анонимные функции с поддержкой замыканий, LINQ, исключения, комментарии в формате XML. Переняв многое от своих предшественников - языков C++, Java, Delphi. С#, опираясь на практику их использования, исключает некоторые модели, зарекомендовавшие себя как проблематичные при разработке программных систем, например, C# не поддерживает множественное наследование классов (в отличие от C++).


Язык программирования C++ - компилируемый статически типизированный язык программирования общего назначения. Поддерживает разные парадигмы программирования, но, в сравнении с его предшественником - языком Си, - наибольшее внимание уделено поддержке объектно-ориентированного и обобщённого программирования. Название «Си++» происходит от Си, в котором унарный оператор ++ обозначает инкремент переменной.В 1990-х годах язык стал одним из наиболее широко применяемых языков программирования общего назначения. При создании Си++ стремились сохранить совместимость с языком Си. Большинство программ на Си будут исправно работать и с компилятором Си++. Си++ имеет синтаксис, основанный на синтаксисе Си.

Достоинства C++ - чрезвычайно мощный язык, содержащий средства создания эффективных программ практически любого назначения, от низкоуровневых утилит и драйверов до сложных программных комплексов самого различного назначения. В частности : Высокая совместимость с языком С, позволяющая использовать весь существующий С-код (код С может быть с минимальными переделками скомпилирован компилятором С++; библиотеки, написанные на С, обычно могут быть вызваны из С++ непосредственно без каких-либо дополнительных затрат, в том числе и на уровне функций обратного вызова, позволяя библиотекам, написанным на С, вызывать код, написанный на С++). Поддерживаются различные стили и технологии программирования, включая традиционное директивное программирование, ООП, обобщенное программирование, метапрограммирование (шаблоны, макросы). Имеется возможность работы на низком уровне с памятью, адресами, портами. Возможность создания обобщённых контейнеров и алгоритмов для разных типов данных, их специализация и вычисления на этапе компиляции, используя шаблоны. Кроссплатформенность. Доступны компиляторы для большого количества платформ, на языке C++ разрабатывают программы для самых различных платформ и систем. Эффективность. Язык спроектирован так, чтобы дать программисту максимальный контроль над всеми аспектами структуры и порядка исполнения программы. Недостатки Отчасти недостатки C++ унаследованы от языка-предка - Си, - и вызваны изначально заданным требованием возможно большей совместимости с Си. Это такие недостатки, как: Синтаксис, провоцирующий ошибки: Препроцессор, унаследованный от С, очень примитивен. Плохая поддержка модульности (по сути, в классическом Си модульность на уровне языка отсутствует, её обеспечение переложено на компоновщик). Подключение интерфейса внешнего модуля через препроцессорную вставку заголовочного файла (#include) серьёзно замедляет компиляцию при подключении большого количества модулей (потому что результирующий файл, который обрабатывается компилятором, оказывается очень велик).

Паскаль . Появившийся в 1972 году язык Паскаль был назван так в честь великого французского математика XVII века, изобретателя первой в мире арифметической машины Блеза Паскаля. Этот язык был создан швейцарским учёным, специалистом в области информатики Никлаусом Виртом как язык для обучения методам программирования. Паскаль – это язык программирования общего назначения. Особенностями языка являются строгая типизация и наличие средств структурного(процедурного) программирования. Паскаль был одним из первых таких языков. По мнению Н. Вирта, язык должен способствовать дисциплинированию программирования, поэтому, наряду со строгой типизацией, в Паскале сведены к минимуму возможные синтаксические неоднозначности, а сам синтаксис интуитивно понятен даже при первом знакомстве с языком. Язык Паскаль учит не только тому, как правильно написать программу, но и тому, как правильно разработать метод решения задачи, подобрать способы представления и организации данных, используемых в задаче. С 1983 года языкПаскаль введён в учебные курсы информатики средних школ США.

Язык программирования Delphi (Дельфи) - среда разработки, использует язык программирования Delphi (начиная с 7 версии язык в среде именуется Delphi, ранее - Object Pascal), разработанный фирмой Borland и изначально реализованный в её пакете Borland Delphi, от которого и получил в 2003 году своё нынешнее название. Object Pascal - по сути является наследником языка Pascal с объектно-ориентированными расширениями. Delphi обеспечивает визуальное проектирование пользовательского интерфейса, имеет развитый объектно-ориентированный язык Object Pascal (позже переименованный в Delphi) и уникальные по своей простоте и мощи средства доступа к базам данных. Язык Delphi по возможностям значительно превзошел язык Basic и даже в чем-то язык C++, но при этом он оказался весьма надежным и легким в изучении (особенно в сравнении с языком C++). В результате, среда Delphi позволила программистам легко создавать собственные компоненты и строить из них профессиональные программы.

Java - объектно-ориентированный язык программирования, разработанный компанией Sun Microsystems - 23 мая 1995 года. Программы на Java транслируются в байт-код, выполняемый виртуальной машиной Java (JVM) - программой, обрабатывающей байтовый код и передающей инструкции оборудованию как интерпретатор. Достоинство подобного способа выполнения программ - в полной независимости байт-кода от операционной системы и оборудования, что позволяет выполнять Java-приложения на любом устройстве, для которого существует соответствующая виртуальная машина. Другой важной особенностью технологии Java является гибкая система безопасности благодаря тому, что исполнение программы полностью контролируется виртуальной машиной. Любые операции, которые превышают установленные полномочия программы (например, попытка несанкционированного доступа к данным или соединения с другим компьютером) вызывают немедленное прерывание. Часто к недостаткам концепции виртуальной машины относят то, что исполнение байт-кода виртуальной машиной может снижать производительность программ и алгоритмов, реализованных на языке Java. В последнее время был внесен ряд усовершенствований, которые несколько увеличили скорость выполнения программ на Java: применение технологии трансляции байт-кода в машинный код непосредственно во время работы программы (JIT-технология) с возможностью сохранения версий класса в машинном коде, широкое использование платформенно-ориентированного кода (native-код) в стандартных библиотеках, аппаратные средства, обеспечивающие ускоренную обработку байт-кода (например, технология Jazelle, поддерживаемая некоторыми процессорами фирмы ARM).

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

Таким образом, в наше время существует множество языков, и у каждого из них свои особенности.

Нет ничего более плодородного на онлайн-ветках, чем столкновение программистов-адептов разных языков. Любой аргумент в пользу «своего» и уничижительный заброс в пользу «чужого» в лучшем случае приводят к ответу «спорно» и взаимным претензиям. Так давайте попробуем разобраться в критериях, по которым можно оценить язык программирования, и разберемся — насколько вообще уместно их сравнивать?

Простота использования

Как определить, насколько легко новичку выучить язык программирования? За базис лучше всего брать школьный Pascal: возможности там были не великие, зато близость к английскому языку и отсутствие лишних действий на первых порах позволяют проникнуться к языку большой симпатией. То есть человек, не обладающий исключительными навыками в программировании, может или сразу, или после короткого ликбеза интерпретировать код начальной сложности. По такому критерию к сложным в данном случае можно отнести Assembler или C++.

Но есть и другой взгляд: брать за самый сложный язык тот, по которому вы сможете отыскать меньше всего литературы, в частности на русском языке. Как правило это недавно созданные экземпляры или практически не имеющие реального применения в современном мире. Но тут здесь возникает большая сложность градации: если условные Rust или C можно расставить по разным полюсам рейтинга без особых проблем, то как быть с давним спором Ruby и Python?

Прибыльность

Это то, на что обращают внимание молодые умы и посетители сайтов с онлайн-курсами. Ведь всегда есть вопросы: «если я изучу C, когда ко мне придёт миллион? А будет с Python быстрее?». Но дело в том, что оценка языка, как источника дохода, напрямую зависит от рыночной ситуации. То есть возможно язык Haskell и принесёт вам большой доход, но лишь в том случае, если вы найдёте соответствующую работу. А вот со знанием PHP вы сможете начать зарабатывать свой миллион раньше, правда потратите втрое больше чистого времени.

Было бы разумным ввести некую единицу оценки прибыльности исходя из соотношения зарплата/вакансия. Тогда, скорее всего, в лидерах бы оказались языки для мобильных платформ Android и iOS, т.е. Java и Swift. Однако в данном случае стоит вопрос необходимости и достаточности знания профильного языка: если безупречное владение тем же PHP может составлять 30% «must-have» требований, то вот Swift вряд ли потянет и на 10%. Да и является ли показателем качества языка количество вакансий? Может быть, всё в точности наоборот?

Быстродействие

Ещё один довольно спорный фактор, зависящий по сути от двух факторов - поставленной задачи и используемой машины, то есть абсолютно субъективных факторов. Нет, конечно, мы можем взять одну машину и устроить на ней сотни тестов разной сложности, чтобы выяснить, что C - это отличный язык программирования по быстродействию, а в самом низу списка будет Basic. Но градация скорости в середине вновь будет очень небольшой и брать её за показатель преимущества - нелепо.

Удобство

Субъективный параметр, который тем не менее, часто принимается за доказательство. Удобным язык может называться, если стандартные конструкции в нём записываются максимально коротко. Также удобным языком можно называть тот, где риск появления плавающей ошибки минимален. Удобный язык - этот тот, для работы с которым создано множество редакторов, плагинов, надстроек и пр. На самом деле любой основополагающий принцип языка можно выдать за удобство и оказаться правым. Так что такое удобство?

Область применения

Вот это первый аргумент в сегодняшнем списке, который можно посчитать, привести примеры и выявить победителя. Однако в каком случае данный спор выявит победителя? В каком случае количество бьёт качество? В споре двух мальчишек на уроке информатики? В реальной ситуации, когда человек подбирает себе инструмент для конкретной задачи или будущей работы, вопрос выбора языка с широким диапазоном применения обычно не стоит. Как правило, ситуация обратная: чем уже специализация, тем эффективнее применение языка. То есть знать несколько языков куда полезнее, чем один, но “широкоформатный”.

Функциональность

Функциональность и возможности - это как раз то, что можно почерпнуть из соответствующей таблички в Википедии . Сравнение полезное, достаточно полное и спорить с собеседником можно просто тыкая в соответствующую таблицу. Но вновь возвращаемся к эффективности такого сравнения: сподобят ли вас потенциально большие возможности взяться за изучение нового языка? Или это только отпугнёт?

А как вы считаете, можно ли сравнивать языки программирования? По каким ещё критериям?

Есть много параметров по которым можно сравнивать языки программирования. Именно как языки, а не платформы. Популярность, инструменты разработки, область применения, позиция на рынке — это важно, но интересно именно посмотреть на языки сами по себе.
Причём пофигу на их парадигмы: и на Си можно писать в ООП стиле (GTK+ например) и на Java можно делать монады .
А как они именно в жизни?

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

Я же хочу обратить ваше внимание на такие почти субъективные параметры:

1. Набор базовых аксиом.
Есть языки которые похожи на китайский — куча иероглифов. А есть такие как английский, где всего 26 букв, зато из них можно комбинировать слова а из слов предложения.
Очевидно что чем меньше таких вот базовых символов тем проще начать обучение. Но получается больше текста, да.
С другой стороны доводить набор букв до 1 и 0 тоже бессмысленно. Тут нужно подбирать компромис. В среднем во всех языках около 15ти операторов и до десяти базовых типов.
Аксиоматичную мощность можно посчитать и вывести коэффициент. Я даже нашёл какое-то сравнение мощности языков столетней давности .
Это вполне серъёзная и слегка научная задача годная для диссертации. Тут даже и графики красивые в маткаде можно нарисовать и из Википедии накопипастить заумных терминов. По крайней мере всерьёз годик разбираться.
Но вместо этого все пишут диссеры про какую то фигню, в лучшем случае о рефакторинге или RESTе. И кстати тоже не особо мудрённое дело для диссера: метод экстрактнуть или HTTP запрос отправить. Ловите намёк 😉

2. Читабельность.
Большую часть времени программисты читают код а не пишут.
Что такое читабельность начинаешь понимать когда сравниваешь код на питоне и на перле.
Тут такая закономерность: чем высокоуровневей язык программирования, тем лучше читаемость. SQL вообще читается как обычный текст.

3. Синтаксический сахар
Для часто повторяющихся примитивных вещей. Например, в яве строки — это объекты, и по хорошему нужно их конкатенировать вызовом метода. Но чтобы облегчить людям жизнь специально добавили синтаксический сахар когда оператор + выполняет конкатенацию строк.
Но плюс работает только для строк в виде исключения: всем остальным классам переопределять операторы нельзя, чтобы не было удивления в коде.
А вот в C++, C#, Groovy и многих других языках можно делать переопределение операторов и в результате потом часто удивляешься что это за фигня такая в коде: users << new User() пока не узнаёшь что добрые молодцы добавили сахару для добавления в список.
Вообщем, тут опять таки как с языками — если в английском жёсткая грамматика и порядок слов, то в русском пиши как хочешь и синтезируй слова.
В результате иностранцы не могут понять русский.

4. Возможность выстрелить в ногу.
Лучше всего стрелять в ногу на Си: тут ты выхватываешь настоящий Segmentation fault а не унылый NPE. Делишь 1/3 и получаешь 0. Пишешь if (x = 3) и долго удивляешься почему код не работает. И через переполненный буфер хакеры тебе валят сервак.
А ещё в Си бывают приколы когда из-за оптимизирующей опции компилятора программа перестаёт работать вообще.

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

#include #include #if !defined (_MSC_VER) || _MSC_VER < 1400 #error Wrong compiler! Use MSVS 8.0 #endif #define НАЧЕЛ { #define КОНЧЕЛ;} #define ТИПА int #define ВДРУГ if (#define ТАДА) #define НИХРИНА else #define ВЗАД return #define КАГДИЛА (#define ЙО; #define ЖЖОШ(p,n) for (; (p) <= (n); (p)++) #define БАЗАР std::cout << #define СЛЫШЬ << #define СТОЙ system ("echo. & pause"); #define БЛИН _wsetlocale (LC_ALL, L"Russian_Russia.ACP"); #define ВРОДЕ try #define ИБАНУЦЦО throw #define АПСТЕНУ catch (const char* __чё__) #define ПРЕВЕД ТИПА main КАГДИЛА ТАДА #define МЕДВЕД ВЗАД 0; КОНЧЕЛ ТИПА КРУТО КАГДИЛА ТИПА фигня ТАДА НАЧЕЛ БАЗАР "ВАЩЕ " ЙО ВДРУГ фигня == 8 ТАДА ИБАНУЦЦО "мля! " ЙО ВЗАД 0 КОНЧЕЛ ПРЕВЕД НАЧЕЛ БЛИН ВРОДЕ НАЧЕЛ ТИПА фишка = 0 ЙО ЖЖОШ (фишка, 10) НАЧЕЛ БАЗАР фишка СЛЫШЬ " "; ВДРУГ фишка >= 5 ТАДА КРУТО (фишка) ЙО КОНЧЕЛ КОНЧЕЛ АПСТЕНУ НАЧЕЛ БАЗАР "ИБАНУЦЦО invoked: " СЛЫШЬ __чё__; КОНЧЕЛ СТОЙ МЕДВЕД

Теперь давайте сравним языки по этим критериям:
Си
1. Минимальный базовый набор аксиом: В Си всё просто: вот функции, вот структуры, вот указатели, в атаку! Труъ сишники, типа Торвальдса, смотрят на плюсников как на позеров.
2. Читабельность: в Си она низкая i++, j—, for (;;) но сильно выручает что набор минимален.
3. Синтаксический сахар: квадратные скобочки для массивов (можно обойтись указателем) и строковые литералы.
4. В ногу вы просто стреляете: выходите за рамки буферов, забываете подчистить память, в результате имеете настоящий Segmentation fault.
5. Как это не удивительно без магии в Си не сделать ни шагу. Магия в Си — это директивы прекомпилятора. На них делается всё: инклдюды, константы, DSL’ы.

Си++
1. Тут уже появляются объекты, но с ними ещё куча свистелок и перделок по числу которых плюсы впереди планеты всей. Вообще появляется ощущение что плюсы тестовый полигон для всех всех концепций программирования.
2. Читабельность почти никакая. Местами спасает только Си-подобный синтаксис.
3. Сахарка хватает: даже хелоу ворлд начинается с переопределённого оператора << для cout.
4. Стреляем в ногу из пулемёта. Классика жанра — множественное наследование.
5. Магия достигается через шаблоны STL и Boost.

Ява
1. Тут просто взяли Си++ и убрали всё лишнее. В результате всё просто: вот класс, вот методы, создаёшь объект и дергаешь методы, если что-то пошло не так кидаешь исключение, «шо не йасно?».
Но всё равно перестарались: вложенные классы, статические методы, примитивные типы. Когда начинаешь готовится к сертификации OSCJP внезапно понимаешь что Явы ты не знаешь.
2. Синтаксис вполне уже читабельный, если не обращать внимание на реликты после Си. По сути lingva franca из-за чего её любят использовать в учебниках.
3. Если знаешь все приколы Си то в ногу стрельнуть уже довольно сложно. Разве что Out of memory или Null pointer exception. Собственно поэтому на Яве и написали кучу энтерпрайзного софта.
4. Магия постигается с помощью аннотаций, которые вообще то не должны влиять на программу. Но по факту по следам аннотаций потом курочат байт код до неузнаваемости.

Си шарп
1. Взяли яву и натащили всего нужного и ненужного в целях маркетинга. Чем структуры отличаются от классов? Зачем нужен yield ? Судя по всему он и не так часто используется .
3. Опять переопределение операторов и прочий зоопарк из C++, но уже с уборщиком мусора.
2. Читабельность нормальная. Ну примерно как у Делфи (создатель один и тот же).
4. Для магии всегда придумывают целые технологии, типа LINQ.

Пайтон
1. Большой набор базовых принципов которые плохо подобраны.
2. Читабельность хорошая — издалека вообще со стихами Маяковского можно спутать.
4. Магия достигается использованием сишных либ которые всё умеют. Поэтому на питоне написано половина гуёв на линуксах.
Вообще про питон лично я ничего хорошего не могу сказать — я на нём написал с десяток скриптов и каждый раз это было отстойно. Может в нём и есть какая фишка.

Перл
2. Перл получился в процессе эволюции текстового редактора. И бесспорно имеет самый ужасный write-only синтаксис. Регулярные выражения как раз пришли из перла, что о многом говорит. Мне кажется создатели Brainfuck просто немного упростили Perl.
4. Магия перла в том что одной строчкой можно переколбасить весь текст в документе.

Руби
По сути — объектно-ориентированный Перл.
1. Синтаксис настолько засахаренный что можно получить диабет.
2. Читабельность как у перла, но выручает единообразие — главное понять концепцию.
4. Магия достигается с помощью метапрограммирования поверх динамизма. Например, если вы дёргаете метод которого нет, то можно его перехватить и всё таки выполнить. Так делают динамик файндеры, например.
Всё что в книжках по яве написано «так делать нельзя» в руби пишут «смотрите, можно даже так сделать!».

Ada
Аду создавали в рамках конкурса для армии США. Т.е. изначально попросили академиков придумать самый крутой язык программирования. За что боролись на то и напоролись.
1. Базовый набор огромен. До появления Scala это был самый богатый язык программирования.
2. При этом великолепный читабельный паскальный синтаксис.
3. Сахару достаточно.
4. Магии практически нет — всё итак уже в языке. Хотя тут я не уверен, я не так много кода видел на Аде.

Я не просто так упомянул Аду, почитайте её критику:

Хоар выразил своё сожаление тем, что «погремушки и побрякушки возобладали над фундаментальными требованиями надёжности и безопасности» и предостерёг от «армады ракет, летящих не туда из-за не обнаруженной вовремя ошибки в компиляторе Ады». Никлаус Вирт высказался более сдержанно, но тоже негативно. Он сказал: «Слишком много всего вываливается на программиста. Я не думаю, что, изучив треть Ады, можно нормально работать. Если вы не освоите всех деталей языка, то в дальнейшем можете споткнуться на них, и это приведёт к неприятным последствиям». Жан Ишбиа, руководитель группы разработчиков Ады, выразив своё «уважение и восхищение» Виртом, не согласился с ним, сказав: «Вирт верит в простые решения сложных проблем. Я не верю в такие чудеса. Сложные проблемы требуют сложных решений».

Стоит ли говорить что в результате Ада в жопе а в космос летает софт написанный на Сишечке?

Scala
Скала это попытка сделать Яву функциональной в ущерб здравому смыслу.
Точно так же как и Аду её придумали искусственно в «лаборатории по созданию языков программирования» что уже должно настораживать.
Как и любая функциональщина, она доводит до оргазма всяких умников с «математическим складом ума».
Нормальным инженерам становится страшно когда они представляют как поддерживать проект на Скале. Scala хуже, чем Java. Как минимум, для половины Java проектов .

Очень хорошо это описано в статье Андрея Платова О выборе языка программирования :

Scala даст огромный рост продуктивности по сравнению с Java (так же как C->C++). Это верно для проекта с одним автором в вакууме. Сложность, и безграничные возможности языка отразятся гемороем, который нихера не будет способствовать продуктивности. Вопрос только в том чего будет больше. Я пока не знаю, да и больших Scala проектов в мире по большому счету еще нет.

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

Ещё по теме

PS

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

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

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

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

Примечание : Подобные оценки не могут служить критерием качества и даже производительности. Различные по идеологии языки будут иметь совершенно различающуюся относительную производительность на различных классах задач. Поэтому речь может идти только о сравнении порядков производительности и на определённом классе задач.

Примечание : Не ищите какого-то скрытого смысла и подтекста в том порядке, в котором представлены различные языки - они описаны в том произвольном порядке, в котором они хронологически тестировались.

Задача

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

Для грубых оценок вполне пригодна задача рекурсивного вычисления чисел Фибоначчи. Эта функция настолько проста, что её формулировка будет просто показана в изложении кода на языке C.

Примечание (для дотошной публики) : Существуют 2 определения последовательности чисел Фибоначчи: а). F 1 =0, F 2 =1, F N =F N-1 +F N-2 и б). F 1 =1, F 2 =1, F N =F N-1 +F N-2 . Как легко видеть, эти последовательности сдвинуты на 1 член, так что не стоит ломать копья по этому поводу: можно использовать любую форму. Мы будем использовать 2-ю.

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

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

Многие языковые средства предполагают и предоставляют те или иные способы оптимизации выполнения (например, уровень оптимизации, указываемый компилятору). Там, где мне известны способы оптимизации выполнения, будет использоваться максимальный уровень оптимизации.

Запуск команд на хронометраж мы станем делать командами вида:

# time nice -19 <команда_fibo> 30
  • команда выполняется от root, чтобы позволить повысить приоритет (nice -9) задачи выше нормального, снизив дисперсию результатов;
  • хронометраж выполняется системной командой time (не будем вмешиваться в процесс временных измерений);
  • параметр (30, порядковый номер числа Фибоначчи) определяет размерность задачи, объём вычислений в зависимости от него нарастает экспоненциально.

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

Результаты выполнения могут радикально меняться в зависимости от версии используемых инструментальных средств (компилятора, интерпретатора). Поэтому в итогах выполнения будет показываться версия используемого программного обеспечения.

Язык C

Листинг 1. Реализация задачи на языке C (fibo_c.c):
#include unsigned long fib(int n) { return n < 2 ? 1: fib(n - 1) + fib(n - 2); } int main(int argc, char **argv) { unsigned num = atoi(argv[ 1 ]); printf("%ld\n", fib(num)); return 0; }

Выполнение:

$ gcc --version gcc (GCC) 4.8.2 20131212 (Red Hat 4.8.2-7) ... # time nice -19 ./fibo_c 30 1346269 real 0m0.013s user 0m0.010s sys 0m0.002s

Можно предположить, что приложение, компилированное из C кода, будет самым быстрым. Поэтому именно эти цифры мы станем использовать как базовые значения для сравнения.

C++

Реализация будет выглядеть так:

Листинг 2. Реализация на языке C++ (fibo_c.cc):
#include #include using namespace std; unsigned long fib(int n) { return n < 2 ? 1: fib(n - 1) + fib(n - 2); } int main(int argc, char **argv) { unsigned num = atoi(argv[ 1 ]); cout << fib(num) << endl; return 0; }

Из этого единого кода будет создано 2 приложения - компиляцией GCC и компиляцией Clang:

$ g++ -O3 fibo_cc.cc -o fibo_cc $ clang++ fibo_cc.cc -o fibo_cl

Выполнение приложения, собранного GCC:

# time nice -19 ./fibo_cc 30 1346269 real 0m0.014s user 0m0.012s sys 0m0.002s

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

Выполнение приложения, собранного Clang:

$ clang++ --version clang version 3.3 (tags/RELEASE_33/final) Target: i386-redhat-linux-gnu Thread model: posix # time nice -19 ./fibo_cl 30 1346269 real 0m0.035s user 0m0.033s sys 0m0.001s

Здесь всё гораздо хуже! Это в 2.7 раза медленнее, чем для GCC. Но в объяснение этого может быть то, что в команде компиляции Clang вообще не устанавливалась опция оптимизации (-O...).

Java

Листинг 3. Реализация задачи на Java (fibo.java):
public class fibo { public static long fib(int n) { return n < 2 ? 1: fib(n - 1) + fib(n - 2); } public static void main(String args) { int num = new Integer(args[ 0 ]).intValue(); System.out.println(fib(num)); } }

Компиляция приложения выполняется в реализации OpenJDK:

$ java -version java version "1.7.0_51" OpenJDK Runtime Environment (fedora-2.4.5.1.fc20-i386 u51-b31) OpenJDK Server VM (build 24.51-b03, mixed mode) $ javac fibo.java $ ls -l *.class -rw-r--r-- 1 olej olej 594 Фев 15 16:09 fibo.class

Если то же самое проделать с оригинальном Oracle JDK, то временные результаты могут отличаться.

Выполнение:

# time nice -19 java fibo 30 1346269 real 0m0.176s user 0m0.136s sys 0m0.047s

Выполнение JVM байт-кода Java здесь в 13.5 раз медленнее, чем компилированного в машинные команды кода C.

Python

Аналогичный код на Python:

Листинг 4. Реализация на Python (fibo.py):
#!/usr/bin/python # -*- coding: utf-8 -*- import sys def fib(n) : if n < 2: return 1 else: return fib(n - 1) + fib(n - 2) n = int(sys.argv[ 1 ]) print("{}".format(fib(int(sys.argv[ 1 ]))))

Для этого кода (он написан в совместимом синтаксисе) мы можем также предложить 2 различных способа исполнения:

  • Python версии 2: $ python --version Python 2.7.5 # time nice -19 python fibo.py 30 1346269 real 0m1.109s user 0m1.100s sys 0m0.005s
  • Python версии 3: $ python3 --version Python 3.3.2 # time nice -19 python3 fibo.py 30 1346269 real 0m1.838s user 0m1.823s sys 0m0.009s

Первое, что здесь сразу бросается в глаза: Python 2 быстрее Python 3 на 65%. Это достаточно ожидаемо - это естественная плата за существенно расширенный синтаксис. Ряд публикаций показывают даже существенно большую разницу на определённых классах задач, до 2-х или 3-х раз.

А вот в сравнении с нативным компилированным кодом C Python 2 проигрывает до 100 (85) раз! Это тоже соответствует тому, что звучит в публикациях.

Ruby

Листинг 5. Реализация задачи на Ruby (fibo.rb):
#!/usr/bin/ruby # coding: utf-8 def fib(n) return n < 2 ? 1: fib(n - 1) + fib(n - 2) end puts fib(ARGV[ 0 ].to_i)

Выполнение:

$ ruby --version ruby 2.0.0p353 (2013-11-22 revision 43784) # time nice -19 ruby fibo.rb 30 1346269 real 0m0.566s user 0m0.554s sys 0m0.009s

Здесь время выполнения, на удивление (непонятно почему), почти в 2 раза (1.77) лучше, чем у Python, и медленнее нативного кода C примерно в 43 раза.

Perl

Листинг 6. Реализация задачи на Perl (fibo.pm):
#!/usr/bin/perl sub fib { my $n = shift; $n < 2 ? 1: fib($n - 1) + fib($n - 2) } $f = fib($ARGV[ 0 ]); print "$f\n";

Выполнение:

$ perl --version This is perl 5, version 18, subversion 2 (v5.18.2) built for i386-linux-thread-multi ... # time nice -19 perl fibo.pm 30 1346269 real 0m2.335s user 0m2.329s sys 0m0.002s

Здесь проигрыш нативному коду C составляет свыше 179 раз! Но это достаточно естественно и ожидаемо - Perl не язык для вычислений, и его ниша это текстовая обработка.

JavaScript

Листинг 7. Реализация на JavaScript (файл fibo.js):
#!/usr/bin/js -U var fib = function(n) { // функциональный литерал return n < 2 ? 1: fib(n - 1) + fib(n - 2); } print(fib(arguments[ 0 ]))

Выполнение приложения (начиная с уточнения версии):

$ js -v JavaScript-C 1.8.5 2011-03-31 # time nice -19 js fibo.js 30 1346269 real 0m0.689s user 0m0.683s sys 0m0.005s

Этот результат удивил: это почти те же цифры, что и у Ruby, и в 2 раза лучше, чем Python. От нативного кода C здесь отставание в 53 раза.

PHP

Эквивалент задачи, выраженный на языке PHP:

Листинг 8. Реализация PHP (файл fibo.php):
#!/usr/bin/php

Выполнение приложения:

$ php --version PHP 5.5.9 (cli) (built: Feb 11 2014 08:25:04) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies # time nice -19 php fibo.php 30 1346269 real 0m1.307s user 0m1.292s sys 0m0.013s

Это в 108 раз медленнее, чем эквивалентное C приложение.

Lua

Листинг 9. Реализация задачи на языке Lua (файл fibo.lua):
#!/usr/bin/lua fib = function(n) -- функциональный литерал if(n < 2) then return 1 else return fib(n - 1) + fib(n - 2) end end print(fib(arg[ 1 ] + 0))

Выполнение такого приложения (с проверкой версии Lua):

$ lua Lua 5.2.2 Copyright (C) 1994-2013 Lua.org, PUC-Rio > # time nice -19 lua fibo.lua 30 1346269 real 0m0.629s user 0m0.624s sys 0m0.003s

Это те же результаты, что и у JavaScript и Ruby.

bash

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

Листинг 10. Реализация задачи в bash (файл fido.sh):
#!/bin/bash if [ "$1" -lt "2" ] then echo "1" else f1=$($0 `expr $1 - 1`) f2=$($0 `expr $1 - 2`) echo `expr $f1 + $f2` fi

Я не рискну вызывать такое решение с аргументом 30 (как остальные варианты) - я просто не дождусь решения... Но выполняется такого скрипт вполне успешно:

$ bash --version GNU bash, version 4.2.37(1)-release (i486-pc-linux-gnu) … # time nice -19 ./fibo.sh 10 89 real 0m1.137s user 0m0.350s sys 0m0.475s # time nice -19 ./fibo.sh 12 233 real 0m2.979s user 0m0.935s sys 0m1.248s # time nice -19 ./fibo.sh 14 610 real 0m7.857s user 0m2.528s sys 0m3.166s

Получается, что скрипт bash вычисляет функцию от 8 столько же, сколько не очень «спешному» Perl требуется для вычисления функции от 29 (это при экспоненциальном то росте!):

# time nice -19 perl fibo.pm 29 832040 real 0m1.464s user 0m1.448s sys 0m0.004s

Практического смысла показанная реализация bash не имеет, но сама такая возможность интересна. Другой возможностью может быть искусственно организованная рекурсия (с очередью, стеком возвратов) при вызове функции внутри скрипта:

Листинг 11. Внутренняя рекурсия в bash (файл fido_f.sh):
#!/bin/bash declare -a res fib () { if [ "$1" -lt 2 ] then res[ $1 ]=1. else. fib `expr $1 - 1` let s=${res[ `expr $1 - 1` ]}+${res[ `expr $1 - 2` ]} res[ $1 ]=$s fi } res[ 0 ]=1 fib $1 echo ${res[ $1 ]}

Здесь уже совсем другие результаты:

# time nice -19 ./fibo_f.sh 30 1346269 real 0m0.157s user 0m0.037s sys 0m0.083s # time nice -19 ./fibo_f.sh 60 2504730781961 real 0m0.337s user 0m0.075s sys 0m0.167s

Для N=60 результат даже превосходит результаты выполнения нативного C кода. Но здесь мы просто наблюдаем результат обмана: при вычислениях сделана «оптимизация» и фактически рекурсивное вычисление выродилось в циклическое, не порождающее 2-х деревьев рекурсивных вызовов.

Можно воспользоваться различными способами - по синтаксису, возможностям, функциям и т.д. Для родственных языков программирования задача упрощается. В случае принципиально различных языков, можно воспользоваться сравнением по функциям. При таком сравнении, пишут несколько программ для решения набора задач на каждом из языков, далее подвергаются сравнению получившиеся программы. В зависимости от цели, сравнению может быть подвергнут синтаксис, скорость, размер или же для показа сходства/разницы в подходах для решения задачи, а так же для демонстрации областей применения определённого языка программирования. Есть несколько критериев, на основе которых можно осуществить оценку синтаксиса сравниваемых языков программирования.
1.Целостность. Отсутствие целостности делает его более трудным для изучения и повышает вероятность ошибок в программах на этом языке.
2.Избыточность конструкций. Язык с избыточностью или частично перекрывающие друг друга конструкции, позволяет добиться одной цели различными способами. Избыточность ведет к появлению различных стилей программирования при использовании одного и того же языка - различные алгоритмы используемые программистами для написания могут давать один и тот же результат.
3.Выразительность. Правильно написанная программа отражает лежащий в ее основе алгоритм. Чем выразительнее язык, тем легче решать стоящую задачу.
4.Многословие и безопасность. Согласно неформальному правилу, коротких одностраничных программ: чем короче код, тем меньше ошибок в нём можно совершить, к тому же длинные программы менее удобны при чтении и отладке. Многословный язык приводит к тому, что записанный короткий алгоритм превращается в длинный листинг программы. Необходимо заметить, что использование «шифрограммы» немногословного языка также нежелательно.

Правильность написания легко проверяется трансляторами языка программирования. Диагностируемые ими различные ошибки в синтаксисе это первый барьер на пути отладки при написании и вводе программы. В кратком языке программирования, без избыточности конструкций, очень трудно выявить множество самых различных ошибок, поскольку программы являются корректными с точки зрения синтаксиса этого языка программирования.
Краткое сравнение синтаксиса C, C++ и Python.
Пример игры написанной на языке Python 3.
print(‘Поиграем в очко?’)
count = 0

while True:
choice = input(‘Будете брать карту? y/n\n’)
if choice == ‘y’:
current = koloda.pop()
print(‘Вам попалась карта достоинством %d’ %current)
count += current
if count > 21:
print(‘Извините, но вы проиграли’)
break
elif count == 21:
print(‘Поздравляю, вы набрали 21!’)
break
else:
print(‘У вас %d очков.’ %count)
elif choice == ‘n’:
print(‘У вас %d очков и вы закончили игру.’ %count)
break

print(‘До новых встреч!’)
Пример программы написанной на C,C++. Языки C и C++ являются род-ственными, поэтому данная программа одинакова для обоих языков.
#pragma argsused
int main(int argc, char* argv)
{float x, y;
cout<<«vvedite x»;
cin>>x;
cout<<«vvedite y»;
cin>>y;
if ((x*x+y*y<=4)&&(x*x/9+y*y<=1))
cout<<«prinadlezhit»;
else cout<<«ne prinadlezhit»;
getch();
return 0;}

Существует два основных уровня языков программирования: высокого и низкого. Язык низкого уровня – язык, созданный для использования с определённым типом процессора и учётом особенности его архитектуры, иначе говоря, близок к машинному коду. Низкоуровневые языки не похожи на привычный язык людей. Длинные программы на них пишутся редко, зато будут работать быстро, в малом объеме памяти с минимумом ошибок. Чем ниже уровень языка, то есть ближе к машинному коду, тем меньше и конкретнее задачи, каждой команды. В изучении и применении заметно проще языки высокого уровня. Написанные с их помощью программы, используются на ПК с любой платформой, при наличии транслятора данного языка в машинный код.

Эти языки никак не учитывают свойства процессора и не предоставляют прямого обращения к нему. Конешно это ограничивает возможности программистов, но уменьшает вероятность совершения ошибки. На высокоуровневом языке операций придёться проделать намного больше для выполнения необходимой задачи. С их появлением программисты получили возможность больше времени уделять решению конкретной проблемы, не отвлекаясь на вопросы организации процессора. Все современные языки программирования можно разделить по классам, типам и специализации. Часто случается так, что один язык или несколько могут относится к разным категориям. Для сравнения выбраны C, C++ и Python. У каждого есть достоинства и недостатки, одно и то же решение для поставленной задачи может быть реализовано различными способами даже в пределах одного языка. Что же касается сравнения, C и Python являются объектно-ориентированными и обобщёнными, C не относится в силу своей упрощённости. По типизации данных C и C++ относятся к одной группе статически-явной, как родственные языки. Python относится к динамической типизации данных.

Ключевым моментом для программиста служит выбор компилятора-интерпретатора: главный элемент любого языка, позволяющий перевести строки символов в код работоспособной программы. Исходя из краткой сравнительной характеристики можно сделать вывод, что для начинающих программистов проще и лучше начать изучение C, нежели углублятся в изучение низкоуровнего ас-семблера или Python, так как язык C/C++ можно считать основным базовым в современном программировании. Он позволяет писать графически оформленные программы или просто консольные версии приложений не вдаваясь в подробное изучение архитектуры процессоров. Python или ассемблер не настолько гибкие и лояльные к начинающему программисту, поэтому при начальном изучении возникают трудности в виде ошибок которые не сразу заметны.



Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: