Python сталкивается с накладными расходами каждые 98 казней? - PullRequest
3 голосов
/ 20 июня 2019

У меня большая база данных, я просто хочу присвоить константу новому столбцу.При первых казнях (от 1 до 97);все хорошо, код работает быстро.Затем память выполняет ракеты на итерации 98, затем все в порядке до итерации 196 (98 итераций после), где снова запускается ракета RAM, затем цикл продолжает ракеты памяти на каждом i, где i - это умножение на 98 ...

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

Вот мой код

Edit : Я думаю, что это не сборка мусора, потому что gc.isenabled() возвращает False в конце кода

import pandas as pd
import numpy as np

n = 2000000
data = pd.DataFrame({'a' : range(n)})
for i in range(1, 100):
    data['col_' + str(i)] = np.random.choice(['a', 'b'], n)

gc.disable()
for i in range(1, 600):
    data['test_{}'.format(i)] = i
    print(str(i)) # slow at every i multiplication of 98

gc.isenabled()
> False

А вот мое использование памяти, пики на итерации i*98 (где i - целое число)

Я на Windows 10, Python 3.6.1 |Анаконда 4.4.0 |Панды 0.24.2

У меня 16 ГБ ОЗУ и 8-ядерный процессор

enter image description here

1 Ответ

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

Во-первых, я хочу подтвердить то же поведение в Ubuntu с 16 ГБ ОЗУ и отключенным GC. Поэтому это определенно не проблема с управлением памятью в GC или Windows.

Во-вторых, в моей системе он замедляется после каждых 99 итераций: после 99, после 198, после 297 и т. Д. В любом случае, у меня есть довольно ограниченный файл подкачки, поэтому, когда память RAM + Swap заполнена, происходит сбой следующая трассировка стека:

294
295
296
297
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/indexes/base.py", line 2657, in get_loc
    return self._engine.get_loc(key)
  File "pandas/_libs/index.pyx", line 108, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/index.pyx", line 132, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 1601, in pandas._libs.hashtable.PyObjectHashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 1608, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 'test_298'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/internals/managers.py", line 1053, in set
    loc = self.items.get_loc(item)
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/indexes/base.py", line 2659, in get_loc
    return self._engine.get_loc(self._maybe_cast_indexer(key))
  File "pandas/_libs/index.pyx", line 108, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/index.pyx", line 132, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 1601, in pandas._libs.hashtable.PyObjectHashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 1608, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 'test_298'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "py-memory-test.py", line 12, in <module>
    data['test_{}'.format(i)] = i
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/frame.py", line 3370, in __setitem__
    self._set_item(key, value)
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/frame.py", line 3446, in _set_item
    NDFrame._set_item(self, key, value)
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/generic.py", line 3172, in _set_item
    self._data.set(key, value)
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/internals/managers.py", line 1056, in set
    self.insert(len(self.items), item, value)
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/internals/managers.py", line 1184, in insert
    self._consolidate_inplace()
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/internals/managers.py", line 929, in _consolidate_inplace
    self.blocks = tuple(_consolidate(self.blocks))
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/internals/managers.py", line 1899, in _consolidate
    _can_consolidate=_can_consolidate)
  File "/usr/local/lib/python3.6/dist-packages/pandas/core/internals/blocks.py", line 3149, in _merge_blocks
    new_values = new_values[argsort]
MemoryError

Таким образом, кажется, что pandas иногда выполняет какое-то слияние / консолидацию / перепаковку при вставке. Давайте рассмотрим функцию core / internals / Manager.py insert, в которой есть следующие строки:

def insert(self, loc, item, value, allow_duplicates=False):
    ...
    self._known_consolidated = False

    if len(self.blocks) > 100:
        self._consolidate_inplace()

Я думаю, это именно то, что мы искали!

Каждый раз, когда мы делаем insert, создается новый блок. Когда количество блоков превышает некоторое ограничение, выполняется дополнительная работа (консолидация). Разница между пределом в 100 блоков в коде и полученными нами эмпирическими числами около 98-99 может быть объяснена наличием некоторых дополнительных метаданных данных, которые тоже требуют некоторого места.

UPD : чтобы проверить эту гипотезу, я попытался изменить 100 -> 1000000, и она работала просто отлично, без разрывов в производительности, нет MemoryError. Однако нет общедоступного API для изменения этого параметра во время выполнения, он просто жестко задан.

UPD2 : отправлена ​​проблема в pandas, поскольку MemoryError не выглядит подходящим поведением для такой простой программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...