当前位置: 首页 > 面试题库 >

Python mmap-缓慢访问文件结尾(带有测试代码)

郎雪风
2023-03-14
问题内容

几天前,我发布了一个类似的问题,但是没有任何代码,现在我创建了一个测试代码,以期寻求帮助。

代码在底部。

我得到了一些数据集,其中有一堆大文件(〜100个),我想从这些文件中高效地提取特定行(在内存和速度上)。

我的代码获取了一个相关文件的列表,代码使用[第1行]打开了每个文件,然后使用[第2行]将文件映射到了内存,对于每个文件,我都收到一个索引列表并遍历检索到的索引相关信息(此示例为10个字节),如下所示:[3-4行],最后我用[5-6行]关闭了句柄。

binaryFile = open(path, "r+b")
binaryFile_mm = mmap.mmap(binaryFile.fileno(), 0)
for INDEX in INDEXES:
    information = binaryFile_mm[(INDEX):(INDEX)+10].decode("utf-8")
binaryFile_mm.close()
binaryFile.close()

该代码并行运行,每个文件具有数千个索引,并连续数小时连续数小时执行。

现在解决问题 -当我将索引限制为较小时,代码运行良好(意思是-
当我要求代码从文件开头获取信息时)。但!当我增加索引范围时,一切都会减慢到(几乎)停止并且buff /
cache内存已满(我不确定内存问题是否与减速有关)。

所以我的问题是,为什么要从文件的开头或结尾检索信息,这又有什么用呢?如何克服这个问题,以便从文件的结尾即时访问信息,而又不减慢和增加缓冲/缓存的内存采用。

PS-
一些数字和大小:所以我得到了约100个文件,每个文件的大小约为1GB,当我将索引限制为文件的0%-10%时,它运行良好,但是当我允许索引位于文件停止工作。

代码 -使用python 3.5在Linux和Windows上进行测试, 需要10 GB的存储空间
(创建3个文件,每个文件的随机字符串在3GB之内)

import os, errno, sys
import random, time
import mmap



def create_binary_test_file():
    print("Creating files with 3,000,000,000 characters, takes a few seconds...")
    test_binary_file1 = open("test_binary_file1.testbin", "wb")
    test_binary_file2 = open("test_binary_file2.testbin", "wb")
    test_binary_file3 = open("test_binary_file3.testbin", "wb")
    for i in range(1000):
        if i % 100 == 0 :
            print("progress -  ", i/10, " % ")
        # efficiently create random strings and write to files
        tbl = bytes.maketrans(bytearray(range(256)),
                          bytearray([ord(b'a') + b % 26 for b in range(256)]))
        random_string = (os.urandom(3000000).translate(tbl))
        test_binary_file1.write(str(random_string).encode('utf-8'))
        test_binary_file2.write(str(random_string).encode('utf-8'))
        test_binary_file3.write(str(random_string).encode('utf-8'))
    test_binary_file1.close()
    test_binary_file2.close()
    test_binary_file3.close()
    print("Created binary file for testing.The file contains 3,000,000,000 characters")




# Opening binary test file
try:
    binary_file = open("test_binary_file1.testbin", "r+b")
except OSError as e: # this would be "except OSError, e:" before Python 2.6
    if e.errno == errno.ENOENT: # errno.ENOENT = no such file or directory
        create_binary_test_file()
        binary_file = open("test_binary_file1.testbin", "r+b")




## example of use - perform 100 times, in each itteration: open one of the binary files and retrieve 5,000 sample strings
## (if code runs fast and without a slowdown - increase the k or other numbers and it should reproduce the problem)

## Example 1 - getting information from start of file
print("Getting information from start of file")
etime = []
for i in range(100):
    start = time.time()
    binary_file_mm = mmap.mmap(binary_file.fileno(), 0)
    sample_index_list = random.sample(range(1,100000-1000), k=50000)
    sampled_data = [[binary_file_mm[v:v+1000].decode("utf-8")] for v in sample_index_list]
    binary_file_mm.close()
    binary_file.close()
    file_number = random.randint(1, 3)
    binary_file = open("test_binary_file" + str(file_number) + ".testbin", "r+b")
    etime.append((time.time() - start))
    if i % 10 == 9 :
        print("Iter ", i, " \tAverage time - ", '%.5f' % (sum(etime[-9:]) / len(etime[-9:])))
binary_file.close()


## Example 2 - getting information from all of the file
print("Getting information from all of the file")
binary_file = open("test_binary_file1.testbin", "r+b")
etime = []
for i in range(100):
    start = time.time()
    binary_file_mm = mmap.mmap(binary_file.fileno(), 0)
    sample_index_list = random.sample(range(1,3000000000-1000), k=50000)
    sampled_data = [[binary_file_mm[v:v+1000].decode("utf-8")] for v in sample_index_list]
    binary_file_mm.close()
    binary_file.close()
    file_number = random.randint(1, 3)
    binary_file = open("test_binary_file" + str(file_number) + ".testbin", "r+b")
    etime.append((time.time() - start))
    if i % 10 == 9 :
        print("Iter ", i, " \tAverage time - ", '%.5f' % (sum(etime[-9:]) / len(etime[-9:])))
binary_file.close()

我的结果是:( 从文件中获取信息的平均时间比从一开始就获取信息的速度慢了将近4倍,在大约100个文件和并行计算的情况下,这种差异变得更大)

Getting information from start of file
Iter  9         Average time -  0.14790
Iter  19        Average time -  0.14590
Iter  29        Average time -  0.14456
Iter  39        Average time -  0.14279
Iter  49        Average time -  0.14256
Iter  59        Average time -  0.14312
Iter  69        Average time -  0.14145
Iter  79        Average time -  0.13867
Iter  89        Average time -  0.14079
Iter  99        Average time -  0.13979
Getting information from all of the file
Iter  9         Average time -  0.46114
Iter  19        Average time -  0.47547
Iter  29        Average time -  0.47936
Iter  39        Average time -  0.47469
Iter  49        Average time -  0.47158
Iter  59        Average time -  0.47114
Iter  69        Average time -  0.47247
Iter  79        Average time -  0.47881
Iter  89        Average time -  0.47792
Iter  99        Average time -  0.47681

问题答案:

为了确定您是否获得了足够的性能,请检查缓冲区/页面缓存可用的内存(free在Linux中),I / O统计信息-
读取次数,读取大小和持续时间(iostat与硬件规格进行比较;以及您的进程的CPU利用率。

[edit]假设您从本地连接的SSD读取(高速缓存中没有所需的html" target="_blank">数据):

  • 在单线程中进行读取时,您应该希望批量读取50,000次要花费7秒钟以上(50000 * 0.000150)。可能更长,因为对mmap文件的5万次访问将触发更多或更大的读取,因为您的访问未按页面对齐-正如我在另一次问答中所建议的那样,我将使用seek/read代替(并且open该文件buffering=0应避免不必要的读取用于Python缓冲的I / O)。
  • 随着更多线程/进程同时读取,您可以使SSD吞吐量达到饱和(它可以完成4KB读取/秒的速度-可以在5,000到1,000,000之间的任何地方),然后单个读取将变得更慢。

[/编辑]

第一个示例仅访问3 * 100KB的文件数据,因此,由于您拥有的缓存数量远远超过了缓存的可用容量,所有300KB的内容很快就会进入缓存,因此您将看不到I /
O,并且您的python进程将受CPU限制。

我有99.99%的把握,如果您测试从每个文件的最后100KB读取数据,它的性能将与第一个示例一样好-这与数据的位置无关,而与访问的数据大小有关。

第二个示例访问9GB中的随机部分,因此,仅当您有足够的可用RAM来缓存所有9GB内存时,并且仅在将文件预加载到缓存中之后,才能使测试用例以零I运行,您可以希望看到类似的性能。
/ O。

在实际情况下,文件不会完全位于缓存中-因此您会看到许多I / O请求以及python的CPU利用率低得多。由于I /
O比缓存访问要慢得多,因此您应该期望本示例的运行速度较慢。



 类似资料:
  • 我已经为播放音频文件编写了此代码,我想在播放后获得音频文件何时结束的指示。我试过AS。getMicrosecondLength()==AS。getMicrosecondPosition(),但未为AudioStream定义这些方法。请告诉我怎么做。

  • 关于IntelliJ和生成(测试)文件特性的一个小问题。 生成的文件只是一个骨架: 所有类通常都以一个额外的行尾文件结束。 即使是静态分析工具也会标记它,如果它不存在(“缺少文件结尾”之类的东西)。 如何告诉IntelliJ在文件末尾用额外的行生成测试类? 谢谢你

  • 问题内容: 我正在做一个测试:比较cgo和纯Go函数的执行时间,每个函数运行一亿次。与Golang函数相比,cgo函数需要更长的时间,我对此结果感到困惑。我的测试代码是: 结果是: 结果表明,调用C函数比Go函数要慢。我的测试代码有问题吗? 我的系统是:mac OS X 10.9.4(13E28) 问题答案: 正如您所发现的那样,通过CGo调用C / C ++代码的开销相当大。因此,一般而言,最好

  • 基于此链接中的答案,创建了一个包含测试类的测试套件 返回错误 但是每个添加的测试类都有测试方法,并且单独运行

  • 我有一个测试套件: 一类: B类: 现在的问题是位于下, 和 类正在。如果我评论带有测试服运行正常,但未注释,我得到: java.lang.非法状态异常: 无法加载应用程序上下文 在org . spring framework . test . context . cache . defaultcacheawarecontextloaderdelegate . load context(defau

  • 我正在设计一个程序,为消息中的特定字符保存索引位置。然后,我需要根据这些字符的索引位置来检索它们。我将这些字符的位置保存在。txt文件中。我检索了它们,但在最后,我得到了这样一条消息“Exception in thread”AWT-EventQueue-0“java.lang.IndexoutOfBoundsException:Index 120 out of bounded for length