Java vs. Python

Эта публикация является в некоторой степени продолжением публикации «Java & Python» (https://zhevak.wordpress.com/2013/03/13/java-python/)

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

Поскольку я еще не очень силен в Java, то буду сравнить Java и Python на очень простой задаче — вычисление средне-арифметического значения массива чисел.

Массив чисел, который будет обрабатываться, находится в файле. Длина файла — огромная, более одного миллиона чисел. Числа целочисленные, записаны в символьном виде (а не в готовом двоичном формате!)  в колонку, т.е. одно число в строке.

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

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

Вот начало  этого могучего файла с числами:

489
485
482
482
482
483
484
484
485
486
487
490
494
498
502
...

Теперь, собственно, исходные коды программ. Код на Java:

// ave -- вычислить средне-арифметическое значение из массива, который в файле

import java.io.*;

class clAve {
  BufferedReader br = null;
  String str;
  int N;
  int[] ir;
  int number;
  double sum, ave;

  clAve(String filename) {
    try {
      br = new BufferedReader(new FileReader(filename));
      N  = 0;
      while ((str = br.readLine()) != null) {
        N++;
      }
      br.close();

      br = new BufferedReader(new FileReader(filename));
      ir = new int[N];
      sum = 0.0;
      for (int i = 0; i < N; i++) {
        if ((str = br.readLine()) == null) {
          System.out.println("Прочитал только " + i + " строк из " + N);
          break;
        }
        ir[i] = Integer.parseInt(str);
      }
      br.close();

      sum = 0.0;
      for (int i = 0; i < N; i++) {
        sum += ir[i];
      }
      ave = sum / N;
    }
    catch (IOException exc) {
      System.out.println("I/O Error: " + exc);
    }
    catch (NumberFormatException exc) {
      System.out.println("Invalid Format: " + exc);
    }
    System.out.println("N = " + N + ", ave = " + ave);
  }
}

class ave {
  public static void main(String[] args) {
    clAve obAve = new clAve(args[0]);
  }
}

Исходный код на Python:

#! /usr/bin/env python
# coding: utf-8

#'''Найти средне-арифметическое
#'''

#=============================================================================
file_in = open("ir.txt", "r")
strIR = file_in.readlines()  # Строковый массив входных данных
file_in.close()

ir = []  # Целочисленный массив входных данных

# Заполним массив исходными данными
for s in strIR:
  ir.append(int(s))
  
# Вычислим средене-арифметическое значение массива buf
sum = 0.0
for val in ir:
  sum += val

ave = sum / len(ir)
print 'N = ', len(ir), ', ave = ', ave

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

Итак, визуально сразу чувствуется, что на Java писать сложнее. Кода заметно больше. До кучи, я так и не смог найти помощь. Меня интересовало какие методы имеются в классе. Не нашел. Пока я это отношу на свою неопытность.

Размеры исходных файлов составляют 1315 байт (Java) и 702 байт (Python). Это примерно, так как в Питоновском исходнике я еще добавил декоративные элементы и комментарии.

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

Для пущей важности я откомпилировал и Питоновский исходник, превратил его в файл с суффиксом .pyc. Правда, эта компиляция никак не повлияла на скорость исполнения. А если более придирчиво относится к замерам — так прога стала еще и медленнее работать процентов на 10.

Компиляция Джавовского исходника очень простая

$ javac ave.java

В результате получился файл ave.class, который можно выполнять с помощью команды

$ java ave ir.txt

Компиляция Питоновского исходника производится в самой среде Питона. Нужно вызвать Питон в интерактивном режиме

$ python

После этого в его командной строке ввести пару команд:

>>> import py_compile
>>> py_compile.compile("ave.py")

В результате в директории появится еще один файл — ave.pyc.
Затем выходим из среды (из Питона), он нам больше не нужен, и выполняем команду

$ chmod +x ave.py*

Всё! Оба Питоновских файла в одно мгновение стали исполняемыми программами. Теперь их можно просто запускать:

$ ./ave.py

или так

$ ./ave.pyc

(Про клавишу табуляции помним? Знаем, что ее нажатие дописывает остаток файла?)

Итак, размеры откомпилированных файлов составляют 311 байт (Java) и 434 байт (Python). На этот раз Java оказалась компактнее.

Теперь, чтобы измерить время исполнения более объективно давайте перед командами запуска введем еще одну команду — команду для измерения времени работы программы — time. Вот примеры запуска:

$ time java ave ir.txt
$ time ./ave.py

У всех компьютеры разные. Поэтому скорости выполнения окажутся тоже разными. На моем Pentium-4 2.4 GHz, RAM 512 MB время выполнения Джавовской программы составляет примерно 1.1 секунды. Время выполнения Питоновской программы — примерно 3.2 с.

А что вы хотели! Питон — интерпретатор, а Джава исполняет прогу из промежуточного пи-кода. Я, вообще-то ожидал увидеть разницу раз в десять. Так что в моих глазах Питон оказался очень даже молодец!

Осмелюсь сделать вывод: на данный момент для меня лично написать не очень сложную прикладную программу оказалось проще на Питоне, чем на Джаве. Разница времени исполнения программ в несколько раз — это не существенно для разовых вычислений типа этого. Подумаешь, каких-то две лишних секунды! Я на Джаве «высерал кирпичи» и правил баги в течение наверно часа, в то время как на Питоне совсем не торопясь я написал текст и отладил за каких-то 10-15 минут.

А если учесть, что мне не приспичило охранять свои секреты — мне не нужно прятать исходный текст, душить «молодую поросль» лицензиями (не дизассемблировать! Не декомпилировать! Не вскрывать! Не изучать, как работает!), то Джава, получается, проигрывает Питону. Может быть _пока_ проигрывает. Но я не собираюсь на этом останавливаться, я планирую и далее изучать этот вопрос.

И еще. В качестве эталона, напишу-ка я реализацию этой проги на Си… Интересно же!

UPDATE

Ну вот, не поленился и написал реализацию на С. По времени процесс написания кода занял примерно полчаса. Втыкая в различные области (изучение аппаратной части микроконтроллеров, изучение языков высокого уровня (Python, Java), изучение Linux-инструментария (Git, gnuplot, программирование на шелл) и т.д.) я успел подзабыть многие вещи в POSIX. Пришлось несколько раз вызывать помощь (man).

Ну так или иначе, вот Си-шный код:

// ave.c
// Вычисление средне-арифметического

#include <stdio.h>
#include <stdlib.h>

#define STRSIZE   (10)


int main(int argc, char *argv[])
{
  FILE *f;
  char str[STRSIZE];
  int *buf;
  int i, N;
  double sum, ave;
  
  // Откроем файл
  if (argc == 2)
  {
    f = fopen(argv[1], "r");
    if (f == NULL)
    {
      printf("Не могу открыть файл %s\n", argv[1]);
      return EXIT_FAILURE;
    }
  }
  else
  {
    printf("Using: ave <файл>\n");
    return EXIT_FAILURE;
  }
  
  // Определим количество строк в файле
  N = 0;
  while (fgets(str, STRSIZE, f))
    N++;

  printf("N = %d, ", N);
  
  // Зарезервируем память под массив
  buf = calloc(N, sizeof(int));
  
  // Считаем файл еще раз и конвертируем числа в двоичный формат
  fseek(f, 0, SEEK_SET);
  for (i = 0; i < N; i++)
  {
    if (fgets(str, STRSIZE, f))
      buf[i] = atoi(str);
    else
    {
      printf("Проблема при повторном считывании файла\n");
      return EXIT_FAILURE;
    }
  }
  
  // Закроем файл
  fclose(f);
  
  // Подсчитаем средне-арифметическое
  sum = 0.0;
  for (i = 0; i < N; i++)
    sum += buf[i];
  
  // Освободим память
  free(buf);
 
  ave = sum / N;
  
  printf("ave = %f\n", ave); 
  return EXIT_SUCCESS;  
}

Этот код — отнюдь, не шедевр и не образец для подражания. Это обычный рабочий говнокод. Вполне себе работающий и выполняющий возложенную на него функциональность. Ну, не важно! Размер исходника составляет 1478 байт. Почти как Джавоский.

Давайте откомпилируем его:

$ gcc -Os -o ave ave.c

В результате имеем исполняемый файл ave размером 7456 байт. По сути самый огромный файл из всех, рассматриваемых здесь программ. Утешает лишь то, что ему для работы вообще ничего не нужно, ему не нужна среда и не нужен интерпретатор. Джавовская и Питоновская программы не заработают, если в системе не будет установлены Джава-машина и сам Питон. Следует, правда, оговориться, что если Джаву нужно доустанавливать, то Питон уже идет в составе дистрибутива, т.е. ничего дополнительно доустанавливать не надо. Как минимум это так на Ubuntu и Debian, и наверняка это так на их производных типа Mint-а.

Волнующий момент — а какова же скорость исполнения этого варианта программы? Команда

$ time ./ave ir.txt

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

Попробую подвести итог.

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

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

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

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

Я сужу чисто по себе. Для моих задач мне больше подходит Питон и Си, но не Джава. У меня нет задач, для Джавы. Я не вижу смысла тянуть Джаву на те задачи, которые достаточно хорошо решаются на Питоне или Си. Но это не значит, что я закрываю Джаву. Как минимум, я хочу все-таки дочитать книжку Шильдта, а дальше… а дальше — посмотрим!

еще один UPDATE

Посмотрим!!! — Посмотрели уже 😦

Только закончил предыдущее подновление писать, только закрыл блог, еще толком не успел зайти на ЛОР (http://www.linux.org.ru/), как читаю следующее сообщение и постепенно охреневаю:

Начиная с февраля, Oracle прекратила публикацию публичных обновлений Java 6. Тем не менее, сама Java 6 будет доступна в Java Archive. Желающим дальше пользоваться обновлениями Java 6 Oracle предлагает платную поддержку. Публичная поддержка Java 7, предположительно, тоже будет не такой долгой, как Java 6

ну что,.. ну, наверно так тому и быть. Давай, Oracle, до свидания! Спасибо тебе за MySQL и за OpenOffice. За Java — отдельное спасибо!

Ну, собственно, вот они — частные технологии. Что Майкрософт, что Оракл — одного поля ягоды. Вертели они своих клиентов у себя на колбасе! Нельзя им верить, в любой момент они могут выбить у тебя табуретку, стоит только поглубже засунуть голову. Мы для них — поставщики шерсти. Не более.

Реклама

4 responses to “Java vs. Python

  1. Автор, ну и херню вы написали. Молодец, конечно потратил силы, компилировал различный код и т.п. Но дело то уже не в секундах. При разработке приложений, особенно енетпрайз левел и глобал левел больше внимание отдается уже не столько перфомансу, а другими свойствам архитектуры: масштабируемость, поддерживаемость, модифицируемость и т.п. Потому что именно из-за этих свойств деньги уходят в финансовую дыру. Если бы вы когда-нибудь участвовали в очень больших проектах, вы бы видели, что гибкие языки своей гибкостью убивают проект. Именно поэтому жава стала такой востребованной. Да, кода больше, зато все четко и двусмысленность минимальна — например на уровне полиморфной переменной. Это во первых, а во вторых есть такой проект openJDK по лицезии GNU — используй сколько хочешь, как 6 так и 7. Ну и в 3, компиляция это тоже великое благо в больших проектах, так как компилятор оказывает необыкновенную помощь программисту в поиске возможных нестыковок.

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

      И тем не менее, спасибо за Ваш отзыв.

  2. Такое впечатление, что вы специально «наговнокодили» на Java и выставили это как недостаток языка/платформы, а на Питоне — написали кратко и красиво. Зачем, к примеру, в проге на Java вы два раза присваиваете sum = 0.0, используете исключения, а в питоновском варианте — нет, и самое интересное: вы два раза читаете содержимое файла, причем первый раз — только лишь для подсчета числа строк в нем?! Все это мое ИМХО.

    • Ну, как бы Вам сказать… ну да, у меня не очень чисто получилось написать прогу на Jave. И спасибо Вам, что Вы это заметили и подняли шум.

      Но с другой стороны, давайте попробуем оценить на сколько повлияет эта «грязь» на показатели. Давайте по пунктам.

      1. Дважды обнуляю переменную sum.

      Признаю — да, я лоханулся, не заметил. Одна строка лишняя. Уберем её.

      Повлияет ли это на размер кода? — Да, безусловно.

      На сколько сильно? — Ну, на несколько байт на фоне килобайта.

      Это существенно? — Да вообще никто не заметит!

      Хотя с точки зрения программирования — это да, это «грязь».

      2. Использование исключений.

      Ну что тут сказать. Пусть останутся. Просто оценим на сколько ужмётся размер программы и насколько быстрее она будет быстрее работать.

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

      На сколько такое сокращение размера существенно? — Ну, мне кажется не очень существенно, хотя будет заметно. Ну было полтора килобайта, станет один. После компиляции было 300 байт, станет 200.

      Мне сложно сказать как это отзовётся на быстродействии. Понятно, что скорость работы программы возрастёт. Но на сколько? Я не думаю, что в два раза. Мне кажется процентов на 10 от силы.

      Это что-то изменит? — Да, абсолютно ничего! Прога на Jave и так работает быстрее аналогичной проги на Python.

      3. Два раза читаю исходный файл.

      Ну, да — тут есть над чем поработать. Другое дело, что я не силён в Jave. Допустим, если переписать прогу так, что вычисление среднего арифметического будет происходить за один проход. Что от этого измениться?

      Размер проги еще уменьшится. Думаю, что вполне может усохнуть на 20-30 процентов. Ну дак и замечательно! Только это картины никак не испортит!

      Откомпилированный размер модуля как был меньшим у Javf-ы, чем у Python, так оно и останется.

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

      Предположим, скорость работы проги возрастёт ещё раза в полтора. Это как-то изменит картину? — Да никак! Java как работала быстрее Python, так и работает.

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

      Java и Python — каждый хорош по своему. У них у каждого есть своя изюминка. Хотя области их применения сильно пересекаются.

      Мне важно было понять — чего и сколько я теряю при выборе Питона и игнорировании Джавы.

      Я не пишу мега-программы. Я не занимаюсь корпоративными системами. Я не пишу программ на продажу и не оказываю послепродажную поддержку пользователей.

      Я пишу небольшие программы, размеры которых от нескольких десятков строк до, условно говоря, — до тысячи строк. Эти программы по ходу работы с ними могут сильно изменяться. Код программ находится в динамике.

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

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

      После тестирования двух систем на задаче подсчета среднего арифметического, я увидел, что в объеме кода Питон проигрывает примерно в два раза, а в скорости — в 3-5 раз.

      Проигрыш в скорости даже в 10 раз меня вполне устроил бы! Мои проги для расчетов требуют не так много времени. Да и запускаются такие длительные расчеты не так уж и часто — можно и потерпеть!

      Надеюсь, я доходчиво объяснил Вас свою позицию. Если у Вас есть более лучший вариант проги на Джаве, пришлите его — я помещу его в статью.

      Мне не важно — кто быстрее Джава или Питон, или кто создаст меньший по объёму код. Мне важно — где удобнее работать, и чем при этом приходится жертвовать ради удобства.

      А Джава — да, хороша! Но для своих задач!

      И ещё раз спасибо за найденные недочёты в программе!

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s