几天前,我发布了一个类似的问题,但是没有任何代码,现在我创建了一个测试代码,以期寻求帮助。
代码在底部。
我得到了一些数据集,其中有一堆大文件(〜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利用率。
free
iostat
[edit]假设您从本地连接的SSD读取(高速缓存中没有所需的数据):
seek
read
open
buffering=0
[/编辑]
第一个示例仅访问3 * 100KB的文件数据,因此,由于您拥有的缓存数量远远超过了缓存的可用容量,所有300KB的内容很快就会进入缓存,因此您将看不到I / O,并且您的python进程将受CPU限制。
我有99.99%的把握,如果您测试从每个文件的最后100KB读取数据,它的性能将与第一个示例一样好-这与数据的位置无关,而与访问的数据大小有关。
第二个示例访问9GB中的随机部分,因此,仅当您有足够的可用RAM来缓存所有9GB内存时,并且仅在将文件预加载到缓存中之后,才能使测试用例以零I运行,您可以希望看到类似的性能。 / O。
在实际情况下,文件不会完全位于缓存中-因此您会看到许多I / O请求以及python的CPU利用率低得多。由于I / O比缓存访问要慢得多,因此您应该期望本示例的运行速度较慢。