PDA

View Full Version : [Java] Mi aiutate ad ottimizzare l'I/O questo breve programma?


s12a
22-02-2009, 16:16
Salve.
Premetto che non sono una cima quanto a programmazione, mi diletto semplicemente ogni tanto a creare qualche applicazione a me utile.

Ma passiamo al dunque. Ho creato un breve programma in Java da riga di comando che sostanzialmente fa le seguenti cose:

- Legge un file binario dalla struttura definita
- Inserisce tutti i valori in un array ed esegue alcune operazioni sopra di essi
- Riscrive tutti i valori nell'ordine in cui sono stati prelevati dal file di input in un file di output anch'esso binario

Il problema che ho e` che le operazioni di input/output con i file sono piuttosto lente. Tutto il processo di scrittura e lettura di circa 4 milioni di elementi impiega oltre un minuto di tempo su un Athlon X2 5200+ con hard disk Western Digital 6400AAKS, francamente mi sembra un po' troppo considerando che vorrei poter essere in grado di utilizzare agevolmente anche file 10 volte piu` grandi. La fase della manipolazione dei dati invece e` piuttosto veloce. Io ho il dubbio che il problema risieda nelle funzioni utilizzate, probabilmente non velocissime. Sono effettivamente quelle piu` semplici e le prime che ho trovato e che facevano al caso mio; magari esiste qualcosa di piu` ottimizzato ma meno intuitivo.

Allego qui il codice del programma. Per ragioni pratiche, visto che non ho intenzione di tenere segreto il codice, i commenti sono in inglese. Se avete qualche dubbio su qualche parte non esitate a chiedermi.

Non fate inoltre caso ad eventuali usanze da niubbo del linguaggio di programmazione o alla mancanza di controlli di integrita` dei dati o cose del genere. Il programma e` estremamente semplificato, assolutamente non completo, ed al momento mi interessa piu` che altro sapere se e` possibile aumentare la velocita` delle operazioni di I/O.

package qsaconverter;
import java.io.*;

public class Main {

public static void main(String[] args) {
// Input file stream initialization
File file = new File(args[2]);
FileInputStream fis;
DataInputStream ds;

// Output file stream initialization
File fileout = new File(args[2] + ".bin");
FileOutputStream fos;
DataOutputStream out;

// Binary SAC files have a header of 632 bytes which is disregarded.
// The rest of the file is composed of 32 bit floating point words.
// Size is the number of elements we're going to read from the file
final int size = (int)((file.length()-632)/4);

// Creating an array as big as the number of elements contained in the input file
double temparray[] = new double[size];

double maxtemp = 0;
double sum = 0;
double volume = Double.parseDouble(args[0]);
double timestart, timeend, timetotal;

// Start time, for benchmarking purposes
timestart = System.currentTimeMillis();

try {
// Creation of a new input stream
fis = new FileInputStream( file );
ds = new DataInputStream( fis );

// Creation of a new output stream
fos = new FileOutputStream ( fileout );
out = new DataOutputStream ( fos );

System.out.println("- Loading " + size + " elements from \"" + file + "\" and finding maximum absolute value");

// Skipping the 632 bytes header (composed of 158 words of 4 bytes each)
ds.skipBytes(632);

// Loading floating point data words in the previously created array
// Their number is already determinated by the "size" constant
for (int i = 0; i<size; i++) {
temparray[i] = ds.readFloat();

// Finding the maximum absolute value
if (Math.abs(temparray[i]) > maxtemp) {maxtemp = Math.abs(temparray[i]);}
}

System.out.println("- Maximum absolute value: " + maxtemp);

// Calculating the array average value, if args[1] is less than 0
if (Double.parseDouble(args[1]) < 0) {
for (int i = 0; i<size; i++) {sum = sum + temparray[i];}
sum = sum / size;
System.out.println("- Average array value: " + sum);
}

// If args[1] is greater than 0, the array average value is overridden to a custom value
else {
sum = Double.parseDouble(args[1]);
System.out.println("- Maximum absolute value overridden: " + sum);
}

// Array values are vertically adjusted to zero and normalized to +/- peak volume
System.out.println("- Adjusting offset and normalizing maximum peak to +/- " + volume);
for (int i = 0; i<size; i++) {temparray[i] = (temparray[i] - sum)/maxtemp*volume;}

// The whole array of double values is finally dumped to a binary file
System.out.println("- Writing results to raw 64bit float audio file \"" + fileout + "\"");
for (int i = 0; i<size; i++) {out.writeDouble(temparray[i]);}

// Closing opened files
ds.close();
out.close();
}
catch (IOException iof) {System.out.println("- IO error: " + iof);}

// Elapsing processing time, for benchmarking purposes
timeend = System.currentTimeMillis();
timetotal = (timeend - timestart)/1000;

System.out.println("- Done in " + timetotal + " seconds. Please import in Audacity as RAW audio file");

}
}

Questo e` l'output da riga di comando con un file di relativamente piccole dimensioni:

C:\Users\Enter\Desktop\sac>java -jar qSAConverter.jar 0.75 -1 C:\Users\Enter\Desktop\sac\IRIS_BudDataCenter.2009.53.0.0.0.AV.RSO..EHZ.SAC
- Loading 4068787 elements from "C:\Users\Enter\Desktop\sac\IRIS_BudDataCenter.2009.53.0.0.0.AV.RSO..EHZ.SAC" and finding maximum absolute value
- Maximum absolute value: 2048.0
- Average array value: -4.330911890939486
- Adjusting offset and normalizing maximum peak to +/- 0.75
- Writing results to raw 64bit float audio file "C:\Users\Enter\Desktop\sac\IRIS_BudDataCenter.2009.53.0.0.0.AV.RSO..EHZ.SAC.bin"
- Done in 71.583 seconds. Please import in Audacity as RAW audio file

Qui un file SAC binario di esempio compresso in formato zip di piccole dimensioni che potete utilizzare e convertire con il programma (da usare decompresso):

http://www.mediafire.com/?sharekey=8ee25015fd3d22e70dec85adfe0a530aada087815a1020215621d66e282a0ee8

AnonimoVeneziano
22-02-2009, 16:38
Prova ad aggiungere una BufferedInputStream e una BufferedOutputStream a tutto e vedere se migliori la situazione.

In pratica l'aggiungi tra la FileInputStream e la DataInputStream

s12a
22-02-2009, 16:48
Prova ad aggiungere una BufferedInputStream e una BufferedOutputStream a tutto e vedere se migliori la situazione.

In pratica l'aggiungi tra la FileInputStream e la DataInputStream

Grazie mille per la dritta! :)
Il buffered stream ha fatto miracoli, con un file da 797916 elementi il tempo di esecuzione e` passato da circa 14 secondi a 0.184 secondi! :D
Quello dell'esempio a fine post da oltre 4 milioni di elementi e` passato da 71.583 secondi a 1.127 secondi :eek::eek: