2016-06-23 13 views
2

Ich habe eine Textdatei (50 GB) basierend auf der Formel (Gesamtgröße der Datei/Split-Größe) aufgeteilt. Jetzt ist die Aufspaltung in einzelnen Thread sequentiell getan, wie kann ich dies ändern Code, um das Teilen in Multithread durchzuführen (dh parallel sollte der Thread die Datei teilen und im Ordner speichern) Ich möchte die Datei nicht lesen, da sie mehr CPU benötigt. Mein Hauptziel ist es, die CPU-Auslastung zu reduzieren und das Teilen der Datei schnell mit weniger Zeit abzuschließen. Ich habe 8 CPU-Kerne.Teilen von Text-Datei in Stücke in Java mit Multithread

Irgendwelche Vorschläge ?? Danke im Voraus.

public class ExecMap { 


public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { 

    String FilePath = "/home/xm/Downloads/wikipedia_50GB/wikipedia_50GB/file21"; 
    File file = new File(FilePath); 
    long splitFileSize = 64 * 1024 * 1024; 
    long fileSize = file.length(); 
    System.out.println(+fileSize); 
    int mappers = (int) (fileSize/splitFileSize); 
    System.out.println(+mappers); 
    ExecMap exec= new ExecMap(); 
    exec.mapSplit(FilePath,splitFileSize,mappers,fileSize); 
} 

private static void mapSplit(String FilePath, long splitlen, int mappers,long fileSize) { 
ExecutorService executor = Executors.newFixedThreadPool(1); 
     executor.submit(() -> { 
      long len = fileSize; 
      long leninfile = 0, leng = 0; 
      int count = 1, data; 
      try { 
       long startTime = System.currentTimeMillis(); // Get the start Time 
       long endTime = 0; 
       System.out.println(startTime); 
       File filename = new File(FilePath); 
       InputStream infile = new BufferedInputStream(new FileInputStream(filename)); 
       data = infile.read(); 
       while (data != -1) { 

        String name = Thread.currentThread().getName(); 
        System.out.println("task started: " + name +" ====Time " +System.currentTimeMillis()); 
        filename = new File("/home/xm/Desktop/split/" +"Mapper " + count + ".txt"); 
        OutputStream outfile = new BufferedOutputStream(new FileOutputStream(filename)); 
        while (data != -1 && leng < splitlen) { 
         outfile.write(data); 
         leng++; 
         data = infile.read(); 
        } 
        leninfile += leng; 
        leng = 0; 
        outfile.close(); 
        count++; 
        System.out.println("task finished: " + name); 
       } 
       endTime = System.currentTimeMillis(); 
       System.out.println(endTime); 
       long msec = endTime - startTime; 
       long sec = endTime - startTime; 
       System.out.println("Difference in milli seconds: " + msec); //Print the difference in mili seconds 
       System.out.println("Differencce in Seconds: " + sec/1000); 


      } catch (FileNotFoundException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      executor.shutdownNow(); 
     }); 


} 
} 
+2

Wie schlagen Sie vor, die Datei zu teilen, ohne sie zu lesen? Und die Verwendung mehrerer Threads kann nur die CPU-Auslastung insgesamt erhöhen. (Die gleiche Menge an Arbeit, plus der Aufwand beim Erstellen von Threads.) –

+4

Ihre CPU ist nicht der Flaschenhals. Disk-I/O ist. Wenig kannst du hier machen. – Fildor

+0

Also kann der obige Code die CPU-Auslastung speichern? Gibt es irgendeine andere Methode, die ich meine CPU verwenden kann? Wie kann ich meinen Code ändern, um die 50-GB-Datei effizient in 788 aufgeteilte Dateien aufzuteilen? – meow

Antwort

1

können Sie RandomAccessFile verwenden und seek in eine bestimmte Position überspringen verwenden.

So können Sie Ihre Testamentsvollstrecker eine Startposition und eine Endposition geben kann, so dass jeder Vollstrecker auf einem kleinen Stück der Datei arbeiten

Aber wie es Ihr Problem erwähnt wird Disk I/O

0

Der grundlegende Multithread-Ansatz besteht darin, eine Aufgabe zu übernehmen, sie in Teilaufgaben aufzuteilen, die als einzelne Arbeitseinheit ausgeführt werden können, und für jede Teilaufgabe einen Thread zu erstellen. Dies funktioniert am besten, wenn die Threads unabhängig voneinander sein können und keine Kommunikation erfordern und keine Ressourcen teilen.

Haus Gebäude als Analogie

Also, wenn wir ein Haus bauen, einige Teilaufgaben müssen in einer bestimmten Reihenfolge durchgeführt werden. Ein Fundament muss existieren, bevor das Haus gebaut werden kann. Die Wände müssen an Ort und Stelle sein, bevor das Dach aufgesetzt werden kann.

Einige Unteraufgaben können jedoch unabhängig voneinander ausgeführt werden. Das Dach kann geschindelt werden, während die Installateure die Rohrleitungen installieren und die Ziegelschichten die Außenseite des Hauses einmauern.

Grund Gedanken über das Problem

Im Fall Ihrer Datei Splitting zu lösen, würde der grundlegende Ansatz sein, um die Aufgabe zu übernehmen, um die Datei aufteilen, und teilen diese in mehrere Teilaufgaben zuweisen ein Teil der Datei, die in jeden Thread aufgeteilt werden soll.

Allerdings hat diese spezielle Aufgabe, die Datei zu splitten, eine gemeinsame Arbeit, die einen Flaschenhals erzeugen wird und möglicherweise eine Art Synchronisation zwischen den Threads erfordert, wenn sie von der zu trennenden Originaldatei lesen. Wenn mehrere Threads auf dieselbe Datei zugreifen, muss der Dateizugriff so erfolgen, dass die Threads auf ihren zugewiesenen Teil der Datei zugreifen können.

Die gute Nachricht ist, dass, da das einzige, was freigegeben wird, die Originaldatei ist und nur gelesen wird, müssen Sie sich keine Gedanken über das Synchronisieren der Dateilesevorgänge auf Java-Ebene machen.

Ein erster Ansatz

Der Ansatz, den ich auf den ersten betrachten würde, ist die Anzahl von Ausgabedateien durch die Anzahl der Gewindegänge zu unterteilen. Jeder Thread würde dann die Datei mit einem eigenen Dateireader öffnen, so dass jeder Thread unabhängig von den anderen Threads mit seiner Datei-E/A ist. Obwohl die ursprüngliche Datei freigegeben ist, hat jeder Thread seine eigenen Daten über die Leseposition der Datei, so dass jeder Thread unabhängig von der Datei liest.

Jeder Thread würde dann einen eigenen Satz von Ausgabedateien erstellen und aus der ursprünglichen Datei lesen und in die Ausgabedatei schreiben. Die Threads erstellen nacheinander ihre Ausgabedateien, beginnend mit dem ihnen zugewiesenen Offset innerhalb der Originaldatei, lesen aus der Originaldatei und schreiben in die Ausgabedatei.

Auf diese Weise erhalten Sie die Unabhängigkeit der Arbeit jedes Threads. Jeder Thread hat seine eigenen ursprünglichen Dateizugriffsdaten. Jeder Thread hat seine eigene zugewiesene Region der ursprünglichen Datei. Jeder Thread generiert seine eigenen Ausgabedateien.

Andere Überlegungen

Auf der Betriebssystemebene das Dateisystem geteilt. Daher muss das Betriebssystem den Dateisystemzugriff verschachteln und multiplexen. Für eine Anwendung wie diese, bei der Daten von einer Festplattendatei gelesen und dann sofort in eine andere Festplattendatei zurückgeschrieben werden, wartet die Anwendung die meiste Zeit darauf, dass das Betriebssystem die angeforderte E/A-Operation ausführt.

Für eine Festplattendatei müssen mehrere Operationen auf niedrigerer Ebene ausgeführt werden, wie zum Beispiel: (1) Auffinden des Speicherorts der Datei auf der Platte, (2) Suchen nach dieser Position und (3) Lesen oder Schreiben die angeforderte Datenmenge. Das Betriebssystem führt all diese Dinge für die Anwendung aus, und während das Betriebssystem diese Aktionen ausführt, wartet die Anwendung.

In Ihrer Multithreading-Anwendung fragt also jeder Thread das Betriebssystem nach Festplatten-E/A, so dass jeder Thread die meiste Zeit damit verbringt, auf die Festplatten-E/A-Anfrage vom Betriebssystem zu warten. ob diese Datenträger-E/A aus der ursprünglichen Datei liest oder in eine neue geteilte Datei schreibt.

Da diese Festplatten-E/A wahrscheinlich die Bounding-Aktion ist, die die meiste Zeit benötigt, ist die Frage, ob die benötigte Zeit reduziert werden kann.

Ein zweiter Ansatz

So eine alternative Architektur einen einzelnen Thread zu haben sein würde, die nur von der Original-Datei liest und liest in großen Stücken, die mehrmals die Größe der geteilten Dateigröße sind. Dann werden ein oder mehrere andere Threads verwendet, um jeden Chunk zu übernehmen und die Ausgabespaltdatei zu erzeugen.

Der einzelne Thread, der aus der ursprünglichen Datei liest, liest einen Split-Datei-Chunk und gibt diesen Chunk an einen anderen Thread weiter. Der andere Thread erstellt dann die Split-Datei und schreibt den Chunk heraus. Während der andere Thread diesen Unter-Task ausführt, liest der einzelne Thread den nächsten Chunk aus der ursprünglichen Datei und verwendet einen zweiten Thread, um diesen Chunk in eine geteilte Datei zu schreiben.

Dieser Ansatz sollte es ermöglichen, dass die Datenträger-E/A effizienter ist, da große Teile der ursprünglichen Datei in den Speicher gelesen werden. Die Datenträger-E/A mit der ursprünglichen großen Datei wird sequenziell ausgeführt, sodass das Betriebssystem Datenträger-Suchvorgänge und Datenträger-E/A effizienter ausführen kann.

In der ersten Annäherung erfolgt der Zugriff auf die Originaldatei zufällig, was erfordert, dass die Plattenköpfe, die die Daten von der Platte lesen, häufiger neu positioniert werden müssen, wenn jeder Thread eine Plattenleseanfrage macht.

Abschließende Gedanken: test Prognosen von

Um Messen sowohl die diese Ansätze zu bestimmen, ist tatsächlich effizienter erfordern würde versuchen.Während Sie eine Vorhersage basierend auf einem Modell machen können, wie das Betriebssystem und die Festplatten-Hardware funktionieren, bis Sie es tatsächlich ausprobiert haben und die zwei Ansätze messen, werden Sie nicht wissen, ob einer dem anderen überlegen ist.

Und am Ende kann die effizienteste Methode nur einen einzigen Thread haben, der große Teile der ursprünglichen Datei liest und dann die kleineren geteilten Dateien schreibt.

Mögliche Vorteile von mehreren Threads

Auf der anderen Seite, wenn Sie mehrere Threads haben, die große Teile der Datei, die geteilt geben werden, ein Teil des Betriebssystem-Overhead beteiligt Erstellen, Öffnen und Schließen von Dateien können effizienter mit den mehreren Threads geplant werden. Wenn mehrere Threads verwendet werden, können das Dateisystemverwaltungssubsystem des Betriebssystems und die Datenträger-E/A-Routinen die Datenträger-E/A effizienter planen, indem sie zwischen mehreren ausstehenden Datenträger-E/A-Anforderungen wählen.

Aufgrund des Aufwands beim Erstellen und Löschen von Threads sollten Sie beim Start der Anwendung wahrscheinlich eine Reihe von Arbeits-Threads erstellen und dann den Threads eine bestimmte Split-Datei-E/A-Task zuweisen. Wenn der Thread mit dieser Zuweisung fertig ist, wartet er auf einen anderen.

0

Sie werden keinen Vorteil beim Starten mehrerer Threads (wie von vielen in Kommentaren zu der ursprünglichen Frage bemerkt) sehen, "eine Datei parallel zu teilen".

Mehrere Threads, die parallel an Teilen einer großen Aufgabe arbeiten, können die Dinge nur beschleunigen, wenn sie unabhängig voneinander arbeiten . Da in diesem Fall der zeitraubende Teil 50 GB der Datei liest und sie als kleinere Dateien ausgibt, wird dies nicht von Java, sondern vom Betriebssystem ausgeführt (und schließlich vom Plattentreiber, der lesen und später schreiben muss all diese Bytes), mit mehreren Threads wird nur einen kleinen Overhead hinzufügen (für die Erstellung von Threads & Planung), was alles ein bisschen langsamer.

Darüber hinaus sind sequentielle Lese- und Schreibvorgänge in rotierenden Laufwerken (SSDs sind von dieser Regel ausgenommen) viel schneller als zufällige Lese- und Schreibvorgänge - wenn viele Threads von verschiedenen Teilen einer Platte lesen und schreiben, ist der Durchsatz erheblich schlechter wenn ein einzelner Thread alles tut.

Denken Sie darüber nach - Sie haben einen LKW-Fahrer (die OS + Festplatte) und müssen einen großen Haufen von Steinen an der Stelle A in kleinere Haufen von Scheiben an den Orten C, D und E teilen; und Ziegelsteine ​​können nur mit dem LKW reisen. Es gibt nur diesen LKW-Fahrer und Sie, der Aufseher, der die Befehle erteilt. Würden Sie mehr Vorgesetzte (Threads) einstellen, um Befehle parallel zu erteilen? Nein - Sie würden sich nur gegenseitig in die Quere kommen und der Lastwagenfahrer, der versucht, Ihnen allen zu gefallen, würde viel mehr Reisen benötigen, um kleinere Mengen von Ziegeln zu fahren, um die gleiche Arbeit zu machen.