Python mmap - медленный доступ к концу файлов [с тестовым кодом] - PullRequest
4 голосов
/ 17 июня 2019

Я отправил похожий вопрос несколько дней назад, но без кода, теперь я создал тестовый код в надежде получить некоторую помощь.

Код внизу.

У меня есть некоторый набор данных, в котором у меня есть куча больших файлов (~ 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()

Этот код работает параллельно с тысячами индексов для каждого файла и непрерывно делает это несколько раз в секунду в течение нескольких часов.

Теперь к проблеме - Код работает хорошо, когда я ограничиваю индексы маленькими (имеется в виду - когда я прошу код получить информацию из начала файла). Но! когда я увеличиваю диапазон индексов, все замедляется до (почти) остановки, и буферная / кэш-память заполняется (я не уверен, связана ли проблема памяти с замедлением).

Поэтому мой вопрос: почему имеет значение, извлекаю ли я информацию из начала или конца файла и как мне ее преодолеть, чтобы получить мгновенный доступ к информации из конца файла, не замедляя и не увеличивая бафф? / Использование кэш-памяти.

PS - некоторые числа и размеры: поэтому я получил ~ 100 файлов размером около 1 ГБ, когда я ограничил индексы до 0% -10% от файла, он работает нормально, но когда я разрешил индексу в любом месте файла он перестает работать.

Код - протестировано на Linux и Windows с Python 3.5, требуется 10 ГБ дискового пространства (создает 3 файла со случайными строками по 3 ГБ каждый)

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

1 Ответ

1 голос
/ 17 июня 2019

Основная причина, по которой у вас такая разница во времени, заключается в том, что вы должны искать, где вам нужно в файле. Чем дальше от позиции 0, тем дольше это займет.

Что может помочь справка, так как вы знаете начальный индекс, который вам нужен, найдите дескриптор файла до этой точки и затем выполните mmap. Или действительно, зачем вообще беспокоиться о mmap - просто прочитайте количество байтов, которое вам нужно, из искомой позиции и вставьте это в переменную результата.

...