DevGang
Авторизоваться

3 эффективных функции Java для настройки производительности вашего приложения Java 

Вы хотите знать, улучшает ли List::of производительность? Вы хотите знать, как работают методы частного интерфейса? Вы хотите знать, что делает новый HTTPClient?

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

1. Удобные фабричные методы для коллекций (JEP-269)

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

В Java 8 нет удобного способа создавать небольшие коллекции. Хотя есть много библиотек со вспомогательными методами. Что происходит, когда у вас много вариантов? Множество произвольно выбранных опций приводят к непоследовательности кода. Как новые версии Java решают эту проблему? Они добавляют простой, удобный и эффективный способ создания коллекций.

// new way to create collections
Set<String> set = Set.of("a", "b", "c");

Другая проблема. Многословие. Добавление элементов в коллекцию происходит с высокой степенью детализации. Больше подробностей - больше шаблонного кода. Как Java 9 снижает многословие? Java 9 предоставляет простые методы создания коллекций. До Java 9 было много шаблонов для создания простой коллекции.

Вот исследования и результаты тестов производительности.

Исследование

Вот мое дополнение к JEP-269. Мы воспользуемся этим тестом и посмотрим, как JEP-269 ускоряет обработку небольших коллекций.

В чем суть теста? У вас есть несколько способов создания коллекций. Если у вас есть простая модель, Cat, вам нужно добавить их в файл catsNonJava9Way. Создайте экземпляр каждого и добавьте их в цикл. Когда модели простые, содержащие одну строку, вы можете удалить экземпляры. Это catsJava8Way.  catsJava9Way показывает более новый способ Java.

Вот как новая Java улучшает небольшие коллекции.

Пропускная способность лучше с подходом Java 9. Нет создания временных объектов. То же самое происходит и в среднем по времени.

Пропускная способность с подходом Java 9 лучше в сотни раз.
Пропускная способность с подходом Java 9 лучше в сотни раз.
Среднее время на подходе Java 9 также лучше в сотни раз.
Среднее время на подходе Java 9 также лучше в сотни раз.

Что тормозит предыдущие подходы Java? Размещение временного объекта. Две трети времени уходит на создание объекта. Удаление переменной new Cat ускоряет выполнение. Избегайте использования временных переменных. Требуется время, чтобы выделить, собрать мусор и назначить их.

После удаления ненужной переменной
После удаления ненужной переменной

Brian Goetz предполагает, что подход Java 8 приносит больше вреда, чем пользы. Вредит читабельности кода, пропускной способности, но не в среднем по времени.

Проведем сравнение. После тестов среднее время Java 9 не превосходит подход Java 8. Среднее время незначительно, так как оно в несколько раз медленнее.

Java 8 лучше, чем Java 9 в среднем по времени
Java 8 лучше, чем Java 9 в среднем по времени

Java 9 имеет лучшую пропускную способность, чем подход Java 8. Это означает, что Java 9 выполняет больше операций в секунду, чем подход Java 8. Пропускная способность незначительна.

Java 9 превосходит производительность Java 8
Java 9 превосходит производительность Java 8

Какой вывод? Выбирать Java 9 или Java 8 по производительности не имеет смысла. Они оба неплохо выступают. Вы должны выбрать то, что делает код чище. Выбор за вами.

cats8 = Collections.unmodifiableList(Stream.of("George", "John", "Mack", "Other", "Lorem").collect(Collectors.toList()));
// or this one
cats9 = List.of("George", "John", "Mack", "Other", "Lorem");

IntelliJ по умолчанию предлагает более чистый способ. Более чистый способ создания коллекций.

IntelliJ уведомляет об использовании вместо этого подхода Java 9
IntelliJ уведомляет об использовании вместо этого подхода Java 9

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

JEP-269 не ориентирован на большие коллекции. Это не цель. Вот тест, который я использую, чтобы доказать это.

Нецелевое задание JEP-269 - контрольный показатель
// excerpt from the benchmark

@Warmup(iterations = 50, time = 2, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 3, time = 2, timeUnit = TimeUnit.MILLISECONDS)
@Fork(1)
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode({ Mode.Throughput})
public class Collections9 {
  private static final int LIST_LENGTH = 100000;
  private List<String> strings = new ArrayList<>(LIST_LENGTH);
  private static Set<String> hugeCatsSet;

  @Setup
  public void setupTest(){
    for(int i = 0; i < LIST_LENGTH; i++) {
      strings.add(UUID.randomUUID().toString());
    }
  }

  @Benchmark
  public void catsJava8Way_HugeCollections() {
    hugeCatsSet = new HashSet<>(strings);
  }

  @Benchmark
  public void catsJava9Way_HugeCollections() {
    hugeCatsSet = strings.stream().collect(Collectors.toUnmodifiableSet());
  }

}

Java 8 работает лучше, чем Java 9, с большими коллекциями.

Java 8 работает даже лучше, чем подход Java 9+
Java 8 работает даже лучше, чем подход Java 9+

Выводы

Java 9 дает ясный, краткий и эффективный способ создания коллекций. Все используют один и тот же API, например List.of, что упрощает использование. По моим оценкам, это ускоряет создание коллекции порядка сотни раз.

Большие коллекции лучше работают с Java 8. Целью этого изменения не было улучшение обработки больших коллекций. После сравнительного анализа мы видим, что нецелевые цели достигнуты. Нет никаких улучшений в работе с большими коллекциями.

2. Методы частного интерфейса (JEP-213)

Основная проблема состоит в использование методов копирования и вставки в интерфейсе. Подобный код копируется снова и снова. Используя частные методы, вы можете поместить общий код в частный метод.

Копипаст, когда у вас нет частного метода
public interface CatInterface {

	default void walk() {
		System.out.println("walk");
		System.out.println("walk");
		System.out.println("walk");
		System.out.println("walk");
		System.out.println("walk");
	}

	default void run() {
		System.out.println("run");
		System.out.println("run");
		System.out.println("run");
		System.out.println("run");
		System.out.println("run");
	}

	default void fastWalk() {
		System.out.println("fastWalk");
		System.out.println("fastWalk");
		System.out.println("fastWalk");
		System.out.println("fastWalk");
		System.out.println("fastWalk");
	}

	default void retreat() {
		System.out.println("retreat");
		System.out.println("retreat");
		System.out.println("retreat");
		System.out.println("retreat");
		System.out.println("retreat");
	}

	default void fastRetreat() {
		System.out.println("fastRetreat");
		System.out.println("fastRetreat");
		System.out.println("fastRetreat");
		System.out.println("fastRetreat");
		System.out.println("fastRetreat");
	}
}

Project Coin добавил незначительные улучшения в Java 7. Теперь он включен в LTS Java. Проверьте JEP-213 для получения более подробной информации о проекте Coin.

Метод частного интерфейса в Java 9
package de.jawb.jmh.benchmark.example.java9features.model;

public interface CatInterface9 {

	default void walk() {
		doTheCalculus("walk");
	}

	default void run() {
		doTheCalculus("run");
	}

	default void fastWalk() {
		doTheCalculus("fastWalk");
	}

	default void retreat() {
		doTheCalculus("retreat");
	}

	default void fastRetreat() {
		doTheCalculus("fastRetreat");
	}

	private void doTheCalculus(String pace)
	{
		System.out.println(pace);
		System.out.println(pace);
		System.out.println(pace);
		System.out.println(pace);
		System.out.println(pace);
	}
}

Исследование

Java 9 работает лучше. Теперь у нас есть две причины использовать частные методы:

  1. Представление
  2. Читаемость кода

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

Почему происходит повышение производительности? Мое скромное мнение таково, что частный метод становится «горячим». Вот результаты тестов.

Cats в Java 9 мяукает лучше
Cats в Java 9 мяукает лучше
Cats в Java 9 немного быстрее
Cats в Java 9 немного быстрее

Выводы

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

Еще одно преимущество частных методов - производительность. Пропускная способность и среднее время лучше в подходе Java 9. Частный метод становится «горячим» и в среднем работает лучше.

3. Способ резерва для новых HTTP-клиентов (JEP-110)

Brian Goetz предполагает, что устаревшие HTTP-запросы закончились. JDK 1.1 сначала добавляет поддержку HTTP-запросов. Вплоть до JDK 9 вы могли использовать устаревшие Gopher и FTP. Без Java 9 HTTP-клиент неэффективен, так как он содержит множество устаревших функций.

До Java 9 запросы HTTP не поддерживали HTTP / 2. В будущих версиях Java HTTPClient станет де-факто HTTP-клиентом в Java.

Устаревшие собственные клиенты были медленными, трудными в использовании и не имели документации. Вы все еще можете найти активные вопросы StackOverflow о HTTP-запросах. JEP-110 открывает путь к лучшему HTTPClient.

Исследование

Вот как вы могли бы использовать новый класс HTTPUrlConnection.

Старый подход - взято из Mastering Java 9 с небольшими изменениями
public void urlTests() throws IOException {
		URL theUrl = null;
		BufferedReader theReader = null;
		StringBuilder theStringBuilder;
		// put the URL into a String
		String theUrlS = "https://www.packtpub.com/";
		// here we are creating the connection
		theUrl = new URL(theUrlS);
		HttpURLConnection theConnection = (HttpURLConnection)
				theUrl.openConnection();
		theConnection.setRequestMethod("GET");
		// add a delay
		theConnection.setReadTimeout(30000); // 30 seconds
		theConnection.connect();
		// next, we can read the output
		theReader = new BufferedReader(
				new InputStreamReader(theConnection.getInputStream()));
		theStringBuilder = new StringBuilder();
		// read the output one line at a time
		String theLine = theReader.readLine();
		while (theLine != null)
		{
			theStringBuilder.append(theLine).append('\n');
			theLine = theReader.readLine();
		}

		// echo the output to the screen console
	 System.out.println(theStringBuilder.toString());
		// close the reader
	 theReader.close();
}

Вот новый подход с инкубатором HTTPClient. В настоящее время инкубированный HTTPClient - это часть java.net.http.

Мы можем еще раз проверить этот вопрос. Выделяется один ответ. Это способ HTTP в Java 9. Сравним принятый ответ и этот. Подход Java 9 делает больше с меньшим количеством кода.

ответ для Java 9
// GET
HttpResponse response = HttpRequest
    .create(new URI("http://www.stackoverflow.com"))
    .headers("Foo", "foovalue", "Bar", "barvalue")
    .GET()
    .response();

Для HTTPUrlConnection нет асинхронности. Вам нужно взломать все, чтобы установить асинхронность. В новом HTTPClient это встроено.

// https://docs.oracle.com/en/java/javase/13/docs/api/java.net.http/java/net/http/HttpClient.html
   HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://foo.com/"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();
   client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println); 

Выводы

HTTPClient это новый способ выполнения HTTP-запросов. Поддерживает новый протокол HTTP, создает краткий код и актуален.

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

В заключение

Более новая Java предлагает новые и удобные методы для создания коллекций. Основное внимание уделяется коллекциям меньшего размера, как вы можете видеть в наших тестах производительности. Использование статических фабричных методов Java 9 снижает многословие и повышает производительность.

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

Brian Goetz поддерживает новейший HTTPClient. Это открывает путь для асинхронных запросов. Поддержка более новых версий HTTP. В конце концов, лучший опыт разработчика.

Даже после этих изменений большинство людей используют Java 8. Вам следует перейти на Java 11, поскольку она также поддерживает LTS. Не только LTS, но и все функции, которые вы видели в этой статье.

Источник:

#Java
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

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

Попробовать

В подарок 100$ на счет при регистрации

Получить