Как я писал некоторое время назад, я столкнулся с непонятным мне крайне медленным дисковым обменом при работе с файлами прямого (произвольного) доступа.
Сейчас у меня появилось немного времени и я решил проверить какова скорость работы с дисками нескольких типов файлового доступа. Очевидно, что тест не универсален, а ориентирован скорее на мою исходную задачу.
Имеется большой файл (порядка 100 миллионов значений) содержащий описание некоего спутникового снимка. Файл слишком велик, что бы постоянно держать его в памяти, а таких файлов несколько (в моем случае 11). С другой стороны, постоянное наличие файла в памяти не является необходимым. По отношению к файлу имеется несколько типов запросов:
1. Необходимо прочитать весь файл, что бы произвести отрисовку. Все читается последовательно.
2. По запросам (порядка 20 тысяч) извлечь из файла значение, соответсвующее определенным координатам. К сожалению запросы поступают в произвольном порядке и их упорядочение по строкам в файле невозможно.
Изначально формат файла текстовый. Для данного теств я в 100 раз уменьшил размеры для ускорения тестов. Перенос файлов на другой компьютер не предполагался.
Итак имеется текстовый файл «Source.txt» с заголовком
ncol = 1000
nrow = 1000
За которым следует 1000 строк в которых указано по 1000 значений типа double. Исходно у меня была идея, что после преобразования файла в бинарный вид чтение и запись будут быстрее. Посему я преобразовал исходный файл в бинарный с записью заголовка в начале в виде двух чисел типа double.
Проведенный тест.
Я сгенерировал массив из 1 000 000 действительных чисел. После чего произвел запись на диск этого массива несколькими видами файлового доступа, засекая время.
Произвел тест скорости последовательного чтения этих файлов.
Сгенерировал массив из 20 000 пар срока-столбец полученных генератором случайных чисел (для воспроизводимости результатов) и провел тест скорости получения данных при чтении в случайном порядке.
Тестировались следующие виды файлов (доступа)
1. Текстовые файлы – BufferedWriter(FileWriter), BufferedReader(FileReader).
2. Бинарные файлы
2.1. Простейшие бинарные файлы FileOutputStream, FileInputStream
2.2. Бинарные файлы с интерфейсом DataInput, DataOutput: DataOutputStream(FileOutputStream), DataInputStream(FileInputStream).
2.3. Бинарные файлы прямого доступа с использованием интерфейса DataInput, DataOutput.
2.4. Бинарные файлы прямого доступа без использованиея интерфейса DataInput, DataOutput
2.5. То же что 2.3 но с отображением файла в память.
Таким образом получилось 6 вариантов файлов.
Текст теста приведен ниже.
| Код |
package files;
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile;
import java.nio.ByteBuffer; import java.nio.DoubleBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel;
import java.util.Random; import java.util.StringTokenizer;
public class FileAccessTimeTest { private final int rows = 1000, cols = 1000, reads = 20000; private double[][] data = new double[rows][cols]; private int ccol = 0, rrow = 0; private byte[] bytes = new byte[8]; private ByteBuffer bb = ByteBuffer.wrap(bytes); private double[][] control = new double[rows][cols]; private int coords[][] = new int[reads][2]; private double[] contr = new double[reads]; private final String dir = "C:\\LocalData\\em322\\Apps\\Tests\\Files\\TestFile\\"; private static final int BYTE_IN_DBL = Double.SIZE / Byte.SIZE;
public static void main(String[] args) { new FileAccessTimeTest(); }
public FileAccessTimeTest() { //Data generation generateData(); bb.mark(); try { //Consequent write test textWrite(dir); simpleBinaryWrite(dir); dataInOutWrite(dir); rafSimpleWrite(dir); rafInOutWrite(dir); rafMapSimpleWrite(dir); //Consequent read test textRead(dir); simpleBinaryRead(dir); dataInOutRead(dir); rafSimpleRead(dir); rafInOutRead(dir); rafMapSimpleRead(dir); //Random order read test textReadRO(dir); simpleBinaryReadRO(dir); dataInOutReadRO(dir); rafSimpleReadRO(dir); rafInOutReadRO(dir); rafMapSimpleReadRO(dir); } catch (IOException e) { e.printStackTrace(); } }
private final void generateData() { Random rnd = new Random(); //Generate data to input/output for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) data[row][col] = rnd.nextDouble() * 1000; //Generate coordinates to random read test for (int r = 0; r < reads; r++) { coords[r][0] = rnd.nextInt(rows); coords[r][1] = rnd.nextInt(cols); } }
public byte[] toByteArray(double value) { bb.reset(); bb.putDouble(value); return bytes; }
public double fromByteArray() { bb.reset(); return bb.getDouble(); }
private final void textWrite(final String dir) throws IOException { System.out.print("Consequent write text "); long t0 = System.nanoTime(); BufferedWriter bw = new BufferedWriter(new FileWriter(dir + "Text.txt")); //Write header bw.write("ncols " + cols); bw.newLine(); bw.write("nrows " + rows); bw.newLine(); //Write data for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) bw.write(data[row][col] + " "); bw.newLine(); } bw.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); }
private final void simpleBinaryWrite(final String dir) throws IOException { System.out.print("Consequent write simple "); long t0 = System.nanoTime(); FileOutputStream fos = new FileOutputStream(dir + "simple.bin"); double d; //Write header d = cols; fos.write(toByteArray(d)); d = rows; fos.write(toByteArray(d)); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) fos.write(toByteArray(data[row][col])); fos.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); }
private final void dataInOutWrite(final String dir) throws IOException { System.out.print("Consequent write DataOutput "); long t0 = System.nanoTime(); DataOutputStream dos = new DataOutputStream(new FileOutputStream(dir + "data.bin")); double d; //Write header d = cols; dos.writeDouble(d); d = rows; dos.writeDouble(d); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) dos.writeDouble(data[row][col]); dos.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); }
private final void rafSimpleWrite(final String dir) throws IOException { System.out.print("Consequent write raf simple "); long t0 = System.nanoTime(); RandomAccessFile raf = new RandomAccessFile(dir + "raf simple.bin", "rw"); double d; //Write header d = cols; raf.write(toByteArray(d)); d = rows; raf.write(toByteArray(d)); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) raf.write(toByteArray(data[row][col])); raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); }
private final void rafInOutWrite(final String dir) throws IOException { System.out.print("Consequent write raf DataOutput "); long t0 = System.nanoTime(); RandomAccessFile raf = new RandomAccessFile(dir + "raf data.bin", "rw"); double d; //Write header d = cols; raf.writeDouble(d); d = rows; raf.writeDouble(d); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) raf.writeDouble(data[row][col]); raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); }
private final void rafMapSimpleWrite(final String dir) throws IOException { System.out.print("Consequent write raf simple map "); long t0 = System.nanoTime(); RandomAccessFile raf = new RandomAccessFile(dir + "raf simple map.bin", "rw"); raf.setLength(((long)(2 + cols * rows)) * BYTE_IN_DBL); MappedByteBuffer buffer = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0L, raf.length()); DoubleBuffer doubleBuffer = buffer.asDoubleBuffer(); double d; //Write header d = cols; doubleBuffer.put(d); d = rows; doubleBuffer.put(d); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) doubleBuffer.put(data[row][col]); buffer.force(); raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); }
private final void controlData() { if (rows != rrow || cols != ccol) System.out.println("Incorrect header"); for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) if (control[row][col] != data[row][col]) { System.out.println("Incorrect data in row " + row + " column" + col); return; } }
private final void textRead(final String dir) throws IOException { System.out.print("Consequent read text "); long t0 = System.nanoTime(); StringTokenizer stk; BufferedReader br = new BufferedReader(new FileReader(dir + "Text.txt")); //Write header String line, s; double d; line = br.readLine(); stk = new StringTokenizer(line); stk.nextToken(); //Skip title s = stk.nextToken(); d = Double.parseDouble(s); ccol = (int)d; line = br.readLine(); stk = new StringTokenizer(line); stk.nextToken(); //Skip title s = stk.nextToken(); d = Double.parseDouble(s); rrow = (int)d; //Write data for (int row = 0; row < rows; row++) { line = br.readLine(); stk = new StringTokenizer(line); for (int col = 0; col < cols; col++) control[row][col] = Double.parseDouble(stk.nextToken()); } br.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlData(); }
private final void simpleBinaryRead(final String dir) throws IOException { System.out.print("Consequent read simple "); long t0 = System.nanoTime(); FileInputStream fis = new FileInputStream(dir + "simple.bin"); //Write header fis.read(bytes); ccol = (int)fromByteArray(); fis.read(bytes); rrow = (int)fromByteArray(); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) { fis.read(bytes); control[row][col] = fromByteArray(); } fis.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlData(); }
private final void dataInOutRead(final String dir) throws IOException { System.out.print("Consequent read DataOutput "); long t0 = System.nanoTime(); DataInputStream dis = new DataInputStream(new FileInputStream(dir + "data.bin")); //Write header ccol = (int)dis.readDouble(); rrow = (int)dis.readDouble(); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) control[row][col] = dis.readDouble(); dis.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlData(); }
private final void rafSimpleRead(final String dir) throws IOException { System.out.print("Consequent read raf simple "); long t0 = System.nanoTime(); RandomAccessFile raf = new RandomAccessFile(dir + "raf simple.bin", "r"); //Write header raf.read(bytes); ccol = (int)fromByteArray(); raf.read(bytes); rrow = (int)fromByteArray(); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) { raf.read(bytes); control[row][col] = fromByteArray(); } raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlData(); }
private final void rafInOutRead(final String dir) throws IOException { System.out.print("Consequent read raf DataOutput "); long t0 = System.nanoTime(); RandomAccessFile raf = new RandomAccessFile(dir + "raf data.bin", "r"); //Write header ccol = (int)raf.readDouble(); rrow = (int)raf.readDouble(); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) control[row][col] = raf.readDouble(); raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlData(); }
private final void rafMapSimpleRead(final String dir) throws IOException { System.out.print("Consequent read raf simple map "); long t0 = System.nanoTime(); RandomAccessFile raf = new RandomAccessFile(dir + "raf simple map.bin", "r"); MappedByteBuffer buffer = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 0L, raf.length()); DoubleBuffer doubleBuffer = buffer.asDoubleBuffer(); //Write header ccol = (int)doubleBuffer.get(); rrow = (int)doubleBuffer.get(); //Write data for (int row = 0; row < rows; row++) for (int col = 0; col < cols; col++) control[row][col] = doubleBuffer.get(); buffer.force(); raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlData(); }
private final void controlDataRA() { for (int i = 0; i < reads; i++) if (contr[i] != data[coords[i][0]][coords[i][1]]) { System.out.println("Incorrect data in position " + i); return; } }
private final void textReadRO(final String dir) throws IOException { System.out.print("Random order read text "); long t0 = System.nanoTime(); StringTokenizer stk; String line; for (int i = 0; i < reads; i++) { //Reopen file BufferedReader br = new BufferedReader(new FileReader(dir + "Text.txt")); //Skip header lines br.readLine(); br.readLine(); //Skip preceeding rows for (int k = 0; k < coords[i][0]; k++) br.readLine(); //Read needed line line = br.readLine(); stk = new StringTokenizer(line); //Skip preceeding cols for (int k = 0; k < coords[i][1]; k++) stk.nextToken(); contr[i] = Double.parseDouble(stk.nextToken()); br.close(); } long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlDataRA(); }
private final void simpleBinaryReadRO(final String dir) throws IOException { System.out.print("Random order read simple "); long t0 = System.nanoTime(); long pos; for (int i = 0; i < reads; i++) { //Reopen file FileInputStream fis = new FileInputStream(dir + "simple.bin"); //Calculate number of double which must be skipped pos = (2+coords[i][0] * cols + coords[i][1]) * BYTE_IN_DBL; //Skip header while (pos != 0) pos -= fis.skip(pos); //Read current value fis.read(bytes); contr[i] = fromByteArray(); fis.close(); } long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlDataRA(); }
private final void dataInOutReadRO(final String dir) throws IOException { System.out.print("Random order read DataOutput "); long t0 = System.nanoTime(); long pos; for (int i = 0; i < reads; i++) { //Reopen file DataInputStream dis = new DataInputStream(new FileInputStream(dir + "data.bin")); //Calculate number of double which must be skipped pos = (2 + coords[i][0] * cols + coords[i][1]) * BYTE_IN_DBL; //Skip header while (pos != 0) pos -= dis.skip(pos); //Read current value contr[i] = dis.readDouble(); dis.close(); } long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlDataRA(); }
private final void rafSimpleReadRO(final String dir) throws IOException { System.out.print("Random order read raf simple "); long t0 = System.nanoTime(); long pos; RandomAccessFile raf = new RandomAccessFile(dir + "raf simple.bin", "r"); for (int i = 0; i < reads; i++) { //Calculate number of double which must be skipped pos = (2 + coords[i][0] * cols + coords[i][1]) * BYTE_IN_DBL; //Seek position raf.seek(pos); //Read current value raf.read(bytes); contr[i] = fromByteArray(); } raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlDataRA(); }
private final void rafInOutReadRO(final String dir) throws IOException { System.out.print("Random order read raf DataOutput "); long t0 = System.nanoTime(); long pos; RandomAccessFile raf = new RandomAccessFile(dir + "raf data.bin", "r"); for (int i = 0; i < reads; i++) { //Calculate number of double which must be skipped pos = (2 + coords[i][0] * cols + coords[i][1]) * BYTE_IN_DBL; //Seek position raf.seek(pos); //Read current value contr[i] = raf.readDouble(); } raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlDataRA(); }
private final void rafMapSimpleReadRO(final String dir) throws IOException { System.out.print("Random order read raf simple map "); long t0 = System.nanoTime(); int pos; RandomAccessFile raf = new RandomAccessFile(dir + "raf simple map.bin", "r"); MappedByteBuffer buffer = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 0L, raf.length()); DoubleBuffer doubleBuffer = buffer.asDoubleBuffer(); for (int i = 0; i < reads; i++) { //Calculate number of double which must be skipped pos = 2 + coords[i][0] * cols + coords[i][1]; //Read current value contr[i] = doubleBuffer.get(pos); } buffer.force(); raf.close(); long t1 = System.nanoTime(); System.out.println("\t" + (t1 - t0)); controlDataRA(); }
}
|
Результаты теста следующие (время в секундах):
Последовательная Последовательное Чтение в
запись чтение случайном порядке
Текстовый файл 1.993 0.867 1,343.343
Простейшие бинарные 7.289 4.967 3.683
Бинарные с интерфейсом DataOutput 13.611 4.904 3.945
Прямого доступа простые 7.639 4.955 0.165
Прямого доступа с интерфейсом DataOutput 54.617 37.470 0.844
Прямого доступа с отображением в памяти 0.107 0.020 0.006
Получается, что при последовательном доступе самые быстрые из файлов без отображения в памяти являются текстовые файлы, что полностью противоречит моим воспоминаниям о работе с файлами в Дельфи.
Самые быстрые - файлы с отображением в памяти. Это не вызывает вопросов. А вот почему бинарные файлы настолько медленнее текстовых вогнало меня в ступор.
Это сообщение отредактировал(а) Mirkes - 14.4.2014, 19:57