Источником проблемы является то, что объект, возвращаемый из UDF, не соответствует объявленному типу. np.unique
не только возвращает numpy.ndarray
, но и преобразует числовые значения в соответствующие NumPy
типы , которые не совместимы с DataFrame
API. Вы можете попробовать что-то вроде этого:
udf(lambda x: list(set(x)), ArrayType(IntegerType()))
или это (чтобы сохранить порядок)
udf(lambda xs: list(OrderedDict((x, None) for x in xs)),
ArrayType(IntegerType()))
.
Если вы действительно хотите np.unique
вам нужно преобразовать вывод:
udf(lambda x: np.unique(x).tolist(), ArrayType(IntegerType()))
Память, выделенная на куче, может быть подвергнута воздействию водяных знаков. Это осложняется внутренней оптимизацией Python для выделения небольших объектов (PyObject_Malloc
) в 4 пулах KiB, классифицированных для размеров размещения в кратных 8 байтах - до 256 байт (512 байт в 3.3). Сами пулы находятся в 256 KiB аренах, поэтому, если используется только один блок в одном пуле, вся 256 игровая зона KiB не будет выпущена. В Python 3.3 распределитель малых объектов был переключен на использование анонимных карт памяти вместо кучи, поэтому он должен лучше работать при освобождении памяти.
Кроме того, встроенные типы поддерживают фрилистов ранее выделенных объектов, которые могут или не может использовать распределитель малых объектов. Тип int
поддерживает freelist с собственной выделенной памятью, и для его очистки требуется вызвать PyInt_ClearFreeList()
. Это можно косвенно назвать полным gc.collect
.
Попробуй так, и скажи мне, что получишь. Вот ссылка на psutil .
import os
import gc
import psutil
proc = psutil.Process(os.getpid())
gc.collect()
mem0 = proc.get_memory_info().rss
# create approx. 10**7 int objects and pointers
foo = ['abc' for x in range(10**7)]
mem1 = proc.get_memory_info().rss
# unreference, including x == 9999999
del foo, x
mem2 = proc.get_memory_info().rss
# collect() calls PyInt_ClearFreeList()
# or use ctypes: pythonapi.PyInt_ClearFreeList()
gc.collect()
mem3 = proc.get_memory_info().rss
pd = lambda x2, x1: 100.0 * (x2 - x1) / mem0
print "Allocation: %0.2f%%" % pd(mem1, mem0)
print "Unreference: %0.2f%%" % pd(mem2, mem1)
print "Collect: %0.2f%%" % pd(mem3, mem2)
print "Overall: %0.2f%%" % pd(mem3, mem0)
Выход:
Allocation: 3034.36%
Unreference: -752.39%
Collect: -2279.74%
Overall: 2.23%
Редактирование:
Я переключился на измерение относительно размер VM процесса для устранения эффектов других процессов в системе.
Среда выполнения C (например, glibc, msvcrt) сжимает кучу, когда смежное свободное пространство наверху достигает постоянного, динамического или настраиваемого порога , С помощью glibc вы можете настроить это с помощью mallopt
(M_TRIM_THRESHOLD). Учитывая это, неудивительно, что куча сжимается больше - даже намного больше - чем блок, который вы free
.
В 3.x range
не создается список, поэтому в приведенном выше тесте не будет создано 10 миллионов int
объектов. Даже если это так, тип int
в 3.x в основном представляет собой 2.x long
, который не реализует freelist.
eryksun ответил на вопрос №1, и я ответил на вопрос № 3 (оригинал № 4), но теперь давайте ответим на вопрос №2:
Почему он выпускает 50.5mb в конкретный - какова сумма, выпущенная на основе?
blockquote>. На ее основе лежит, в конечном счете, целая серия совпадений внутри Python и
malloc
, которые очень трудно предсказать.Во-первых, в зависимости от того, как вы измеряете память, вы можете только измерять страницы, фактически отображаемые в памяти. В этом случае всякий раз, когда страница выгружается пейджером, память будет отображаться как «освобожденная», даже если она не была освобождена.
Или вы можете измерять используемые страницы, которые могут или не могут рассчитывать выделенные, но никогда не затронутые страницы (в системах, которые оптимистически перенастраиваются, например, linux), страницы, которые выделяются, но помечены
MADV_FREE
и т. д.Если вы действительно измерение выделенных страниц (на самом деле это не очень полезная вещь, но, похоже, это то, о чем вы спрашиваете), и страницы действительно были освобождены, два обстоятельства, в которых это может произойти: либо вы использовали
brk
или эквивалент для сокращения сегмента данных (очень редко в настоящее время), или вы использовалиmunmap
или аналогичные для выпуска сопоставленного сегмента. (Существует также теоретически незначительный вариант для последнего, поскольку есть способы освободить часть отображенного сегмента, например, украсть его с помощьюMAP_FIXED
для сегментаMADV_FREE
, который вы сразу же отключите.)Но большинство программ напрямую не выделяют страницы из памяти; они используют
malloc
-статический распределитель. Когда вы вызываетеfree
, распределитель может выпустить только страницы в ОС, если вы простоfree
используете последний живой объект в сопоставлении (или на последних N страницах сегмента данных).CPython делает это еще более сложным - у него есть настраиваемый 2-уровневый распределитель объектов поверх специализированного распределителя памяти на вверху
malloc
. (См. исходные комментарии для более подробного объяснения.) И, кроме того, даже на уровне C API, гораздо меньше Python, вы даже не контролируете прямо, когда объекты верхнего уровня deallocated.Итак, когда вы отпускаете объект, как вы узнаете, собирается ли он выпустить память в ОС? Сначала вы должны знать, что вы выпустили последнюю ссылку (включая любые внутренние ссылки, о которых вы не знали), позволяя GC ее освободить. (В отличие от других реализаций, по крайней мере, CPython освободит объект, как только ему будет разрешено.) Обычно он освобождает по меньшей мере две вещи на следующем уровне вниз (например, для строки вы отпускаете объект
PyString
и строковый буфер).Если вы do освободите объект, чтобы узнать, приведет ли это к тому, что следующий уровень не освободит блок хранения объектов, вам необходимо знать внутреннее состояние распределителя объектов, а также как это реализовано. (Это, очевидно, не может произойти, если вы не освободите последнее в блоке, и даже тогда этого может не произойти.)
Если вы сделали , освободите блок чтобы узнать, вызывает ли это вызов
free
, вы должны знать внутреннее состояние распределителя PyMem, а также то, как оно реализовано. (Опять же, вы должны освободить последний используемый блок в пределах областиmalloc
ed, и даже тогда это может не произойти.)Если вы сделали
free
amalloc
ed region, чтобы узнать, вызывает ли этоmunmap
или эквивалент (илиbrk
), вы должны знать внутреннее состояниеmalloc
, а также то, как оно реализовано. И этот, в отличие от других, очень специфичен для платформы. (И снова вам, как правило, приходится снимать последнее использованиеmalloc
в сегментеmmap
, и даже тогда этого может не случиться.)Итак, если вы хотите понять, почему это случилось, чтобы выпустить ровно 50,5 Мб, вам придется проследить его снизу вверх. Почему
malloc
удалил страницы размером 50,5 МБ, когда вы сделали эти одни или несколько вызововfree
(возможно, чуть больше 50.5 Мб)? Вам нужно будет прочитать вашу платформуmalloc
, а затем пройти различные таблицы и списки, чтобы увидеть ее текущее состояние. (На некоторых платформах он может даже использовать информацию на системном уровне, что практически невозможно захватить, не делая моментальный снимок системы для проверки в автономном режиме, но, к счастью, обычно это не проблема). И тогда вы должны делайте то же самое на 3 уровнях выше этого.Итак, единственный полезный ответ на вопрос: «Потому что».
Если вы не используете ограниченную ресурсами (например, встроенную) разработку, у вас нет причин заботиться об этих деталях.
И если вы являются делая ограниченную ресурсами разработку, зная, что эти детали бесполезны; вы в значительной степени должны выполнять конечный запуск всех этих уровней и, в частности,
mmap
необходимую вам память на уровне приложения (возможно, с одним простым, хорошо понятным распределителем зоны приложения, находящимся между ними).