Теперь, может быть, что-то в 50 000-м очень большое, и это вызывает OOM, поэтому, чтобы проверить это, я сначала попробую:
file_list_chunks = list(divide_chunks(file_list_1,20000))[30000:]
Если при 10000 произойдет сбой, это подтвердит, является ли 20k слишком большим для размера фрагмента, или если снова произойдет сбой при 50,000, есть проблема с кодом ...
Хорошо, на код ...
Во-первых, вам не нужен явный конструктор list
, в python гораздо лучше итерировать, чем генерировать весь список в память.
file_list_chunks = list(divide_chunks(file_list_1,20000))
# becomes
file_list_chunks = divide_chunks(file_list_1,20000)
Я думаю, вы можете неправильно использовать ThreadPool здесь:
Запрещает отправку любых других задач в пул. Как только все задачи будут выполнены, рабочие процессы завершатся.
Это выглядит как close
, возможно, некоторые мысли все еще работают, хотя я думаю, что это безопасно, это выглядит немного непитонным, лучше использовать менеджер контекста для ThreadPool:
with ThreadPool(64) as pool:
results = pool.map(get_image_features,f)
# etc.
Явные del
s в python фактически не гарантируют освобождение памяти .
Вы должны собрать после соединения / после с:
with ThreadPool(..):
...
pool.join()
gc.collect()
Вы также можете попробовать разделить это на более мелкие части, например. 10000 или даже меньше!
Молоток 1
Одна вещь, которую я хотел бы сделать здесь, вместо того, чтобы использовать pandas DataFrames и большие списки, это использовать базу данных SQL, вы можете сделать это локально с помощью sqlite3 :
import sqlite3
conn = sqlite3.connect(':memory:', check_same_thread=False) # or, use a file e.g. 'image-features.db'
и использовать менеджер контекста:
with conn:
conn.execute('''CREATE TABLE images
(filename text, features text)''')
with conn:
# Insert a row of data
conn.execute("INSERT INTO images VALUES ('my-image.png','feature1,feature2')")
Таким образом, нам не придется обрабатывать объекты большого списка или DataFrame.
Вы можете передать соединение каждому из потоков ... вам может понадобиться что-то немного странное, например:
results = pool.map(get_image_features, zip(itertools.repeat(conn), f))
Затем, после завершения расчета, вы можете выбрать из базы данных все, в какой формат вы хотите. Например. используя read_sql .
Молоток 2
Используйте здесь подпроцесс, вместо того, чтобы запускать его в одном и том же экземпляре python, "shell" для другого.
Поскольку вы можете передавать начало и конец в python как sys.args, вы можете нарезать их следующим образом:
# main.py
# a for loop to iterate over this
subprocess.check_call(["python", "chunk.py", "0", "20000"])
# chunk.py a b
for count,f in enumerate(file_list_chunks):
if count < int(sys.argv[1]) or count > int(sys.argv[2]):
pass
# do stuff
Таким образом, подпроцесс должным образом очистит питон (утечки памяти не будет, поскольку процесс будет прерван).
Моя ставка в том, что Hammer 1 - это то, что нужно, такое ощущение, что вы склеиваете много данных и без необходимости считываете их в списки Python, а использование sqlite3 (или другой базы данных) полностью избегает этого.