Scala быстрый текстовый файл для чтения и загрузки в память

В Scala для чтения текстового файла и загрузки его в массив используется общий подход:

scala.io.Source.fromFile("file.txt").getLines.toArray

Существует ли более быстрый подход, особенно для очень больших файлов: сначала считывать блоки байтов в память, а затем разбивать их по символам новой строки? (См. Читать весь файл в Scala для часто используемых подходов.)

Большое спасибо.

1 ответ

Решение

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

// measures time taken by enclosed code
def timed[A](block: => A) = {
  val t0 = System.currentTimeMillis
  val result = block
  println("took " + (System.currentTimeMillis - t0) + "ms")
  result
}

val source = timed(scala.io.Source.fromFile("test.txt")) // 200mb, 500 lines
// took 0ms

val lines = timed(source.getLines)
// took 0ms

timed(lines.next) // read first line
// took 1ms

// ... reset source ...

var x = 0
timed(lines.foreach(ln => x += ln.length)) // "use" every line
// took 421ms

// ... reset source ...

timed(lines.toArray)
// took 915ms

Учитывая скорость чтения 500 МБ в секунду для моего жесткого диска, оптимальное время было бы 400 мс для 200 МБ, что означает, что нет места для улучшений, за исключением не преобразования итератора в массив.

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


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

source.getLines.map(_.split(",").map(_.trim.toInt)).toArray

значительно быстрее, чем

source.getLines.toArray.map(_.split(",").map(_.trim.toInt))

(Для меня это 1,9 с вместо 2,5 с), потому что вы не преобразуете весь гигантский массив в другой, а просто каждую строку в отдельности, заканчивая одним массивом (использует только половину пространства кучи). Кроме того, поскольку чтение файла является узким местом, преобразование во время чтения имеет то преимущество, что приводит к лучшей загрузке процессора.

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