J'essaie d'illustrer la différence de performance entre les E/S traditionnelles et les fichiers mappés en mémoire dans Java pour les étudiants. J'ai trouvé un exemple quelque part sur internet mais tout n'est pas clair pour moi, je ne pense même pas que toutes les étapes soient de nature. Je lis beaucoup à ce sujet ici et là, mais je ne suis pas convaincu d'une mise en œuvre correcte de l'un ni de l'autre.IO traditionnel vs mappé en mémoire
Le code que j'essaie de comprendre est:
public class FileCopy{
public static void main(String args[]){
if (args.length < 1){
System.out.println(" Wrong usage!");
System.out.println(" Correct usage is : java FileCopy <large file with full path>");
System.exit(0);
}
String inFileName = args[0];
File inFile = new File(inFileName);
if (inFile.exists() != true){
System.out.println(inFileName + " does not exist!");
System.exit(0);
}
try{
new FileCopy().memoryMappedCopy(inFileName, inFileName+".new");
new FileCopy().customBufferedCopy(inFileName, inFileName+".new1");
}catch(FileNotFoundException fne){
fne.printStackTrace();
}catch(IOException ioe){
ioe.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}
}
public void memoryMappedCopy(String fromFile, String toFile) throws Exception{
long timeIn = new Date().getTime();
// read input file
RandomAccessFile rafIn = new RandomAccessFile(fromFile, "rw");
FileChannel fcIn = rafIn.getChannel();
ByteBuffer byteBuffIn = fcIn.map(FileChannel.MapMode.READ_WRITE, 0,(int) fcIn.size());
fcIn.read(byteBuffIn);
byteBuffIn.flip();
RandomAccessFile rafOut = new RandomAccessFile(toFile, "rw");
FileChannel fcOut = rafOut.getChannel();
ByteBuffer writeMap = fcOut.map(FileChannel.MapMode.READ_WRITE,0,(int) fcIn.size());
writeMap.put(byteBuffIn);
long timeOut = new Date().getTime();
System.out.println("Memory mapped copy Time for a file of size :" + (int) fcIn.size() +" is "+(timeOut-timeIn));
fcOut.close();
fcIn.close();
}
static final int CHUNK_SIZE = 100000;
static final char[] inChars = new char[CHUNK_SIZE];
public static void customBufferedCopy(String fromFile, String toFile) throws IOException{
long timeIn = new Date().getTime();
Reader in = new FileReader(fromFile);
Writer out = new FileWriter(toFile);
while (true) {
synchronized (inChars) {
int amountRead = in.read(inChars);
if (amountRead == -1) {
break;
}
out.write(inChars, 0, amountRead);
}
}
long timeOut = new Date().getTime();
System.out.println("Custom buffered copy Time for a file of size :" + (int) new File(fromFile).length() +" is "+(timeOut-timeIn));
in.close();
out.close();
}
}
Quand exactement est-il nececary utiliser RandomAccessFile
? Ici, il est utilisé pour lire et écrire dans le memoryMappedCopy
, est-il vraiment nécessaire de simplement copier un fichier? Ou est-ce une partie de la cartographie des souvenirs? En customBufferedCopy
, pourquoi synchronized
est utilisé ici?
J'ai aussi trouvé un autre exemple que -should- tester les performances entre les 2:
public class MappedIO {
private static int numOfInts = 4000000;
private static int numOfUbuffInts = 200000;
private abstract static class Tester {
private String name;
public Tester(String name) { this.name = name; }
public long runTest() {
System.out.print(name + ": ");
try {
long startTime = System.currentTimeMillis();
test();
long endTime = System.currentTimeMillis();
return (endTime - startTime);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public abstract void test() throws IOException;
}
private static Tester[] tests = {
new Tester("Stream Write") {
public void test() throws IOException {
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(new File("temp.tmp"))));
for(int i = 0; i < numOfInts; i++)
dos.writeInt(i);
dos.close();
}
},
new Tester("Mapped Write") {
public void test() throws IOException {
FileChannel fc =
new RandomAccessFile("temp.tmp", "rw")
.getChannel();
IntBuffer ib = fc.map(
FileChannel.MapMode.READ_WRITE, 0, fc.size())
.asIntBuffer();
for(int i = 0; i < numOfInts; i++)
ib.put(i);
fc.close();
}
},
new Tester("Stream Read") {
public void test() throws IOException {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream("temp.tmp")));
for(int i = 0; i < numOfInts; i++)
dis.readInt();
dis.close();
}
},
new Tester("Mapped Read") {
public void test() throws IOException {
FileChannel fc = new FileInputStream(
new File("temp.tmp")).getChannel();
IntBuffer ib = fc.map(
FileChannel.MapMode.READ_ONLY, 0, fc.size())
.asIntBuffer();
while(ib.hasRemaining())
ib.get();
fc.close();
}
},
new Tester("Stream Read/Write") {
public void test() throws IOException {
RandomAccessFile raf = new RandomAccessFile(
new File("temp.tmp"), "rw");
raf.writeInt(1);
for(int i = 0; i < numOfUbuffInts; i++) {
raf.seek(raf.length() - 4);
raf.writeInt(raf.readInt());
}
raf.close();
}
},
new Tester("Mapped Read/Write") {
public void test() throws IOException {
FileChannel fc = new RandomAccessFile(
new File("temp.tmp"), "rw").getChannel();
IntBuffer ib = fc.map(
FileChannel.MapMode.READ_WRITE, 0, fc.size())
.asIntBuffer();
ib.put(0);
for(int i = 1; i < numOfUbuffInts; i++)
ib.put(ib.get(i - 1));
fc.close();
}
}
};
public static void main(String[] args) {
for(int i = 0; i < tests.length; i++)
System.out.println(tests[i].runTest());
}
}
Je vois plus ou moins ce qui se passe, ma sortie ressemble à ceci:
Stream Write: 653
Mapped Write: 51
Stream Read: 651
Mapped Read: 40
Stream Read/Write: 14481
Mapped Read/Write: 6
Qu'est-ce qui rend le Stream Read/Write si incroyablement long? Et comme un test de lecture/écriture, pour moi, il semble un peu inutile de lire le même entier encore et encore (si je comprends bien ce qui se passe dans le Stream Read/Write
) Ne serait-il pas mieux de lire int à partir du fichier précédemment écrit et juste lire et écrire ints sur le même endroit? Y a-t-il une meilleure façon de l'illustrer?
J'ai me casser la tête sur beaucoup de ces choses pendant un certain temps et je ne peux pas obtenir toute l'image ..
Seidre: Quelle meilleure façon de comprendre que de demander de l'aide? – Geoff
Je n'ai pas très bien expliqué ma situation (je pensais que cela n'avait pas vraiment d'importance, une question est une question, non?) Je fais mon stage pour un professeur en systèmes d'information. Pour l'OS sujet, il a décidé de démontrer quelques principes en Java. N'ayant pas de vrai background java lui-même (il en sait encore beaucoup) mais n'a pas le temps d'expérimenter. C'est à moi d'améliorer/expliquer ses exemples. En tant qu'étudiant, j'ai encore beaucoup à apprendre et je ne comprends pas encore tout. C'est pourquoi je le demande ici .. Merci d'avoir essayé malgré tout. La classe n'est pas multithread, donc je ne vois pas pourquoi synchronized est utilisé? – Senne
Il n'y a aucune raison d'avoir une synchronisation à moins que l'auteur ait également utilisé cette méthode d'une autre classe où il testait le multithreading. Mais il y a beaucoup de problèmes avec cela - la méthode ne devrait pas être statique et le tampon non-statique. Le tampon doit être défini dans la méthode elle-même pour ce test. J'ai mis à jour ma réponse. –