Во-первых, я хочу подтвердить то же поведение в 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
не выглядит подходящим поведением для такой простой программы.