如何使用MMAP功能提高Python文件读取性能?

如何使用MMAP功能提高Python文件读取性能?

阅读更多:Python 教程

介绍

MMAP缩写为内存映射,当映射到文件时,使用操作系统的虚拟内存直接访问文件系统上的数据,而不需要使用常规的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()函数创建内存映射文件。 我们可以通过文件对象的fileno()方法或从os.open()传递文件名。

注意:在调用mmap()之前,用户需要负责打开文件并关闭它。

mmap()的第二个参数是以字节为单位表示要映射的文件部分的大小。 如果该值为0,则映射整个文件。 你还可以使用一个额外的参数 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.

"""

# create a inout file with some text
input_file = 'input.txt'
f = open(input_file, "w+")
f.write(input_text)
f.close()

#Open the file in read mode
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个字节。现在,如果您再读取一次10个字节,它会从第6到第15个字节提供字节。

4. 要设置内存映射文件以进行更新,请在进行映射之前将其打开为’r+’(而不是’w’)。

我将用一个示例向您展示如何在原地修改行的一部分。

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"输出 \n *** 更新前的行\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 *** 更新后的行 \n {m.readline().rstrip()}")

f.seek(0)
print(f" \n *** 最终文件 \n {f.readline().rstrip()}")

输出

*** 更新前的行
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.'

*** 更新后的行
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.'

*** 最终文件
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”在内存中和文件中被替换。

6. 如果你想在内存中查看更改,但不想在磁盘上更新文件,则使用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"输出 \n *** 更新前的行 \n {m.readline().rstrip()}")

# 使用seek倒回位置0
m.seek(0)

# 查找单词并反转它
loc = m.find(word)
m[loc:loc + len(word)] = modified_word
m.flush()

# 使用seek倒回位置0
m.seek(0)
print(f" \n *** 更新后的行 \n {m.readline().rstrip()}")

f.seek(0)
print(f" \n *** 最终文件 \n {f.readline().rstrip()}")

输出

*** 更新前的行
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.'

*** 更新后的行
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.'

*** 最终文件
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.

7. 注意,输入和输出中未更改的内容,而更改仅应用于内存中。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程