Scala vs java, производительность и память?

Я стараюсь заглянуть в Scala, и у меня есть один базовый вопрос, который я, похоже, не нашел ответа: в общем, есть ли разница в производительности и использовании памяти между Scala и Java?

Ответ 1

Scala позволяет очень просто использовать огромные объемы памяти, не осознавая этого. Это, как правило, очень мощный, но иногда может раздражать. Например, предположим, что у вас есть массив строк (называемый array) и карта из этих строк в файлы (называемые mapping). Предположим, вы хотите получить все файлы, которые находятся на карте, и поступать из строк длиной более двух. В Java вы можете

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}

Уф! Тяжелая работа. В Scala самый компактный способ сделать то же самое:

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

Легко! Но, если вы не знакомы с работой коллекций, то, что вы можете не понимать, заключается в том, что этот способ создания дополнительного промежуточного массива (с filter) и дополнительный объект для каждого элемента массива (с mapping.get, который возвращает параметр). Он также создает два функциональных объекта (один для фильтра и один для flatMap), хотя это редко является серьезной проблемой, поскольку объекты функций небольшие.

Таким образом, использование памяти на примитивном уровне одинаково. Но библиотеки Scala имеют множество мощных методов, которые позволяют легко создавать огромное количество (обычно короткоживущих) объектов. Сборщик мусора обычно хорош с таким видом мусора, но если вы полностью не замечаете, какая память используется, скорее всего, вы столкнетесь с проблемой раньше, чем в Scala, чем Java.

Обратите внимание, что код компьютерной игры Benchmark Game Scala написан в довольно подобном Java стиле, чтобы получить производительность, подобную Java, и, следовательно, имеет использование памяти в виде Java. Вы можете сделать это в Scala: если вы напишете код, похожий на высокопроизводительный Java-код, это будет высокопроизводительный Scala код. (Возможно, вы сможете записать его в более идиоматическом стиле Scala и получить хорошую производительность, но это зависит от специфики.)

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

Ответ 2

Я новый пользователь, поэтому я не могу добавить комментарий к ответу Rex Kerr выше (позволяя новым пользователям "отвечать", но не "комментарий" - очень странное правило btw).

Я подписался просто, чтобы ответить на "phew, Java настолько многословный и такой тяжелый труд", в котором упоминается популярный ответ Rex выше. Хотя вы можете, конечно, написать более сжатый код Scala, приведенный пример Java явно раздувается. Большинство разработчиков Java кодируют что-то вроде этого:

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}

И, конечно, если мы собираемся притвориться, что Eclipse не делает большую часть фактического ввода для вас и что каждый сохраненный символ действительно делает вас лучшим программистом, тогда вы можете это сделать:

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));

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

Ответ 3

Запишите свой Scala как Java, и вы можете ожидать, что вы получите почти одинаковый байт-код - с почти одинаковыми метриками.

Запишите его более "идиоматично", с неизменяемыми объектами и функциями более высокого порядка, и это будет немного медленнее и немного больше. Единственным исключением из этого правила является использование общих объектов, в которых параметры типа используют аннотацию @specialised, это создаст еще больший байт-код, который может опередить производительность Java, избегая бокса/распаковки.

Также стоит упомянуть тот факт, что больше памяти/меньше скорости является неизбежным компромиссом при написании кода, который можно запускать параллельно. Идиоматический код Scala является гораздо более декларативным по своей природе, чем обычный Java-код, и часто является всего лишь 4 символами (.par) от полностью параллельного.

Итак, если

  • Scala код занимает 1.25x длиннее кода Java в одном потоке
  • Его можно легко разделить на 4 ядра (теперь они распространены даже в ноутбуках).
  • для параллельного времени выполнения (1.24/4 =) 0.3125x оригинальной Java

Не могли бы вы сказать, что код Scala теперь сравнительно на 25% медленнее или в 3 раза быстрее?

Правильный ответ зависит от того, как вы определяете "производительность":)

Ответ 4

Компьютерные игры Benchmarks Game:

Тест скорости java/ scala 1.71/2.25

Тест памяти java/ scala 66.55/80.81

Итак, в этих тестах говорится, что java на 24% быстрее, а scala использует на 21% больше памяти.

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

Нижняя строка: Если scala делает вас и вашу команду (и людей, которые проецируют проект, когда вы уходите), более продуктивны, тогда вы должны пойти на это.

Ответ 5

Другие ответили на этот вопрос относительно жестких циклов, хотя, по-видимому, существует очевидная разница в производительности между примерами Рекса Керра, о которых я прокомментировал.

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

Я относительно не знаком с Scala (примерно год или около того), но до сих пор его ощущается, что он позволяет отложить многие аспекты проектирования, реализации и выполнения относительно легко (с достаточным количеством фоновых чтений и экспериментирование:)

Функции отложенного дизайна:

Возможности отложенной реализации:

Отложенные функции выполнения: (извините, никаких ссылок)

  • Потоковые безопасные ленивые значения
  • Pass-по имени
  • Монадический материал

Эти функции для меня - это те, которые помогают нам прокладывать путь к быстрым, жестким приложениям.


Примеры Рекса Керра отличаются тем, какие аспекты исполнения отложены. В примере Java распределение памяти отложено до тех пор, пока не будет рассчитан размер, где пример Scala отменяет поиск по карте. Для меня они кажутся совершенно разными алгоритмами.

Здесь я думаю, что это больше яблок для яблок, эквивалентных его примеру Java:

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})

Нет промежуточных коллекций, экземпляров Option и т.д. Это также сохраняет тип коллекции, поэтому bigEnough type is Array[File] - Array collect реализация, вероятно, будет делать что-то в соответствии с тем, что делает код Mr Kerr Java.

Отложенные конструктивные элементы, перечисленные выше, также позволят разработчикам API-интерфейсов Scala реализовать эту быструю сборку с массивом в будущих выпусках без нарушения API. Это то, что я имею в виду, чтобы пройти путь к скорости.

также:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)

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


Один пример простой скорости выполнения в Scala - с протоколированием.

В Java мы можем написать что-то вроде:

if (logger.isDebugEnabled())
    logger.debug("trace");

В Scala это справедливо:

logger.debug("trace")

потому что параметр сообщения для отладки в Scala имеет тип "=> String", который я считаю как функция без параметра, выполняемая при ее оценке, но которую документация вызывает pass-by-name.

EDIT { Функции в Scala являются объектами, поэтому здесь есть дополнительный объект. Для моей работы вес тривиального объекта стоит удалить возможность того, что сообщение журнала становится бесполезно оценено. }

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

Для меня это согласованная тема в Scala.


Жесткий код не может понять, почему Scala работает быстрее, хотя он немного намекает.

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

В Java удивительный код часто вынужден стать непостижимым беспорядком, и поэтому он не очень жизнеспособен в API качества продукции, поскольку большинство программистов его не смогут использовать.

У меня есть большие надежды, что Scala может позволить эйнштейнам среди нас внедрять гораздо более компетентные API, потенциально выраженные через DSL. Основные API в Scala уже далеко по этому пути.

Ответ 6

Java и Scala скомпилируются до байт-кода JVM, поэтому разница не такая большая. Лучшее сравнение, которое вы можете получить, вероятно, находится на контролеры компьютерных языков, в котором, по сути, говорится, что Java и Scala имеют одинаковое использование памяти, Scala только немного медленнее, чем Java, в некоторых из перечисленных тестов, но это может быть просто потому, что реализация программ отличается.

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

Ответ 8

Пример Java действительно не является идиомой для типичных прикладных программ. Такой оптимизированный код может быть найден в методе системной библиотеки. Но тогда он будет использовать массив правильного типа, т.е. File [], и не будет генерировать исключение IndexOutOfBoundsException. (Различные условия фильтрации для подсчета и добавления). Моя версия будет (всегда (!) С фигурными фигурными скобками, потому что я не люблю тратить час на поиск ошибки, которая была введена путем сохранения 2 секунд, чтобы нажать один ключ в Eclipse):

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

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

В моем абстрактном базовом классе DAO у меня есть абстрактный внутренний класс для общего механизма кэширования. Для каждого конкретного типа объекта модели существует подкласс абстрактного базового класса DAO, в котором внутренний класс подклассифицирован для обеспечения реализации метода, который создает бизнес-объект при его загрузке из базы данных. (Мы не можем использовать инструмент ORM, потому что мы обращаемся к другой системе через собственный API.)

Этот код подклассов и экземпляров не совсем ясен в Java и будет очень читабельным в Scala.