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

Я выполняю некоторые задания на CodeEval. В основном задача очень проста: "Распечатайте сумму всех целых чисел, считанных из файла".

Мое решение следующее:

import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;

public class SumIntegersFromFile {

    public static void main(String args[]) throws IOException{

        File file = new File(args[0]);
         BufferedReader br = new BufferedReader( new FileReader(file));
         String line;
         int i=0;
         while((line=br.readLine())!=null){
            int k = Integer.parseInt(line);
             i+=k;
         }
         br.close();
         System.out.println(i);
    }
}

Но мне сказали, что это решение не оптимально с точки зрения производительности.

Код основан на рекомендациях в вопросе Лучший способ прочитать текстовый файл. Единственное отличие здесь в том, что я читаю целые числа вместо строк.

Каков наиболее эффективный способ чтения целых чисел из файла на Java?

Ответы

Ответ 1

Если вам явно не указано иначе, вы не должны предполагать, что общее количество будет соответствовать int. Попробуйте изменить тип i на long или даже BigInteger и посмотреть, не влияет ли это на ваш счет.

Вы можете попробовать сделать то же самое с k (и используя Long.parseLong(line)). Это будет зависеть от точной формулировки вопроса, но, возможно, отдельные значения могут превышать пределы int.

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

(И, предположительно, вам сказали, что это одна запись в строке...)

Но если вы хотите выжать каждый последний бит производительности, вам нужно прочитать файл как двоичный, а не строковый: переключение каждой строки в String просто слишком дорого. Подробный отчет о том, как это сделать, можно найти в этом вопросе о суммировании целых чисел из текстового файла.

Ответ 2

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

Чтение данных из файлов или по сети происходит на несколько порядков медленнее, чем манипулирование данными в памяти. Таким образом, производительность кода, который смешивает операции ввода-вывода с некоторыми манипуляциями с данными в памяти, обычно занимает время, затраченное на ввод-вывод. Режимы манипулирования данными в памяти редко бывают полезны. Если операции ввода-вывода выполняются параллельно с манипулированием данными (что будет иметь место, если O/S выполняет некоторые операции с чтением), обработка данных может быть практически бесплатной: ускорение обработки данных не приведет к сокращению времени, уменьшение времени процессора для обработки данных будет точно компенсировано увеличением количества времени, в течение которого программа блокируется при ожидании ввода.

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

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

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