Операция чтения с диска выполняется очень медленно | Java Stream

Мне нужно прочитать изображения из папки и сгенерировать для них контрольную сумму. Есть около 330760 изображений. Ниже приведен код:

package com.test;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import org.apache.commons.codec.digest.DigestUtils;

public class FileTest2 {

    private void readFiles() throws IOException {
        try (Stream<Path> filePathStream = Files
                .walk(Paths.get("d:\\codebase\\images"))) {
            filePathStream.parallel().forEach(filePath -> {
                String checksumSHA256 = "";
                try {
                    checksumSHA256 = DigestUtils.sha384Hex(new FileInputStream(filePath.toString()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (Files.isRegularFile(filePath)) {
                    System.out.println(checksumSHA256);
                    System.out.println(filePath);
                    System.out.println("\n");

                }
            });
        }
    }

    public static void main(String[] args) throws IOException {
        long startTime = System.currentTimeMillis();
        FileTest2 fileTest = new FileTest2();
        fileTest.readFiles();
        long endTime = System.currentTimeMillis();
        System.out.println("Total Time took: " + (endTime - startTime) / 1000);
    }
}

На это ушло около 36 минут.

Конфигурация системы:

Количество ядер: 8
Память: 32 ГБ (свободно 15-17 ГБ). Остальная часть памяти используется другим сервером.

36 минут - это слишком много. Есть ли способ повысить производительность?

Похоже, что на самом деле это не заняло так много времени, поскольку я могу видеть все записи / пути к именам файлов в текстовом файле. Однако код все еще выполняется. Я пропустил закрытие какого-либо ресурса здесь?

miserable 16.10.2018 20:37

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

Andreas 16.10.2018 20:40

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

Andreas 16.10.2018 20:43

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

miserable 16.10.2018 20:50

Что значит мой код ничего не делает? Он читает все файлы / изображения из файловой системы и выводит имя на консоль.

miserable 16.10.2018 20:50

Зачем вообще беспокоиться о пуле потоков? Вы пробовали filePathStream.parallel().forEach? Кроме того, это неправильный тест.

Jacob G. 16.10.2018 20:52

@PuneetJain Код не делает ничего, что потребляет много памяти. Вы печатаете все имена файлов по одному, поэтому в памяти одновременно находится только одно имя. Вы не читаете файлы (контент), а только их имена, так почему вы думаете, что код будет использовать много памяти?

Andreas 16.10.2018 20:54

Вы пробовали использовать VisualVM или любой другой профилировщик Java для проверки потребления памяти вместо диспетчера задач Windows? Также вы используете ключ -Xms при запуске приложения Java?

Ivan 16.10.2018 20:57

@ Иван - Еще нет. Но планирую использовать.

miserable 16.10.2018 21:11

@ JacobG. Не пробовал параллельно. Можете попробовать

miserable 16.10.2018 21:12

Обновил мой код. Генерация контрольной суммы для всех изображений.

miserable 16.10.2018 21:12

@ JacobG. Я только что закончил пробовать с параллельным сервером, и это заняло: 2086 секунд.

miserable 16.10.2018 21:59

Почему вы тестируете Files.isRegularFile(filePath)после, вы рассчитали для него контрольную сумму? Не поздно ли поставить под сомнение эту собственность? Кроме того, не рекомендуется выполнять три отдельных оператора печати с потоком параллельный, когда каждый другой поток мог бы выполнять свои собственные операторы печати между ними. Кроме того, почему вы идете по старому обходному пути ввода-вывода, то есть new FileInputStream(filePath.toString()), вместо использования Files.newInputStream(filePath)?

Holger 17.10.2018 10:18
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
13
234
1

Ответы 1

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

public static void main(String[] args) throws Exception {
    long startTime = System.currentTimeMillis();

    FileTest fileTest = new FileTest();
    fileTest.readFiles();

    long endTime = System.currentTimeMillis();
    System.out.println("Total Time took: "+ (endTime-startTime)/1000);
}

Примечание: по крайней мере, из того фрагмента кода, который вы опубликовали, нет причин использовать ExecutorService

Только что обновил свой код. Генерация контрольной суммы для всех изображений.

miserable 16.10.2018 21:12

Пожалуйста, запустите предложенный мной код и сообщите значение «Общее время:»

David Soroko 16.10.2018 21:19

Я только что закончил параллель, и это заняло: 2086 секунд. filePathStream.parallel (). forEach (filePath -> {} Еще пробовать без параллелизма, но я уверен, что это было бы ужасно,

miserable 16.10.2018 21:58

Прокомментируйте, пожалуйста, распечатанные утверждения и укажите значение «Общее время:»

David Soroko 16.10.2018 22:05

Кроме того, с реализацией sha вы измеряете не только время, необходимое для обхода файловой системы, но также время, необходимое для чтения каждого файла.

David Soroko 16.10.2018 22:11

Хорошо. Прокомментирую и попробую. В настоящее время пытаюсь без parallel (). Для SHA - по сути, мой код будет генерировать sha для всех изображений и сохранять их в базе данных. Мне нужно запускать этот код один раз в день. Так что я стараюсь добиться максимальной производительности. Я считаю, что это замедлится еще больше, если я сохраню SHA в базе данных.

miserable 16.10.2018 22:32

Заняло 9988 секунд без parallel (). Пробую без println сейчас.

miserable 17.10.2018 00:59

2137 секунд без println и с parallel ().

miserable 17.10.2018 02:14

Итак, около 7 мсек. на файл. Вы можете улучшить ситуацию, используя BufferedInputStream

David Soroko 17.10.2018 15:26

Другие вопросы по теме