トップMy OpenStreetMap > OSMバイナリレコードのキャッシング

OSMバイナリレコードのキャッシング

ブロックキャッシュ管理

ブロック分割したOSMバイナリレコードファイルを一定数(現在は32)メモリに読み込んでおく。 必要に応じて、LRU(Least Recently Used)方式で入れ替えを行う。

バイナリデータは何も変更は加えず int配列として保有する。

ブロックのサイズは異なるため、メモリ領域は動的に確保しているため、スワップによりメモリの解放が起き、 ガーベージコレクションの対象となる。

class Block {
    static final int MaxBlocks = 32;
    static final Block[] blocks = new Block[MaxBlocks+1];

    final static String DIR = "/storage/5BF6-842C/Map/";
    int ix;
    String path;
    int ref_count;  // 参照スレッド数
    long time;      // 最終参照時間
    int[] vals;     // OSMバイナリレコード


    private static Block get(String dir, int Zoom, int bx, int by) {
        String path = (dir + Zoom + "/" + bx + "/" + by + ".dat");
        return get(path);
    }

    private static Block get(String path) {
        long start = System.currentTimeMillis();
        for (Block blk : blocks) {
            if (blk != null && blk.ix > 0 && blk.path.equals(path)) {
                return blk;     // キャッシュにあった
            }
        }
        // キャッシュになかった
        // 空きブロックまたは最も古いブロックをさがす
        long time = System.currentTimeMillis();
        int ixLRU = -1;
        for (int ix = 1; ix < blocks.length; ix++) {
            Block block = blocks[ix];
            if (block == null) {
                blocks[ix] = new Block(ix); // プログラム起動後最初の割り当て
                ixLRU = ix;
                break;  // 空きブロックが見つかった
            }
            if (block.ref_count == 0 && block.time < time) {
                ixLRU = ix;
                time = block.time;
            }
        }
        if (ixLRU < 0) {
            Log.e("Block", "ブロックが確保できない");
            return blocks[0];
        }

        try {
            Block block = blocks[ixLRU];
            block.path = path;
            block.vals = readAllInts(path);
            block.time = System.currentTimeMillis();
            long elapsed = System.currentTimeMillis() - start;
            System.out.printf("#%d %s %dms %dKB\n",
                    ixLRU, path, elapsed, block.vals.length/1024);
            return block;
        } catch (IOException e) {
            e.printStackTrace();
            return blocks[0];   //null;
        }
    }

}

ブロックの読み込み

int配列への読み込みはパフォーマンス上、少し凝ったプログラムとなっている。

8MB単位で読み込み、それを int配列に変換している。

 static byte[] ba = new byte[8*1024*1024];

 static int[] readAllInts(String file) throws IOException {
     File f = new File(file);
     if (!f.exists()) {
         Log.d("get", "no file: " + file);
         return new int[0];  // レコードなし
     }
     int file_length = (int)f.length();
     int[] buffer = new int[file_length/4];
     FileInputStream is = new FileInputStream(file);
     for (int offset = 0; offset < file_length; ) {
         int nbytes = is.read(ba);
         IntBuffer intBuf = ByteBuffer.wrap(ba).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
         intBuf.get(buffer, offset/4, nbytes/4);
         offset += nbytes;
     }
     is.close();
     return buffer;
 }

読み込み時間例を下に示す。

#1 /storage/5BF6-842C/Map/lands_split1/1/0.dat 38ms 9376KB
#2 /storage/5BF6-842C/Map/lands_split5/28/12.dat 7ms 1604KB
#3 /storage/5BF6-842C/Map/lands_low1/1/0.dat 3ms 327KB
#4 /storage/5BF6-842C/Map/japanL3/7/2.dat 4ms 828KB
#5 /storage/5BF6-842C/Map/japanL3/7/3.dat 14ms 3673KB
#6 /storage/5BF6-842C/Map/japanM7/113/49.dat 7ms 1906KB
#7 /storage/5BF6-842C/Map/japanM7/113/50.dat 13ms 3609KB
#8 /storage/5BF6-842C/Map/japanM7/113/51.dat 2ms 14KB
#9 /storage/5BF6-842C/Map/japanH7/113/50.dat 9ms 2461KB
#10 /storage/5BF6-842C/Map/japanN7/113/50.dat 31ms 8566KB
#11 /storage/5BF6-842C/Map/japanH13/7269/3228.dat 3ms 367KB
#12 /storage/5BF6-842C/Map/japanH13/7269/3229.dat 3ms 636KB
#13 /storage/5BF6-842C/Map/japanH13/7269/3230.dat 3ms 377KB
#14 /storage/5BF6-842C/Map/japanH13/7269/3231.dat 3ms 451KB
#15 /storage/5BF6-842C/Map/japanH13/7270/3228.dat 2ms 411KB
#16 /storage/5BF6-842C/Map/japanH13/7270/3229.dat 3ms 484KB
#17 /storage/5BF6-842C/Map/japanH13/7270/3230.dat 2ms 271KB
#18 /storage/5BF6-842C/Map/japanH13/7270/3231.dat 2ms 265KB
#19 /storage/5BF6-842C/Map/japanH13/7271/3228.dat 3ms 521KB
#20 /storage/5BF6-842C/Map/japanH13/7271/3229.dat 2ms 444KB
#21 /storage/5BF6-842C/Map/japanH13/7271/3230.dat 3ms 349KB
#22 /storage/5BF6-842C/Map/japanH13/7271/3231.dat 3ms 371KB
#23 /storage/5BF6-842C/Map/japanH13/7275/3224.dat 3ms 648KB
#24 /storage/5BF6-842C/Map/japanH13/7275/3225.dat 3ms 590KB
#25 /storage/5BF6-842C/Map/japanH13/7275/3226.dat 3ms 567KB
#26 /storage/5BF6-842C/Map/japanH13/7275/3227.dat 4ms 654KB
#27 /storage/5BF6-842C/Map/japanH13/7276/3224.dat 4ms 659KB
#28 /storage/5BF6-842C/Map/japanH13/7276/3225.dat 3ms 647KB
#29 /storage/5BF6-842C/Map/japanH13/7276/3226.dat 3ms 298KB
#30 /storage/5BF6-842C/Map/japanH13/7276/3227.dat 1ms 70KB

DataInputStreamに、BufferedInputStreamを介在させてみる。

 static int[] readAllInts(String file) throws IOException {
     File f = new File(file);
     if (!f.exists()) {
         Log.d("get", "no file: " + file);
         return new int[0];  // レコードなし
     }
     int file_length = (int)f.length();
     int[] buffer = new int[file_length/4];
     DataInputStream dis = new DataInputStream(
         new BufferedInputStream(new FileInputStream(file)));
     for (int n = 0; n < buffer.length; n++) {
        buffer[n] = dis.readInt();
     }
     dis.close();
     return buffer;
 }

結果は下に示すように1桁大きい読み込み時間となる。

#1 /storage/5BF6-842C/Map/lands_split1/1/0.dat 991ms 9376KB
#2 /storage/5BF6-842C/Map/lands_split5/28/12.dat 137ms 1604KB
#3 /storage/5BF6-842C/Map/lands_low1/1/0.dat 24ms 327KB
#4 /storage/5BF6-842C/Map/japanL3/7/2.dat 60ms 828KB
#5 /storage/5BF6-842C/Map/japanL3/7/3.dat 264ms 3673KB
#6 /storage/5BF6-842C/Map/japanM7/113/49.dat 138ms 1906KB
#7 /storage/5BF6-842C/Map/japanM7/113/50.dat 269ms 3609KB
#8 /storage/5BF6-842C/Map/japanM7/113/51.dat 5ms 14KB
#9 /storage/5BF6-842C/Map/japanH7/113/50.dat 176ms 2461KB
#10 /storage/5BF6-842C/Map/japanN7/113/50.dat 614ms 8566KB
#11 /storage/5BF6-842C/Map/japanH13/7269/3228.dat 28ms 367KB
#12 /storage/5BF6-842C/Map/japanH13/7269/3229.dat 46ms 636KB
#13 /storage/5BF6-842C/Map/japanH13/7269/3230.dat 29ms 377KB
#14 /storage/5BF6-842C/Map/japanH13/7269/3231.dat 35ms 451KB
#15 /storage/5BF6-842C/Map/japanH13/7270/3228.dat 31ms 411KB
#16 /storage/5BF6-842C/Map/japanH13/7270/3229.dat 36ms 484KB
#17 /storage/5BF6-842C/Map/japanH13/7270/3230.dat 23ms 271KB
#18 /storage/5BF6-842C/Map/japanH13/7270/3231.dat 20ms 265KB
#19 /storage/5BF6-842C/Map/japanH13/7271/3228.dat 38ms 521KB
#20 /storage/5BF6-842C/Map/japanH13/7271/3229.dat 33ms 444KB
#21 /storage/5BF6-842C/Map/japanH13/7271/3230.dat 28ms 349KB
#22 /storage/5BF6-842C/Map/japanH13/7271/3231.dat 29ms 371KB
#23 /storage/5BF6-842C/Map/japanH13/7275/3224.dat 48ms 648KB
#24 /storage/5BF6-842C/Map/japanH13/7275/3225.dat 43ms 590KB
#25 /storage/5BF6-842C/Map/japanH13/7275/3226.dat 43ms 567KB
#26 /storage/5BF6-842C/Map/japanH13/7275/3227.dat 49ms 654KB
#27 /storage/5BF6-842C/Map/japanH13/7276/3224.dat 49ms 659KB
#28 /storage/5BF6-842C/Map/japanH13/7276/3225.dat 48ms 647KB
#29 /storage/5BF6-842C/Map/japanH13/7276/3226.dat 23ms 298KB
#30 /storage/5BF6-842C/Map/japanH13/7276/3227.dat 7ms 70KB

リファレンス

[1]