Python 32-разрядные ограничения памяти на 64-битных окнах

17

У меня проблема с памятью, которую я не могу понять.

Я нахожусь на 64-битной машине Windows с 8 ГБ памяти и запускаю 32-битную программу python.

Программы читают 5,118 зашифрованных numpy-файлов (npz). Windows сообщает, что файлы занимают 1,98 ГБ на диске

Каждый файл npz содержит две части данных: 'arr_0' имеет тип np.float32 и 'arr_1' имеет тип np.uint8

Скрипт python считывает каждый файл, добавляя свои данные в два списка, а затем закрывает файл.

Вокруг файла 4284/5118 программа выдает исключение MemoryException

Однако диспетчер задач говорит, что использование памяти python.exe * 32 при возникновении ошибки составляет 1,854,848K ~ = 1,8 ГБ. Гораздо меньше, чем мой 8-гигабайтный лимит, или предполагаемый 4-гигабайтный предел 32-битной программы.

В программе я улавливаю ошибку памяти, и она сообщает: Каждый список имеет длину 4285. Первый список содержит в общей сложности 1 928 588 480 данных float32 ~ = 229,9 МБ. Второй список содержит 12 342 966 272 uint8's ~ = 1,471.3MB данных.

Итак, все, кажется, проверяется. За исключением части, где я получаю ошибку памяти. У меня больше памяти, и файл, с которым он падает, составляет ~ 800 КБ, поэтому он не пропустил чтение огромного файла.

Кроме того, файл не поврежден. Я могу прочитать его просто отлично, если я не буду использовать всю эту память заранее.

Чтобы сделать вещи более запутанными, все это, похоже, отлично работает на моей машине Linux (хотя у нее есть 16 ГБ памяти, а не 8 ГБ на моей машине с Windows), но, похоже, это не машина ОЗУ, которая вызывает эту проблему.

Почему Python бросает ошибку памяти, когда я ожидаю, что он сможет выделить еще 2 ГБ данных?     

задан Erotemic 17.08.2013 в 00:10
источник
  • Объем физической памяти, который у вас есть, не имеет значения. В Windows у вас всегда есть своп, хотите ли вы этого или нет. –  abarnert 17.08.2013 в 00:14
  • Когда это работает на вашей Linux-машине ... это тоже с 32-разрядным Python? –  abarnert 17.08.2013 в 00:14
  • можете ли вы опубликовать код, который вы используете для загрузки файла .npz? если вы используете np.load (файл, mmap_mode = 'r +'), он будет использовать гораздо меньше памяти, так как с помощью этого аргумента вы откроете массив с отображением памяти ... –  Saullo Castro 17.08.2013 в 00:15
  • Проблема связана не с самим python, а с numpy и scipy, для чего нужен 64-битный fortran-компилятор. Единственный существующий, если от Intel, если я правильно помню и не свободен. Я знаю, что вы можете использовать WinPyton, так что это возможно. Тем не менее, вы должны использовать неофициальные двоичные файлы с веб-сайта, не связанного с python.org. –  J. Martinot-Lagarde 20.08.2013 в 09:08
  • @Erotemic: Что заставляет вас думать, что 32-битный Python более стабилен? В настоящее время большинство основных разработчиков находятся на 64-битных Unix-блоках. Было много ошибок и проблем с производительностью, когда изменения сделали 32-бит хуже, и никто не заметил в течение нескольких месяцев, и очень мало в другом направлении. Я могу понять проблемы сборки / инструментальной цепочки, но если ваша причина действительно думает, что сам Python нестабилен в 64 бит, вы ошибаетесь. –  abarnert 21.08.2013 в 21:25
Показать остальные комментарии

1 ответ

34

Я не знаю, почему вы думаете, что ваш процесс должен иметь доступ к 4 ГБ. Согласно ограничениям памяти для выпуска Windows в MSDN, в 64-разрядной версии Windows 7, по умолчанию 32 -битный процесс получает 2 ГБ. * Именно это и заканчивается.

Итак, есть ли способ обойти это?

Ну, вы можете создать пользовательскую сборку из 32-разрядного Python, которая использует флаг IMAGE_FILE_LARGE_ADDRESS_AWARE , и перестроить numpy и все ваши другие модули расширения. Я не могу обещать, что весь соответствующий код действительно безопасен для работы с флагом с большим адресом; есть хороший шанс, но если кто-то уже это сделал и не протестировал, «хороший шанс» - это лучший, кто может знать.

Или, что более очевидно, просто используйте 64-битный Python.

Объем физической ОЗУ не имеет значения. Вы, кажется, думаете, что у вас есть «ограничение 8 ГБ» с 8 ГБ ОЗУ, но это не так, как это работает. Ваша система занимает всю вашу RAM плюс любое место подкачки, в котором она нуждается , и делит ее между приложениями; приложение может получить 20 ГБ виртуальной памяти, не получая ошибку памяти даже на машине 8 ГБ. Между тем, 32-разрядное приложение не имеет доступа к более чем 4 ГБ, и ОС будет использовать некоторые из этих адресных пространств (половина из них по умолчанию в Windows), поэтому вы можете получить только 2 ГБ даже на машине с 8 ГБ это ничего не работает. (Нельзя сказать, что в современной ОС можно «ничего не запускать», но вы знаете, что я имею в виду.)

Итак, почему это работает в вашей Linux-коробке?

Поскольку ваш Linux-модуль настроен на предоставление 32-разрядных процессов на 3,5 ГБ виртуального адресного пространства, или 3,99 ГБ, или ... Ну, я не могу сказать вам точный номер, но каждый дистрибутив, который я видел много лет был настроен как минимум на 3,25 ГБ.

* Также обратите внимание, что вы даже не получаете полные 2 ГБ данных; вашей программы. Большая часть того, что ОС и его драйверы делают доступными для вашего кода, находится в другой половине, но некоторые биты сидят в вашей половине, вместе с каждой загружаемой DLL и любым необходимым пространством и другими вещами. Это не слишком много, но это не ноль.

    
ответ дан abarnert 17.08.2013 в 00:16
  • На самом деле вам не нужно компилировать exe на windows, IMAGE_FILE_LARGE_ADDRESS_AWARE - это всего лишь флаг в заголовке изображения (не то, что это когда-либо официально поддерживалось, но мы не судим;)). Кроме того, dlls не имеют права говорить в этом вопросе, чтобы начать, так что их не нужно менять в любом случае. –  Voo 17.08.2013 в 00:54
  • @Voo: Но весь ваш код, включая ваши DLL, должен быть безопасным для использования с включенным флагом. Если, скажем, Python и его стандартные модули расширения проверяют во время сборки, хотите ли вы поддерживать поддержку большого адреса и генерировать другой код в разных случаях, вам нужно будет перестроить все, а не только exe. Если они всегда имеют большой адрес, то вам не нужно ничего делать. И если они никогда не будут иметь больших адресов, то восстановление не поможет. Я не знаю никакой документации, которая говорит вам, какая из трех она ... –  abarnert 17.08.2013 в 02:59
  • Правда, хотя единственная причина, по которой этот код не сработает с IMAGE_FILE_LARGE_ADDRESS_AWARE, заключается в том, что он сломан для начала (подписанная математика указателя) или делает глупые трюки с битом верхнего порядка указателей. Я очень удивлен, что python делает это - где именно в коде? (GC Я предполагаю, что это почти единственная причина, по которой это может быть полезно). Хотелось бы посмотреть на это. –  Voo 17.08.2013 в 16:28
  • @Voo: У меня нет абсолютно никакой идеи, если Python или любые модули Python, от которых зависит OP, делают такую ​​вещь. Я не думаю, что это возможно, но я не могу этого гарантировать. Очевидно, что по какой-то причине он не построен с IMAGE_FILE_LARGE_ADDRESS_AWARE из коробки; я предполагаю, что причина в том, что до сих пор ни один из разработчиков никогда не обнаружил, что стоит тестировать и / или очищать исходный код, потому что, если им действительно нужно больше 2 ГБ, они просто используют 64-битную сборку. Но это всего лишь предположение, поэтому мой ответ сказал, что есть хорошие шансы, что это сработает, но я не могу обещать. –  abarnert 19.08.2013 в 21:22
  • Да, но если нет компилятора для этого, что python зависит от того, что тогда вся работа по перестройке python будет делать ровно ничто по сравнению с просто изменением одного бита в заголовке, который был моей точкой. И я действительно не вижу, как python может использовать все, что полагается на бит высокого порядка, неиспользуемый - ведь * nix обычно не дает таких гарантий, и там работает python. –  Voo 19.08.2013 в 21:33