Статьи

Производительность Scala Tuple

Tuple — очень мощная конструкция в языке программирования, она позволяет создавать последовательность конечных элементов. Элементы в кортеже могут быть различного типа и очень легко объявляться как («что-то», 1, новый Date ())

Хорошая вещь в кортеже — вы должны выбирать только тип данных элемента, а не имя.

Информатика имеет 2 трудные проблемы: аннулирование кэша и присвоение имен.

Кортеж помогает с проблемой именования.

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

Каковы разные способы реализации кортежа?

Класс с массивом объектов

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

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

Класс с фиксированным номером параметра.

Это лучше, чем первый, но у него тоже мало проблем

  • Параметр имеет тип объекта, поэтому оказывает давление на память
  • Изменяемый, если фреймворк или библиотека не генерируют код или не поддерживают объекты с фиксированным макетом, такие как (Tuple, Tuple2, Tuple3…)
  • Затраты на сериализацию из-за типа объекта.

Scala использует фиксированное число параметров подхода.

В отсутствие Tuple выбор бедного человека состоял в том, чтобы создать класс с числом переменных экземпляра N и дать им правильный тип, в scala есть класс Case, основанный на старой школьной мысли.

Давайте сравним класс Tuple и Case. Я возьму кортеж с 4 параметрами и 2 из них являются примитивными (Int & Double).

Кортеж: (String, String, Int, Double)

Класс case : класс case Trade (символ: String, обмен: String, кол-во: Int, цена: Double)

По структуре они одинаковы и могут заменять друг друга.

Тест памяти

Бенчмарк создает N экземпляра класса tuple / case и помещает его в коллекцию и измеряет распределение памяти.

скала кортеж

Использование памяти для Tuple удваивается, для 5 миллионов объектный кортеж занимает 384 МБ, а класс case занимает всего 189 МБ.

Читать тест производительности

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

скала кортеж

На этом графике показано время, необходимое для суммирования значения Double для объекта 1 миллион, 5 миллионов и т. Д.

Чтение из кортежа происходит медленно, оно занимает вдвое больше времени.

Одна вещь, которая не показана в этой диаграмме, это давление памяти, создаваемое во время чтения. Кортеж оказывает давление на память во время чтения.

Эти цифры показывают, что Tuple не подходит как для памяти, так и для процессора.

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

Я поставлю код Java, который генерируется компилятором Scala.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/*
 Code for (String,String,Int,Double)
*/
public class Tuple4<T1, T2, T3, T4> implements Product4<T1, T2, T3, T4>, Serializable
{
  private final T1 _1;
   
  public T1 _1(){return (T1)this._1;}
  public T2 _2(){
    return (T2)this._2;
  }
  public T3 _3(){
    return (T3)this._3;
  }
  public T4 _4(){
    return (T4)this._4;
  }
}

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

Код класса дела

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/*
Code for
case class Trade(symbol: String, exchange: String, qty: Int, price: Double)
*/
public class Trade implements Product, Serializable
{
  private final String symbol;
  public double price(){
    return this.price;
  }
  public int qty(){
    return this.qty;
  }
  public String exchange(){
    return this.exchange;
  }
  public String symbol(){
    return this.symbol;
  }
   
}

Для кода caseclass scala по-прежнему использует примитивный тип, что дает всю эффективность использования памяти и процессора.

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

Преобразование на основе кортежей создает большую нагрузку на ГХ и влияет на загрузку ЦП.

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

Информация о типе очень важна для оптимизации, и именно поэтому Spark 2 основан на наборе данных, который имеет компактное представление.

Поэтому в следующий раз в поисках быстрого улучшения поменяйте Tuple на case case.

Код, используемый для бенчмаркинга доступен @ githib

Смотреть оригинальную статью здесь: производительность Scala Tuple

Мнения, высказанные участниками Java Code Geeks, являются их собственными.