Как я могу ускорить процесс выделения больших объектов, если у меня достаточно оперативной памяти? - PullRequest
20 голосов
/ 04 мая 2010

У меня уходит до часа, чтобы прочитать структуру данных графа NetworkX объемом 1 гигабайт, используя cPickle (его 1 ГБ, когда он хранится на диске в виде двоичного файла рассылки).

Обратите внимание, что файл быстро загружается в память. Другими словами, если я бегу:

import cPickle as pickle

f = open("bigNetworkXGraph.pickle","rb")
binary_data = f.read() # This part doesn't take long
graph = pickle.loads(binary_data) # This takes ages

Как я могу ускорить эту последнюю операцию?

Обратите внимание, что я попытался собрать данные, используя оба двоичных протокола (1 и 2), и, похоже, не имеет большого значения, какой протокол я использую. Также обратите внимание, что хотя я использую функцию «загрузки» (что означает «строка загрузки») выше, она загружает двоичные данные, а не ascii-data.

У меня 128 ГБ ОЗУ в системе, которую я использую, поэтому я надеюсь, что кто-нибудь подскажет мне, как увеличить некоторый буфер чтения, скрытый в реализации pickle.

Ответы [ 8 ]

8 голосов
/ 18 апреля 2016

Я имел большой успех при чтении структуры данных igraph ~ 750 МБ (двоичный файл pickle), используя сам cPickle. Это было достигнуто простым завершением вызова загрузки рассола, как упомянуто здесь

Пример фрагмента в вашем случае будет выглядеть примерно так:

import cPickle as pickle
import gc

f = open("bigNetworkXGraph.pickle", "rb")

# disable garbage collector
gc.disable()

graph = pickle.load(f)

# enable garbage collector again
gc.enable()
f.close()

Это определенно не самый удачный способ сделать это, однако, он значительно сокращает время, требуемое.
(Для меня оно уменьшилось с 843,04 до 41,28 с, примерно в 20 раз)

6 голосов
/ 04 мая 2010

Скорее всего, вы связаны накладными расходами на создание / размещение объектов Python, а не самим отбором. Если это так, то вы можете сделать это немного, кроме создания не всех объектов. Вам нужна вся структура сразу? Если нет, вы можете использовать ленивое заполнение структуры данных (например: представлять части структуры с помощью засеченных строк, а затем расщеплять их только при обращении к ним).

4 голосов
/ 04 мая 2010

Почему бы вам не попробовать маршалинг ваших данных и сохранить их в ОЗУ с использованием memcached (например).Да, у него есть некоторые ограничения, но, поскольку это указывает, что маршалинг намного быстрее (от 20 до 30 раз), чем травление.

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

1 голос
/ 08 декабря 2015

Я также пытаюсь ускорить загрузку / сохранение графиков networkx. Я использую метод adjacency_graph для преобразования графика во что-то сериализуемое, см., Например, этот код:

from networkx.generators import fast_gnp_random_graph
from networkx.readwrite import json_graph

G = fast_gnp_random_graph(4000, 0.7)

with open('/tmp/graph.pickle', 'wb+') as f:
  data = json_graph.adjacency_data(G)
  pickle.dump(data, f)

with open('/tmp/graph.pickle', 'rb') as f:
  d = pickle.load(f)
  H = json_graph.adjacency_graph(d)

Тем не менее, этот adjacency_graph метод конвертации довольно медленный, поэтому время, затрачиваемое на травление, вероятно, теряется при конвертации.

Так что на самом деле это не ускоряет, облом. Запуск этого кода дает следующие моменты времени:

N=1000

    0.666s ~ generating
    0.790s ~ converting
    0.237s ~ storing
    0.295s ~ loading
    1.152s ~ converting

N=2000

    2.761s ~ generating
    3.282s ~ converting
    1.068s ~ storing
    1.105s ~ loading
    4.941s ~ converting

N=3000

    6.377s ~ generating
    7.644s ~ converting
    2.464s ~ storing
    2.393s ~ loading
    12.219s ~ converting

N=4000

    12.458s ~ generating
    19.025s ~ converting
    8.825s ~ storing
    8.921s ~ loading
    27.601s ~ converting

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

https://gist.github.com/wires/5918834712a64297d7d1

1 голос
/ 20 апреля 2012

Это смешно.

У меня есть огромный словарь ~ 150 МБ (на самом деле collections.Counter), который я читал и писал, используя cPickle в двоичном формате.

Запись заняла около 3 минут.
Я перестал читать его на отметке 16 минут, и моя память полностью забилась.

Я сейчас использую маршала, и это занимает: написать: ~ 3s
читать: ~ 5 с

Я немного побродил и наткнулся на эту статью .
Полагаю, я никогда не смотрел на источник рассола, но он собирает целую виртуальную машину для реконструкции словаря?
Там должно быть примечание о производительности на очень больших объектах в документации IMHO.

0 голосов
/ 22 апреля 2012

В общем, я обнаружил, что, если возможно, при сохранении больших объектов на диск в python гораздо эффективнее использовать numpy ndarrays или scipy.sparse matrices.

Таким образом, для огромных графиков, подобных приведенному в примере, я мог бы преобразовать график в скудную разреженную матрицу (у networkx есть функция, которая делает это, и ее не сложно написать), а затем сохранить эту разреженную матрицу в двоичном формат.

0 голосов
/ 04 мая 2010

Возможно, лучшее, что вы можете сделать, - это разбить большие данные на наименьший объект, скажем, размером менее 50 МБ, который можно сохранить в оперативной памяти и рекомбинировать.

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

В любом случае, другой способ (который является более сложным) - использовать некоторую базу данных NoSQL , такую ​​как MongoDB , для хранения ваших данных ...

0 голосов
/ 04 мая 2010

почему вы не используете pickle.load?

f = open('fname', 'rb')
graph = pickle.load(f)
...