2011-01-27 14 views
6

Я читаю documentation for Memory Management in Python C extensions, и, насколько я могу судить, на самом деле нет смысла использовать malloc, а не PyMem_Malloc. Предположим, что я хочу выделить массив, который не должен подвергаться исходному коду Python, и будет храниться в объекте, который будет собираться мусором. Есть ли причина использовать malloc?Есть ли причина использовать malloc над PyMem_Malloc?

ответ

5

EDIT: Смешанные PyMem_Malloc и PyObject_Malloc исправления; это два разных вызова.

Без PYMALLOC_DEBUG макро активации PyMem_Malloc является псевдонимом Libc-х malloc(), имеющий один частный случай: вызов PyMem_Malloc выделить ноль байт возвращает ненулевой указатель, в то время как таНос (zero_bytes) может возвращать значение NULL или приподнять системная ошибка (source code reference):

/* malloc. Обратите внимание, что nbytes == 0 пытается вернуть не-NULL-указатель, отличный от * из всех других текущих указателей. Это может быть невозможно. */

Кроме того, есть консультативный примечание на pymem.h header file:

Никогда не смешивать вызовы PyMem_ с вызовов на платформе таНос/перераспределить/ calloc/бесплатно.Например, в Windows разные библиотеки DLL могут закончиться использованием разных кучек , и если вы используете PyMem_Malloc, вы получите память из кучи, используемой библиотекой Python ; это может быть катастрофой, если вы free() 'ed, который находится прямо в вашем собственном расширении . Использование PyMem_Free вместо гарантирует, что Python может вернуть память в нужную кучу. В качестве другого например, в режиме PYMALLOC_DEBUG, Python оборачивает все вызовы на все функции памяти PyMem_ и PyObject_ в специальных отладочных обертках, которые добавляют дополнительной информации отладки в динамических блоки памяти. Система подпрограмм не имеет понятия, что делать с этим материалом, а обертки Python понятия не имеют, что делать с необработанными блоками, полученными непосредственно с помощью .

Затем, есть некоторые Python конкретные тюнинги внутри PyMem_Malloc PyObject_Malloc, функция используется не только для расширения C, но и для всех динамических распределений при выполнении программы на Python, как 100*234, str(100) или 10 + 4j:

>>> id(10 + 4j) 
139721697591440 
>>> id(10 + 4j) 
139721697591504 
>>> id(10 + 4j) 
139721697591440 

Предыдущие экземпляры complex() - это небольшие объекты, выделенные в выделенном пуле.

Малых объектов (< 256 байт) распределение с PyMem_Malloc PyObject_Malloc является достаточно эффективным, так как это сделано из пула 8 байт выровненных блоков, существующих один пула для каждого размера блока. Существуют также блоки Pages и Arenas для больших распределений.

Этот комментарий на source code объясняет, как оптимизирован PyObject_Malloc вызов:

/* 
* The basic blocks are ordered by decreasing execution frequency, 
* which minimizes the number of jumps in the most common cases, 
* improves branching prediction and instruction scheduling (small 
* block allocations typically result in a couple of instructions). 
* Unless the optimizer reorders everything, being too smart... 
*/ 

бассейнов, страница и арены оптимизации, предназначенные для снижения external memory fragmentation программ Python долго работающих.

Для получения полной подробной документации по внутренней памяти Python ознакомьтесь с the source code.

+0

Родные распределители уже чрезвычайно оптимизированы. Это не влияет на модули расширения - во всяком случае, это просто дополнительные накладные расходы, чтобы замедлить работу. –

+0

@Glenn У вас твердые цифры, чтобы поддержать это? Документы довольно обширны и детализированы, чтобы быть просто «накладными расходами, чтобы замедлить работу». – vz0

+0

У меня есть опыт и здравый смысл: системный распределитель влияет на скорость всей системы, поэтому приложить немало усилий для их оптимизации. Если вы утверждаете, что Python улучшает это - для использования модуля расширения, а не только для частного случая низкоуровневого ядра Python - это ваше требование о резервном копировании. –

1

Из моего опыта написания функций MATLAB .mex, я считаю, что самым важным определяющим фактором в том, используете ли вы malloc или нет, является переносимость. Скажем, у вас есть файл заголовка, который выполняет загрузку полезных функций только с использованием внутренних типов данных c (нет необходимости в взаимодействии с объектом Python, поэтому нет проблем с использованием malloc), и вы вдруг осознаете, что хотите перенести этот заголовочный файл на другую кодовую базу, которая имеет не имеет ничего общего с Python (возможно, это проект, написанный исключительно на C), использование malloc, очевидно, будет гораздо более переносимым решением.

Но для вашего кода, который является чисто расширением Python, моей первоначальной реакцией было бы ожидать, что функция c родного языка будет выполняться быстрее. У меня нет доказательств, подтверждающих это:

+2

Вы правы, чтобы ожидать, что родная функция C будет работать лучше. Дело в том, что они являются и родными функциями C. :-) –

+1

Ну, я думаю, они ... хорошая техничность :) Я предполагаю, что я имел в виду больше, что я ожидал бы, что система распределения памяти на C будет быстрее, чем встроенный C для системы распределения памяти Python: P – William

+2

Я использовал как malloc, так и PyMem_Malloc. В моих тестах наблюдалась небрежная разница в производительности между ними. Возможно, PyMem_Malloc был быстрее, но я не думаю, что разница была статистически релевантной. YMMV, конечно. – casevh

6

Совершенно нормально для расширений для распределения памяти с помощью malloc или других системных распределителей. Это нормально и неизбежно для многих типов модулей - большинство модулей, которые обертывают другие библиотеки, которые сами ничего не знают о Python, вызовут собственные распределения, когда они произойдут в этой библиотеке. (Некоторые библиотеки позволяют вам контролировать выделение, чтобы этого не было, а большинство нет.)

Существует серьезный недостаток использования PyMem_Malloc: вам нужно удерживать GIL при его использовании. Родные библиотеки часто хотят выпустить GIL при выполнении вычислений с интенсивным использованием процессора или совершать любые вызовы, которые могут блокироваться, например I/O. Необходимость блокировки GIL перед распределением может быть где-то между очень неудобной и проблемой производительности.

Использование оболочек Python для распределения памяти позволяет использовать код отладки памяти Python. Однако с такими инструментами, как Valgrind, я сомневаюсь в реальной ценности этого.

Вам необходимо использовать эти функции, если это требует API; например, если API передан указатель, который должен быть выделен этими функциями, поэтому он может быть освобожден вместе с ними. Запрещая явную причину подобного использования, я придерживаюсь нормального распределения.

Смежные вопросы