2013-05-28 2 views
-1

J'essaie de faire quelques améliorations de performances et je cherche à utiliser des fichiers mappés en mémoire pour écrire des données. J'ai fait quelques tests et étonnamment, MappedByteBuffer semble plus lent que d'allouer des buffers directs. Je ne suis pas capable de comprendre clairement pourquoi ce serait le cas. Quelqu'un peut-il faire allusion à ce qui pourrait se passer dans les coulisses? Ci-dessous sont mes résultats de test:Performances de MappedByteBuffer par rapport à ByteBuffer

J'alloue des tampons de 32 Ko. J'ai déjà créé les fichiers avec des tailles 3Gigs avant de commencer les tests. Donc, la croissance du fichier n'est pas le problème.

Test results DirectBuffer vs MappedByteBuffer

J'ajoute le code que j'ai utilisé pour ce test de performance. Toute contribution/explication sur ce comportement est très appréciée.

import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.io.RandomAccessFile; 
import java.nio.ByteBuffer; 
import java.nio.MappedByteBuffer; 
import java.nio.channels.FileChannel; 
import java.nio.channels.FileChannel.MapMode; 

public class MemoryMapFileTest { 

    /** 
    * @param args 
    * @throws IOException 
    */ 
    public static void main(String[] args) throws IOException { 

     for (int i = 0; i < 10; i++) { 
      runTest(); 
     } 

    } 

    private static void runTest() throws IOException { 

     // TODO Auto-generated method stub 
     FileChannel ch1 = null; 
     FileChannel ch2 = null; 
     ch1 = new RandomAccessFile(new File("S:\\MMapTest1.txt"), "rw").getChannel(); 
     ch2 = new RandomAccessFile(new File("S:\\MMapTest2.txt"), "rw").getChannel(); 

     FileWriter fstream = new FileWriter("S:\\output.csv", true); 
     BufferedWriter out = new BufferedWriter(fstream); 


     int[] numberofwrites = {1,10,100,1000,10000,100000}; 
     //int n = 10000; 
     try { 
      for (int j = 0; j < numberofwrites.length; j++) { 
       int n = numberofwrites[j]; 
       long estimatedTime = 0; 
       long mappedEstimatedTime = 0; 

       for (int i = 0; i < n ; i++) { 
        byte b = (byte)Math.random(); 
        long allocSize = 1024 * 32; 

        estimatedTime += directAllocationWrite(allocSize, b, ch1); 
        mappedEstimatedTime += mappedAllocationWrite(allocSize, b, i, ch2); 

       } 

       double avgDirectEstTime = (double)estimatedTime/n; 
       double avgMapEstTime = (double)mappedEstimatedTime/n; 
       out.write(n + "," + avgDirectEstTime/1000000 + "," + avgMapEstTime/1000000); 
       out.write("," + ((double)estimatedTime/1000000) + "," + ((double)mappedEstimatedTime/1000000)); 
       out.write("\n"); 
       System.out.println("Avg Direct alloc and write: " + estimatedTime); 
       System.out.println("Avg Mapped alloc and write: " + mappedEstimatedTime); 

      } 


     } finally { 
      out.write("\n\n"); 
      if (out != null) { 
       out.flush(); 
       out.close(); 
      } 

      if (ch1 != null) { 
       ch1.close(); 
      } else { 
       System.out.println("ch1 is null"); 
      } 

      if (ch2 != null) { 
       ch2.close(); 
      } else { 
       System.out.println("ch2 is null"); 
      } 

     } 
    } 


    private static long directAllocationWrite(long allocSize, byte b, FileChannel ch1) throws IOException { 
     long directStartTime = System.nanoTime(); 
     ByteBuffer byteBuf = ByteBuffer.allocateDirect((int)allocSize); 
     byteBuf.put(b); 
     ch1.write(byteBuf); 
     return System.nanoTime() - directStartTime; 
    } 

    private static long mappedAllocationWrite(long allocSize, byte b, int iteration, FileChannel ch2) throws IOException { 
     long mappedStartTime = System.nanoTime(); 
     MappedByteBuffer mapBuf = ch2.map(MapMode.READ_WRITE, iteration * allocSize, allocSize); 
     mapBuf.put(b); 
     return System.nanoTime() - mappedStartTime; 
    } 

} 
+0

ajouter plus de clarté, lors de l'écriture aux fichiers, je veux comprendre si, MappedByteBuffer fonctionne mieux que Just ByteBuffer et en utilisant le FileChannel pour faire les écritures. En ce qui concerne ma lecture, MappeByteBuffers sont censés fonctionner beaucoup mieux car il évite les appels JNI explicites d'écriture – Sudoer

Répondre

6

Vous testez la mauvaise chose. Ce n'est pas comment écrire le code dans les deux cas. Vous devriez allouer le tampon une fois, et continuer à mettre à jour son contenu. Vous incluez le temps d'allocation dans le temps d'écriture. Pas valide.

+0

Eh bien, je l'ai fait exprès. Mon schéma d'allocation crée des tampons de différentes tailles. Cependant, si j'ai besoin d'un buffer de plus grande taille, j'ai besoin de tampons plus petits pour en allouer de nouveaux. Par conséquent, j'ai inclus cela dans mon test. Si j'exclue la création du tampon, le tampon mappé en mémoire est encore moins performant. J'essaie de trouver une explication pour ça. – Sudoer

+2

L'explication est que cela prend plus de temps pour mapper un fichier que pour créer un 'ByteBuffer' direct. Je ne sais pas quel genre de réponse vous attendez, mais c'est tout. On suppose qu'il faut plus de traitement pour mapper le fichier en mémoire que pour allouer de la mémoire non mappée. Je ne sais pas pourquoi cela serait considéré comme surprenant. C'est une mauvaise programmation quelle que soit la motivation. – EJP

0

L'échange de données sur le disque est la principale raison pour laquelle MappedByteBuffer est plus lent que DirectByteBuffer. Le coût d'allocation et de désallocation est élevé avec les buffers directs, y compris MappedByteBuffer, et ce coût est cumulé dans les deux exemples, d'où la seule différence dans l'écriture sur le disque, ce qui est le cas avec MappedByteBuffer

Questions connexes