2016-06-18 9 views
0

私は10 977 120の乱数(60 MB)でカンマ区切りのファイルを取得しました。この作業はこれを同時に実行することであり、したがって断食的な方法で可能です。長い話を簡単に言えば、テキストファイルを文字列配列にロードします。私の次のアイデアは、この配列を4つの小さなピースに分けて、各ピースに対してそのピースを合計するスレッドを持つことでした。copyOfRangeで配列を分割するときの奇妙な実行時間

奇妙なことに、私はリストを4つに分割して、非常に異なるランニングタイムを得ています。

私はこのようになります方法持って

public void splitNumbers(String[] numbers){ 
    int size = numbers.length; 
    String[][] numberssplit = new String[4][]; 
    numberssplit[0] = Arrays.copyOfRange(numbers, 0, size/4); 
    numberssplit[1] = Arrays.copyOfRange(numbers, (size/4)+1, size/2); 
    numberssplit[2] = Arrays.copyOfRange(numbers, (size/2)+1, 3*(size/4)); 
    numberssplit[3] = Arrays.copyOfRange(numbers, (3*(size/4))+1, size-1); 
    //MS: 2750 
} 

以上はしかし、2つの部分のみを分割、それはだようにそれが見えるように、5つのMSを取りについて2750ミリ

numberssplit[0] = Arrays.copyOfRange(numbers, 0, size/4); 
numberssplit[1] = Arrays.copyOfRange(numbers, (size/4)+1, size/2); 
//numberssplit[2] = Arrays.copyOfRange(numbers, (size/2)+1, 3*(size/4)); 
//numberssplit[3] = Arrays.copyOfRange(numbers, (3*(size/4))+1, size-1); 
MS: 5 

を取りに最後の2つの部分が長くかかる。

のみ第三の部分を分割するには、2ミリ秒

//numberssplit[0] = Arrays.copyOfRange(numbers, 0, size/4); 
//numberssplit[1] = Arrays.copyOfRange(numbers, (size/4)+1, size/2); 
numberssplit[2] = Arrays.copyOfRange(numbers, (size/2)+1, 3*(size/4)); 
//numberssplit[3] = Arrays.copyOfRange(numbers, (3*(size/4))+1, size-1); 
//MS: 2 

を受け取り、それが唯一の作品であるスプリットだときに第四の部分はありません。最後の二つのコメントを外し最後に

//numberssplit[0] = Arrays.copyOfRange(numbers, 0, size/4); 
//numberssplit[1] = Arrays.copyOfRange(numbers, (size/4)+1, size/2); 
//numberssplit[2] = Arrays.copyOfRange(numbers, (size/2)+1, 3*(size/4)); 
numberssplit[3] = Arrays.copyOfRange(numbers, (3*(size/4))+1, size-1); 
//MS: 2 

は、2927ミリ秒

//numberssplit[0] = Arrays.copyOfRange(numbers, 0, size/4); 
//numberssplit[1] = Arrays.copyOfRange(numbers, (size/4)+1, size/2); 
numberssplit[2] = Arrays.copyOfRange(numbers, (size/2)+1, 3*(size/4)); 
numberssplit[3] = Arrays.copyOfRange(numbers, (3*(size/4))+1, size-1); 
//MS: 2927 

それはnumbersplit[2]numbersplit[3]が、それは多くの時間がかかり組み合わせたときのように思えるがかかりますが、なぜですか?明らかにJavaは、シーンの裏側でいくつかの魔法をしていますが、私はそのロジックを見ることができません。だから何が起こっている?

+1

60MBファイルにはいくつのStringオブジェクトがありますか?基本的には、数字配列はどれくらいですか?あなたのjvmにどのくらいのメモリが割り当てられていますか? –

+0

10 977 120 numbers ^^私は確信しています – Jazerix

答えて

1

短い答え:-verbose:gcを使用してプロジェクトを実行すると、出力によって完全な回答が得られます。

長い答え:

私はあなたのコードの最初のブロックを使用してテストを行いました。範囲の最初のインデックス - から

:説明here

(3*(size/4))+1, size-1 

:ここ

(size/4)+1, size/2 
(size/2)+1, 3*(size/4) 

と二つの数字:あなたはこれらの範囲内の1つの番号を失うことになるすべての ファースト to - コピーする範囲の最終インデックス

配列の複雑さとして。copyOfRangeは(O(n))であり、これらの4つの部分の間に時間差はないはずです。 しかし、あなたがこのようなものを見るなら、メモリ割り当てによって引き起こされるメモリの問題を意味します。

はこれを見てください:

package testproject; 

import java.util.Arrays; 

public class TestProject { 

    public static void main(String[] args) { 
     String[] numbers = getNumbers(0, 10000000); 

     long timestamp = System.currentTimeMillis(); 
     System.out.println("Starting split"); 
     String[][] splitted = splitNumbers(numbers); 
     System.out.println(System.currentTimeMillis() - timestamp); 

     timestamp = System.currentTimeMillis(); 
     System.out.println("Starting split"); 
     splitted = splitNumbers(numbers); 
     System.out.println(System.currentTimeMillis() - timestamp); 
    } 

    public static String[] getNumbers(int from, int to){ 
     String[] res = new String[to-from]; 
     for(int i=0; i<(to-from); i++){ 
      res[i] = Integer.toString(from + i); 
     } 
     return res; 
    } 

    public static String[][] splitNumbers(String[] numbers){ 
     int size = numbers.length; 
     String[][] numberssplit = new String[4][]; 
     numberssplit[0] = Arrays.copyOfRange(numbers, 0, size/4); 
     numberssplit[1] = Arrays.copyOfRange(numbers, (size/4), size/2); 
     numberssplit[2] = Arrays.copyOfRange(numbers, (size/2), 3*(size/4)); 
     numberssplit[3] = Arrays.copyOfRange(numbers, (3*(size/4)), size); 
     return numberssplit; 
    } 
} 

ここでは、二回splitNumbersへの呼び出しを持っています。私のPC上

結果:ガベージコレクタへ

Starting split 
    15 
    Starting split 
    [GC (Allocation Failure) 626829K->625997K(764928K), 0.2908820 secs] 
    [Full GC (Ergonomics) 625997K->625210K(1039872K), 3.2126996 secs] 
    3510 

あなたがここに見るようにそうです、私たちは隠されたコール。 問題が発生します。

+0

ああ、それは素晴らしいです、これをクリアしていただきありがとうございます! – Jazerix

0

データのコピーを必要としないアプローチを検討してください。

たとえば、各タスクに元の配列を渡しますが、動作する範囲を指定します。

もう1つの方法は、リストとして表現し、コピーではなくビューを返すsubListメソッドを使用することです。

+0

私は同意します、もっと良いアイデアは、おそらくファイルを読むときにこれを直接分割することですが、私はまだ上記のようなことが起こっているという答えを探していますcopyOfRangeそれは私の好奇心です:) – Jazerix

+0

私は上記の私のコメントで私の推測はあなたの問題は、jvm内のメモリ管理に関連していることを示唆している。 copyOfRangeは、配列内のポインターを別の配列にコピーするネイティブ呼び出しです。私はこれらの配列がかなり大きいことを推測しています。大きな連続ブロックを割り当てる必要があります。現在のメモリの状態に応じて、これは多くの作業を必要とする可能性があります。 –

+0

これは理にかなっています^^しかし、最初の2つの分割には4-5ms、最後の2つにはおよそ同じサイズのものがあります。 – Jazerix

関連する問題