Pythonのmmap関数でファイル読み込み性能を向上させる方法【実例付き解説】
はじめに
mmap(memory mapping:メモリマッピング)は、ファイルを仮想メモリにマッピングすることで、通常のI/O関数を使わずにファイルシステム上のデータへ直接アクセスできる仕組みです。アクセスごとに個別のシステムコールを発行したり、バッファ間でデータをコピーしたりする必要がないため、I/O性能が大幅に向上します。
実際、メモリ上にあるものはすべて高速に動作します。たとえばSQLiteデータベースをオンディスクではなくインメモリで作成した場合、その方がはるかに高いパフォーマンスを発揮します。
メモリマップドファイルは、用途に応じて「ミュータブル(変更可能)な文字列」として扱うことも、「ファイルライクオブジェクト」として扱うこともできます。
mmapは close()、flush()、read()、readline()、seek()、tell()、write() など多くのメソッドをサポートしており、スライス操作はもちろん、正規表現との組み合わせにも対応しています。
実装手順
1. サンプルテキストファイルを用意する
まず、以下のような内容のテキストファイルを用意します。サンプル文はGoogleなどで検索して入手できます。この内容を input.txt というファイルに保存してください。
Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.
Id porro facete cum. No est veritus detraxit facilisis, sit ea clita decore essent. Ut eam labores fuisset menandri, ex sit brute viderer eleifend, altera argumentum vel ex. Duo at zril sensibus, eu vim ullum assentior, quando possit at his.
Te nam tempor posidonium scripserit, eam mundi reprimique dissentias ne. Vim te soleat offendit democritum. Nam an diam elaboraret, quaeque dissentias an has. Autem legendos dignissim ad vis, sea ex amet petentium reprehendunt, inermis constituam philosophia ne mel. Esse noster lobortis usu ne.
Nec reque postea urbanitas ut, mea in nulla invidunt ocurreret. Ei duo iuvaret numquam. Ferri nemore audire te est, mel et detracto noluisse. Nec eu habeo justo, id pro posse apeirian volutpat. Mea sonet quaestio ne.
Atqui quaeque alienum te vim. Graeco aliquip liberavisse pro ut. Te similique reformidans usu, te mundi aliquando ius. Meis scripta minimum quo no, meis prima fabellas eu eam, laoreet delicata forensibus ut vim. Et quo vocibus mediocritatem, atqui summo an eam.
2. mmap()関数でメモリマップドファイルを作成する
mmap() 関数を使ってメモリマップドファイルを作成します。ファイル名は、ファイルオブジェクトの fileno() メソッド、または os.open() を使って指定できます。
注意: mmap() を呼び出す前にファイルを開き、使用後は閉じるのはユーザーの責任です。
mmap() の第2引数はバイト単位のサイズで、マップするファイルの範囲を示します。0を指定するとファイル全体がマップされます。さらに access 引数として、読み取り専用の ACCESS_READ、ライトスルー方式の ACCESS_WRITE、コピーオンライト方式の ACCESS_COPY を指定することもできます。
import mmap
input_text = """Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.
Id porro facete cum. No est veritus detraxit facilisis, sit ea clita decore essent. Ut eam labores fuisset menandri, ex sit brute viderer eleifend, altera argumentum vel ex. Duo at zril sensibus, eu vim ullum assentior, quando possit at his.
Te nam tempor posidonium scripserit, eam mundi reprimique dissentias ne. Vim te soleat offendit democritum. Nam an diam elaboraret, quaeque dissentias an has. Autem legendos dignissim ad vis, sea ex amet petentium reprehendunt, inermis constituam philosophia ne mel. Esse noster lobortis usu ne.
Nec reque postea urbanitas ut, mea in nulla invidunt ocurreret. Ei duo iuvaret numquam. Ferri nemore audire te est, mel et detracto noluisse. Nec eu habeo justo, id pro posse apeirian volutpat. Mea sonet quaestio ne.
Atqui quaeque alienum te vim. Graeco aliquip liberavisse pro ut. Te similique reformidans usu, te mundi aliquando ius. Meis scripta minimum quo no, meis prima fabellas eu eam, laoreet delicata forensibus ut vim. Et quo vocibus mediocritatem, atqui summo an eam.
"""
# テキストを書き込んだ入力ファイルを作成
input_file = 'input.txt'
f = open(input_file, "w+")
f.write(input_text)
f.close()
# 読み取りモードでファイルを開く
with open(input_file, 'r') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
print(f"Output \n*** Output first 5 bytes of the {input_file} is {m.read(5)} ")
print(f"*** Output Next 10 bytes of the {input_file} is {m.read(10)} ")出力結果
*** Output first 5 bytes of the input.txt is b'Lorem' *** Output Next 10 bytes of the input.txt is b' ipsum dol'
3. ファイルポインタの挙動を理解する
ここではファイルをメモリにマップし、.read() で最初の5バイトを読み込みました。最初の読み込み後、ファイルポインタは10バイト分進んでいます。続けて read(10) を呼び出すと、6〜15バイト目のデータが取得されます。このように、通常のファイルオブジェクトと同じ感覚でシーケンシャルな読み取りが行えます。
4. メモリマップドファイルを更新する
メモリマップドファイルを更新可能な状態にするには、マップする前にファイルを 'r+' モード('w' ではない点に注意)で開きます。
次の例では、行の一部をその場(in-place)で書き換える方法を示します。
import mmap
import shutil
input_file = 'input.txt'
input_copy = input_file.replace('input','input_copy')
# 元ファイルを変更しないよう、コピーを作成
shutil.copyfile(input_file,input_copy)
# 対象の単語
word = b'ipsum'
# 反転した単語
modified_word = word[::-1]
# 更新を受け付けるモードでファイルを開く
with open(input_copy, 'r+') as f:
with mmap.mmap(f.fileno(), 0) as m:
print(f"output \n *** Line before updates \n {m.readline().rstrip()}")
# seekで先頭に巻き戻す
m.seek(0)
# 単語を検索して反転させる
loc = m.find(word)
m[loc:loc + len(word)] = modified_word
m.flush()
# seekで先頭に巻き戻す
m.seek(0)
print(f" \n *** Line after updates \n {m.readline().rstrip()}")
f.seek(0)
print(f" \n *** Final file \n {f.readline().rstrip()}")出力結果
*** Line before updates b'Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.' *** Line after updates b'Lorem muspi dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.' *** Final file Lorem muspi dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.
5. 結果の確認
この例では、最初の行の中間にある「ipsum」という単語が、メモリ上およびファイル内で反転された「muspi」に置き換えられました。スライスへの代入によって、ディスク上のファイルも直接更新されていることがわかります。
6. メモリ上だけの変更を行う(ACCESS_COPY)
何らかの理由でメモリ上の変更だけを確認したい場合、ディスク上のファイルは更新したくないというケースがあります。その場合は ACCESS_COPY を使用します。
import mmap
import shutil
input_file = 'input.txt'
input_copy = input_file.replace('input','input_copy')
# 元ファイルを変更しないよう、コピーを作成
shutil.copyfile(input_file,input_copy)
# 対象の単語
word = b'ipsum'
# 反転した単語
modified_word = word[::-1]
# 更新を受け付けるモードでファイルを開く
with open(input_copy, 'r+') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY) as m:
print(f"output \n *** Line before updates \n {m.readline().rstrip()}")
# seekで先頭に巻き戻す
m.seek(0)
# 単語を検索して反転させる
loc = m.find(word)
m[loc:loc + len(word)] = modified_word
m.flush()
# seekで先頭に巻き戻す
m.seek(0)
print(f" \n *** Line after updates \n {m.readline().rstrip()}")
f.seek(0)
print(f" \n *** Final file \n {f.readline().rstrip()}")出力結果
*** Line before updates b'Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.' *** Line after updates b'Lorem muspi dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.' *** Final file Lorem ipsum dolor sit amet, causae apeirian ea his, duo cu congue prodesset. Ut epicuri invenire duo, novum ridens eu has, in natum meliore noluisse sea. Has ei stet explicari. No nam eirmod deterruisset, nusquam electram rationibus ad sea, interesset delicatissimi et sit. Purto molestiae cu eum, in per hinc periculis intellegam.
まとめ
出力結果を見ると、入力と最終的なファイルの内容は元のまま変わっておらず、変更がメモリ上にのみ適用されていることが確認できます。ACCESS_COPY モードでは、コピーオンライトの仕組みにより、変更内容はページキャッシュ上にのみ存在し、flush() を呼んでもディスクには書き込まれません。この特性を活かせば、元ファイルを保護しながら安全にデータを試すことが可能です。
-
【Python入門】defで定義した関数をMatplotlibでプロットする方法
Pythonでは、defキーワードで定義した自作関数を、Matplotlibを使って簡単にグラフ化できます。本記事では、ユーザー定義関数 f(x) を作成し、その結果をプロットするまでの基本手順をサンプルコード付きで解説します。 プロットまでの基本手順 defで定義した関数をプロットするには、以下の手順に従います。 図のサイズを設定し、サブプロット間および周囲の余白(パディング)を調整する defを使ってユーザー定義関数 f(x) を作成する NumPyを使って x のデータ点を生成する plot()メソッドで x と f(x) をプロットする show()メソッドで図を画面に表示する サ
-
Python Tkinterのasksaveasfile()関数の使い方を徹底解説
TkinterとはTkinterは、PythonでGUI(グラフィカルユーザーインターフェース)プログラミングを行うための標準モジュールです。キャンバス(Canvas)を作成し、その中にさまざまなプロパティや動作を持つUIコンポーネントを配置することで、アプリケーションの画面を構築していきます。本記事では、asksaveasfile()関数を使って、Pythonプログラムで作成したファイルをローカルドライブに保存する方法を解説します。処理の流れまず、ベースとなるウィンドウを作成し、その上にttk.Button関数を使ってボタンを配置します。次に、別の関数を定義し、その中でasksaveasfi