Python молча зависает при записи больших файлов - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь записать большой список numy nd_arrays на диск.

Этот список имеет длину ~ 50000 элементов

Каждый элемент имеет размер nd_array (~ 2048,2) изИнтс.Массивы имеют разные формы.

Метод, которым я (сейчас) пользуюсь, является

@staticmethod
def _write_with_yaml(path, obj):
    with io.open(path, 'w+', encoding='utf8') as outfile:
        yaml.dump(obj, outfile, default_flow_style=False, allow_unicode=True)

Я также пробовал мариновать, что также вызывает ту же проблему :

В небольших списках (длиной ~ 3400) это работает нормально, заканчивается достаточно быстро (<30 секунд). </p>

В списках длиной ~ 6000 это заканчивается через ~ 2 минуты.

Когда список увеличивается, процесс, похоже, ничего не делает .Без изменений в ОЗУ или активности диска.

Я перестал ждать через 30 минут.

После принудительной остановки процесса файл внезапно стал иметь значительный размер (~ 600 МБ).Я не могу знать, закончил ли он писать или нет.

Как правильно писать такие большие списки, знать, успешно ли он писал, и, если возможно, знать, когда запись / чтениесобирается закончить?

Как мне отладить, что происходит, когда кажется, что процесс зависает?

Я предпочитаю не разбивать и не собирать списки вручную в моемкод, я ожидаю, что библиотеки сериализации смогут сделать это для меня.

Ответы [ 2 ]

0 голосов
/ 02 января 2019

для кода

import numpy as np
import yaml

x = []
for i in range(0,50000):
    x.append(np.random.rand(2048,2))
print("Arrays generated")
with open("t.yaml", 'w+', encoding='utf8') as outfile:
    yaml.dump(x, outfile, default_flow_style=False, allow_unicode=True)

в моей системе (MacOSX, i7, 16 ГБ RAM, SSD) с Python 3.7 и PyYAML 3.13 время финиша составляет 61 мин. Во время сохранения процесс python занимал около 5 ГБ памяти, а окончательный размер файла составлял 2 ГБ. Это также показывает накладные расходы на формат файла: так как размер данных составляет 50k *2048* 2 * 8 (размер с плавающей точкой обычно составляет 64 бита в питоне) = 1562 МБ, значит, yaml примерно в 1,3 раза хуже (и сериализация / десериализация также требует времени).

Чтобы ответить на ваши вопросы:

  1. Нет правильного или неправильного пути. Для обновления прогресса и оценка времени окончания не так просто (например, другие задачи могут мешать оценке, ресурсы, такие как память, могут быть использованы и т. д.). Вы можете положиться на библиотеку, которая поддерживает это или реализовать что-то самостоятельно (как подсказал другой ответ)
  2. Не уверен, что «отладка» является правильным термином, так как на практике это может быть просто медленный процесс. Провести анализ производительности нелегко, особенно если использование нескольких / разных библиотек. С чего бы начать, понятно требования: что вы хотите от сохраненного файла? Нужно ли им будь ямл? Сохранение 50k массивов в виде yaml не кажется лучшим решением если вы заботитесь о производительности. Стоит ли сначала спросить себя: «Какой формат лучше всего подходит для меня?» (но вы не дали подробностей, поэтому не могу сказать ...)

Редактировать: если вы хотите что-то просто быстро, используйте рассол. Код:

import numpy as np
import yaml
import pickle

x = []
for i in range(0,50000):
    x.append(np.random.rand(2048,2))
print("Arrays generated")
pickle.dump( x, open( "t.yaml", "wb" ) )

завершается через 9 секунд и генерирует файл размером 1,5 ГБ (без накладных расходов). Конечно, формат рассола должен использоваться в очень разных обстоятельствах, чем yaml ...

0 голосов
/ 02 января 2019

Не могу сказать, что это ответ, но, возможно, это так.

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

Это было решено с помощью JSON.

Не используйте YAML для чего-либо другого, кроме как в качестве конфигурации, которую вы не часто открываете.

Решение для сохранения вашего массива:

np.save(path,array) # path = path+name+'.npy'

Если вам действительно нужно сохранить список массивов, я рекомендую вам сохранить список с путями к массивам (сами массивы вы сохраните на диске с помощью np.save). Сохранение объектов Python на диске не совсем то, что вам нужно. То, что вы хотите - это сохранить массивы с помощью np.save

Полное решение (пример сохранения):

for array_index in range(len(list_of_arrays)):
    np.save(array_index+'.npy',list_of_arrays[array_index])
    # path = array_index+'.npy'

Полное решение (пример загрузки):

list_of_array_paths = ['1.npy','2.npy']
list_of_arrays = []
for array_path in list_of_array_paths:
    list_of_arrays.append(np.load(array_path))

Дополнительные советы:

Python не может работать с большими массивами. Более того, если вы загрузили несколько из них в списке. С точки зрения скорости и памяти всегда работайте с одним, двумя массивами одновременно. Остальные должны ждать на диске. Поэтому вместо ссылки на объект укажите ссылку в виде пути и при необходимости загрузите его с диска.

Кроме того, вы сказали, что не хотите собирать список вручную.

Возможное решение, которое я не советую, но возможно именно то, что вы ищете

>>> a = np.zeros(shape = [10,5,3])
>>> b = np.zeros(shape = [7,7,9])
>>> c = [a,b]
>>> np.save('data.npy',c)
>>> d = np.load('data.npy')
>>> d.shape
(2,)
>>> type(d)
<type 'numpy.ndarray'>
>>> d.shape
(2,)
>>> d[0].shape
(10, 5, 3)
>>> 

Мне кажется, мне не нужно комментировать вышеупомянутый код. Однако после загрузки вы потеряете список, поскольку список будет преобразован в массив numpy.

...