Python GDAL ReadAsArrayの最適化


9

私はGDALのReadAsArrayメソッドを使用して、numpy(具体的には再分類)を使用してラスターデータを処理しています。ラスターが大きいため、配列をブロックで処理し、各ブロックを反復処理して、GeoExamplesの例と同様の方法で処理します。

これらのブロックのサイズを設定して、ラスター全体の処理にかかる時間を最適化する方法を検討しています。numpy配列サイズの制限と、ラスタの「自然な」ブロックサイズを使用するためのGDAL GetBlockSizeの使用に注意して、「自然な」サイズの倍数で構成されるいくつかの異なるブロックサイズを使用してテストしました。以下のサンプルコードで:

import timeit
try:
    import gdal
except:
    from osgeo import gdal

# Function to read the raster as arrays for the chosen block size.
def read_raster(x_block_size, y_block_size):
    raster = "path to large raster"
    ds = gdal.Open(raster)
    band = ds.GetRasterBand(1)
    xsize = band.XSize
    ysize = band.YSize
    blocks = 0
    for y in xrange(0, ysize, y_block_size):
        if y + y_block_size < ysize:
            rows = y_block_size
        else:
            rows = ysize - y
        for x in xrange(0, xsize, x_block_size):
            if x + x_block_size < xsize:
                cols = x_block_size
            else:
                cols = xsize - x
            array = band.ReadAsArray(x, y, cols, rows)
            del array
            blocks += 1
    band = None
    ds = None
    print "{0} blocks size {1} x {2}:".format(blocks, x_block_size, y_block_size)

# Function to run the test and print the time taken to complete.
def timer(x_block_size, y_block_size):
    t = timeit.Timer("read_raster({0}, {1})".format(x_block_size, y_block_size),
                     setup="from __main__ import read_raster")
    print "\t{:.2f}s\n".format(t.timeit(1))

raster = "path to large raster"
ds = gdal.Open(raster)
band = ds.GetRasterBand(1)

# Get "natural" block size, and total raster XY size. 
block_sizes = band.GetBlockSize()
x_block_size = block_sizes[0]
y_block_size = block_sizes[1]
xsize = band.XSize
ysize = band.YSize
band = None
ds = None

# Tests with different block sizes.
timer(x_block_size, y_block_size)
timer(x_block_size*10, y_block_size*10)
timer(x_block_size*100, y_block_size*100)
timer(x_block_size*10, y_block_size)
timer(x_block_size*100, y_block_size)
timer(x_block_size, y_block_size*10)
timer(x_block_size, y_block_size*100)
timer(xsize, y_block_size)
timer(x_block_size, ysize)
timer(xsize, 1)
timer(1, ysize)

次のような出力が生成されます。

474452 blocks size 256 x 16:
        9.12s

4930 blocks size 2560 x 160:
        5.32s

58 blocks size 25600 x 1600:
        5.72s

49181 blocks size 2560 x 16:
        4.22s

5786 blocks size 25600 x 16:
        5.67s

47560 blocks size 256 x 160:
        4.21s

4756 blocks size 256 x 1600:
        5.62s

2893 blocks size 41740 x 16:
        5.85s

164 blocks size 256 x 46280:
        5.97s

46280 blocks size 41740 x 1:
        5.00s

41740 blocks size 1 x 46280:
        800.24s

サイズとピクセルタイプが異なるいくつかの異なるラスターに対してこれを実行してみましたが、xまたはy次元が10倍(場合によっては両方)増加すると処理時間が半分になり、同様の傾向が得られているようです上記の例ではそれほど重要ではありませんが、私の最大のラスターの分数を意味する場合があります。

だから私の質問は、なぜこの行動が起こっているのですか?

処理時間を改善するために使用するブロックを少なくすることを期待していましたが、最小限のテストは最も速くはありません。また、最終テストが先行テストよりもはるかに長いのはなぜですか?行または列ごとに、または読み取られるブロックの形状で合計サイズを読み取るために、ラスターに何らかの好みがありますか?これから得たいのは、入力のサイズに応じて、ラスターのブロックサイズを最適な値に設定できる基本的なアルゴリズムをまとめるための情報です。

入力はESRI ArcINFOグリッドラスタであり、「自然な」ブロックサイズは256 x 16であり、この例のラスタの合計サイズは41740 x 46280でした。


素晴らしいテスト!たくさん助けて!乾杯!
Isaque Daniel、

回答:


4

同じブロックサイズを使用してみましたか?200k x 200kピクセルのオーダーで非常にまばらなラスターデータを扱います。多くのベンチマークにより、プロセスで最も効率的な256x256ピクセルのブロックが生成されました。これは、ブロックを取得するために必要なディスクシークの数に関係しています。ブロックが大きすぎると、連続してディスクに書き込むことが難しくなり、シーク回数が増えます。同様に、小さすぎる場合は、ラスタ全体を処理するために多くの読み取りを行う必要があります。また、合計サイズが2の累乗になるようにするのにも役立ちます。ちなみに256x256はgdalのデフォルトのgeotiffブロックサイズであるため、おそらく同じ結論を導き出しました


256 x 256のブロックは、他のほとんどのテストよりも少し高速でしたが(2560 x 16および41740 x 1と同等)、約5%しかありませんでした。ただし、ラスターをgeotiff形式に変換することにより、これは少なくとも20%の最速のオプションでした。そのため、少なくともtiffの場合は、ブロックサイズの適切な選択のように見えます。私のgdalのデフォルトのgeotiffブロックサイズは128 x 128でしたが。
ssast

1
はい、フォーマットを選択できる場合はgeotiffが最良のオプションです。これまでのところ、開発に最も多くの時間が費やされています。また、圧縮を試してみてください。データがスパース(null値が多い)の場合は、SPARSE_OK作成オプションを使用して、nullブロックの読み取り/書き込みをスキップする必要があります
James

将来の参考のために知っておくと良いですが、この例ではESRI ArcINFOグリッドを読むのに悩んでいます。
ssast

また、最後の2つの例の違いについては、行の主な順序と列の主な順序について読む必要があります。この場合も、要求されたブロックを構築するために必要なディスクシークの数が原因です。
ジェームズ

1

私の疑いは、あなたが本当にGDALのブロックキャッシュにぶつかっていることであり、それは処理速度曲線に大きな影響を与えるつまみです。

これの詳細については、特にSettingConfigOptionsを参照して、GDAL_CACHEMAXその値を大幅に大きな値に変更すると、シミュレーションとどのように相互作用するかを調べてください。


gdal.SetCacheMax(1000000000)を使用してキャッシュを1 GBに設定した後、最後の1 x ysizeを除いて、ほとんどのテストで数秒減少しました。キャッシュを1 MBに減らすと、最後の2つを除くすべてのテストが実際にスピードアップします。これは、ほとんどのテストで自然なブロックサイズを使用しており、重複するブロックがないため、最後の2つのテストを除いてキャッシュは必要ないためだと思います。
ssast
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.