Python メモリマップファイルを試す

20200612_梅の実

写真は梅の実を干しています。我が家の梅古木は枯れてしまい、新しい若手を育成中。
実を楽しむにはまだ、ちょっと早いかな。
さて、何ら分からないが、メモリーマップドファイルというおもしろそうなものを見つけました。
ファイルをメモリ上に置いて速度を速めるのが狙いなのだろう。
さて、どんなものか試してみたいと思います。


Pythonサンプルプログラム

import mmap
import os

# 1文字を10文字列を生成。改行コード付き
def getNumStr(n):
s = ''
for j in range(0,9):
s += n
s += '\n'
return s


def main():
# テキストファイル データ生成
with open("data.txt", "wb") as f:
bpos = 0
for i in range(0, 8):
s = getNumStr(str(i))
f.write(s.encode('utf-8'))

print('%d:%2d-%2d [%s]' % (i, bpos, f.tell(), s))
bpos = f.tell()

# テキストファイルのメモリーマッピング
with open("data.txt", "r+b") as f:
mm = mmap.mmap(f.fileno(), 0)

# ファイル読込
while True:
line = f.readline()
if not line:
break
print(line)
print()

# データ更新
print('---- データ更新 ----')
print(mm[40:50])
print(b"ABCDEFGHI\n"[:10])
mm[40:50] = b"ABCDEFGHI\n"[:10]
print()

# # データ追加を試す
# print('---- 追加 ----')

# mm.seek(0, os.SEEK_END)
# print(mm.tell())
# mm.write(s.encode('utf-8'))
# print()

# s = getNumStr('X')
# mm[-1:] = s.encode('utf-8')

print('---- 一覧表示 ----')
mm.seek(0)
while True:
bs = mm.readline()
if not bs:
break
print(bs.decode('utf-8'), end="")

mm.close()

if __name__ == '__main__':
main()


処理の流れ

  1. ファイルを生成
  2. ファイルを読み込み、メモリーマッピングを行う。
  3. データを1行更新
  4. データを一覧表示
  5. ファイルクローズ


ファイルのオープン/クローズ

ファイルのオープン/クローズはwith句を使っています。with句のインデントから抜けると
ファイルは自動的にクローズされます。


メモリマッピング

プログラムはデータファイルを生成し、そのデータファイルを読み出して
メモリマッピングします。メモリマッピングのミソの部分は下記

    mm = mmap.mmap(f.fileno(), 0)


ファイル読込時のEOF検出

ファイル読込はreadline()を使っています。
意図的にEOFを検出できないか調べましたが、見つけられなかったので
データが読めなかったら終了、EOFに到達とみなしています。


改行コードなし

データ一覧表示時に、print文の改行コードなしを指定しています。

    print("文字列", end="")



実行環境

Windows10 Anaconda
Python 3.8.3


試行

プログラムの初期、生成したデータは下記です。

生成テキストデータ data.txt
000000000
111111111
222222222
333333333
444444444
555555555
666666666
777777777

こんなデータを生成し、今回のメモリーマッピングで使います。


コンソール表示
$ python mmsamp.py
0: 0-10 [000000000
]
1:10-20 [111111111
]
2:20-30 [222222222
]
3:30-40 [333333333
]
4:40-50 [444444444
]
5:50-60 [555555555
]
6:60-70 [666666666
]
7:70-80 [777777777
]
b'000000000\n'
b'111111111\n'
b'222222222\n'
b'333333333\n'
b'444444444\n'
b'555555555\n'
b'666666666\n'
b'777777777\n'

---- データ更新 ----
b'444444444\n'
b'ABCDEFGHI\n'

---- 一覧表示 ----
000000000
111111111
222222222
333333333
ABCDEFGHI
555555555
666666666
777777777


データの4444....の部分がABCDEFGHIに置き換わり、更新されていることが
分かるかと思います。

処理終了時のテキストデータ data.txt
000000000
111111111
222222222
333333333
ABCDEFGHI
555555555
666666666
777777777


こちらもデータの4444....の部分がABCDEFGHIに置き換わり、更新されていることが
分かるかと思います。



さらに...

試しに、下記のようにデータも追加を試しましたが、うまく動きませんでした。
mm.seek(0, os.SEEK_END)
mm.write(s.encode('utf-8'))


s = getNumStr('X')
mm[-1:] = s.encode('utf-8')



感想

ファイルメモリーマッピングなんて昔からあったけれど、使ったためしがない。
ずっとデータ構造で処理するようにしていたので特に使う機会がなく、ファイルでもSQLデータベースばかりだったので、
ビューとか使えばいらないかな。正直、私は今後も使わないかなと思う次第です。
ただ、データ処理をファイルで考える方には有効な手段かなと。
ただ、従来のファイル読込チューニングには使える様な気がします。


修正・加筆

  • 2021/06/03 修正
    IntersectionObserver's による画像のオフスクリーン遅延読み込み処理に変更。
    IE11未対応。



参考



写真引用

Emmieさんによる写真ACからの写真

#python
#Anaconda

関連記事
スポンサーサイト



コメント

非公開コメント