簡體   English   中英

讀取/存儲大量多維數據的最快方法? (JAVA)

[英]Fastest way to read/store lots of multidimensional data? (Java)

我有三個關於三個嵌套循環的問題:

for (int x=0; x<400; x++)
{
    for (int y=0; y<300; y++)
    {
        for (int z=0; z<400; z++)
        {
             // compute and store value
        }
    }
}

我需要存儲所有計算值。 我的標准方法是使用3D陣列:

values[x][y][z] = 1; // test value

但事實證明這很慢:完成這個循環需要192毫秒,其中只有一個int-assignment

int value = 1; // test value

只需66毫秒。

1)為什么數組如此相對較慢?
2)當我把它放在內循環中時,為什么它變得更慢:

values[z][y][x] = 1; // (notice x and z switched)

這需要超過4秒!

3)最重要的是:我可以使用與分配單個整數一樣快的數據結構,但可以存儲與3D數組一樣多的數據嗎?

public static void main( String[] args ) {

    int[][][] storage = new int[ 400 ][ 300 ][ 400 ];
    long start = System.currentTimeMillis();

    for ( int x = 0; x < 400; x++ ) {
        for ( int y = 0; y < 300; y++ ) {
            for ( int z = 0; z < 400; z++ ) {
                storage[x][y][z] = 5;
            }
        }
    }

    long end = System.currentTimeMillis();
    System.out.println( "Time was: " + ( end - start ) / 1000.0 + " seconds." );


}

隨着-Xmx1g跑

時間是:0.188秒。

這看起來非常快......你正在尋找最里面循環中的4800萬個元素。

正在滾動一個愚蠢的小數據結構..

public static void main( String[] args ) {

    StorerGuy[] storerGuys = new StorerGuy[ 400 ];

    long start = System.currentTimeMillis();

    for ( int x = 0; x < 400; x++ ) {
        for ( int y = 0; y < 300; y++ ) {
            for ( int z = 0; z < 400; z++ ) {
                storerGuys[x] = new StorerGuy( x, y, z, 5 );

            }
        }
    }

    long end = System.currentTimeMillis();
    System.out.println( "Time was: " + ( end - start ) / 1000.0 + " seconds." );

}

public static class StorerGuy {

    public int x;
    public int y;
    public int z;
    public int value;

    StorerGuy( int x, int y, int z, int value ) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.value = value;
    }

}

時間是:0.925秒。

這比混合訂單示例中的4秒快。

我認為多個陣列對於這個問題來說太過分了。 使用更復雜的數據結構會更好,因為它會將所有內容保存在1個內存位置(x,y,z,value)。

Java是一種OO語言。 在大多數情況下,你應該使用對象,而不是像[[] [] []那樣奇怪的數據結構。

你試過這個:

Object[][][] store = new Object[ 400 ][300][400];

for (int x=0; x<400; x++)
{
    Object[][] matrix = store[x];

    for (int y=0; y<300; y++)
    {
        Object[] line = matrix[y];
        for (int z=0; z<400; z++)
        {
             // compute and store value
             line[z] = // result;
        }
    }
}

它可能會改善你的緩存顛簸。

1)為什么數組如此相對較慢?

正如其他人指出的那樣,你將蘋果與橙子進行比較。 三重數組很慢,因為它需要三次取消引用(內部至少 - 是的,“Java中沒有指針”); 但話說回來,你不能引用一個整數變量......

2)當我把它放在內循環中時,為什么它變得更慢:

values[z][y][x] = 1; // (notice x and z switched)

因為你減少了緩存一致性。 變化最快的索引應該是最后一個,因此大多數內存訪問在相同的高速緩存塊中彼此相鄰,而不是強制處理器等待從主RAM讀取塊。

3)最重要的是:我可以使用與分配單個整數一樣快的數據結構,但可以存儲與3D數組一樣多的數據嗎?

沒有。沒有這樣的結構,因為整數變量適合機器寄存器(甚至比處理器的內存緩存更快),並且總是可以比你想要提到的任何其他東西更快地訪問。 處理器速度比主存速度快得多。 如果你的'工作集'(你需要操作的數據)不適合寄存器或緩存,你將不得不支付罰款從RAM(或更糟糕的是,磁盤)獲取它。

話雖這么說,Java對每個陣列訪問進行邊界檢查,並且似乎不太聰明地優化邊界檢查。 以下比較可能是有意義的:

public static long test1(int[][][] array) {
    long start = System.currentTimeMillis();
    for ( int x = 0; x < 400; x++ ) {
        for ( int y = 0; y < 300; y++ ) {
            for ( int z = 0; z < 400; z++ ) {
                array[x][y][z] = x + y + z;
            }
        }
    }
    return System.currentTimeMillis() - start;
}

public static long test2(int [] array) {
    long start = System.currentTimeMillis();
    for ( int x = 0; x < 400; x++ ) {
        for ( int y = 0; y < 300; y++ ) {
            for ( int z = 0; z < 400; z++ ) {
                array[z + y*400 + x*400*300] = x + y + z;
            }
        }
    }
    return System.currentTimeMillis() - start;
}

public static void main(String[] args) {

    int[][][] a1 = new int[400][300][400];
    int[] a2 = new int[400*300*400];
    int n = 20;

    System.err.println("test1");
    for (int i=0; i<n; i++) {
        System.err.print(test1(a1) + "ms ");
    }
    System.err.println();
    System.err.println("test2");
    for (int i=0; i<n; i++) {
        System.err.print(test2(a2) + "ms ");
    }
    System.err.println();
}

我系統上的輸出是

test1
164ms 177ms 148ms 149ms 148ms 147ms 150ms 151ms 152ms 154ms 151ms 150ms 148ms 148ms 150ms 148ms 150ms 148ms 148ms 149ms 
test2
141ms 153ms 130ms 130ms 130ms 133ms 130ms 130ms 130ms 132ms 129ms 131ms 130ms 131ms 131ms 130ms 131ms 130ms 130ms 130ms

因此,還有一些改進空間......但我真的認為這不值得你這么做。

我猜這與緩存和寄存器以及內存局部性原理有很大關系。

存儲到陣列中時,Java必須訪問數千個字節的內存。 使用單個變量,它可以將該值保留在緩存中並繼續更新它。

緩存不足以容納整個多維數組,因此Java必須不斷更新緩存到內存和從內存更新緩存。 緩存訪問時間比內存訪問時間快。

我甚至不明白為什么你會做這個測試。 如果需要在多維數組中存儲大量數據,使用單個變量沒有幫助,即使它更快。

此外,在訪問數組時切換參數的原因是因為你在內存中跳轉的次數要多得多(緩存丟失次數多),而不是只是在反向迭代時。

考慮到數組是巨大的,使用的內存量,所需的間接(多維數組是對數組的引用數組......),這對我來說似乎並不慢。 當你切換x和z時,你可能正在破壞緩存。

為了比較,您可以將所有內容存儲在一個平面數組中......這樣可以提高存儲速度......但是檢索會更復雜,速度也更慢。

int k = 0;
for (int x=0; x<400; x++)
{
    for (int y=0; y<300; y++)
    {
        for (int z=0; z<400; z++)
        {
             // compute and store value
             arr[k++] = val;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM