Очистить память видеокарты python

Обновлено: 06.07.2024

Я написал программу на Python, которая работает с большим входным файлом, создавая несколько миллионов объектов, представляющих треугольники. Алгоритм:

  1. читать входной файл
  2. обработать файл и создать список треугольников, представленных их вершинами
  3. Выведите вершины в формате OFF: список вершин, за которым следует список треугольников. Треугольники представлены индексами в списке вершин

Требование OFF, чтобы я распечатывал полный список вершин перед тем, как распечатать треугольники, означает, что я должен держать список треугольников в памяти, прежде чем записать вывод в файл. В то же время я получаю ошибки памяти из-за размеров списков.

Как лучше всего сказать Python, что мне больше не нужны некоторые данные, и их можно освободить?

Почему бы не распечатать треугольники в промежуточный файл и не прочитать их снова, когда они понадобятся? Этот вопрос потенциально может быть о двух совершенно разных вещах. Это ошибки одного и того же процесса Python , и в этом случае мы заботимся о том, чтобы освободить память для кучи процесса Python, или они связаны с различными процессами в системе, и в этом случае мы заботимся об освобождении памяти для ОС?

В соответствии с официальной документацией Python вы можете заставить сборщик мусора освобождать память, на которую нет ссылок gc.collect() . Пример:

В любом случае, вещи часто собираются мусором, за исключением некоторых необычных случаев, поэтому я не думаю, что это сильно поможет. В общем, gc.collect () следует избегать. Сборщик мусора знает, как делать свою работу. Тем не менее, если ОП находится в ситуации, когда он внезапно освобождает множество объектов (например, от миллионов), gc.collect может оказаться полезным. Фактически вызов gc.collect() себя в конце цикла может помочь избежать фрагментации памяти, что, в свою очередь, помогает поддерживать производительность. Я видел, как это существенно Я использую Python 3.6. Вызов gc.collect() после загрузки кадра данных pandas из hdf5 (500 тыс. Строк) уменьшил использование памяти с 1,7 ГБ до 500 МБ Мне нужно загрузить и обработать несколько массивов по 25 ГБ в системе с 32 ГБ памяти. Использование с del my_array последующим gc.collect() после обработки массивом является единственным способом, которым память фактически освобождается, и мой процесс выживает, чтобы загрузить следующий массив.

К сожалению (в зависимости от вашей версии и выпуска Python) некоторые типы объектов используют «свободные списки», которые представляют собой аккуратную локальную оптимизацию, но могут вызвать фрагментацию памяти, в частности, делая все больше и больше памяти «выделенными» только для объектов определенного типа и тем самым недоступен «общему фонду».

Единственный действительно надежный способ гарантировать, что большое, но временное использование памяти ДОЛЖНО возвращать все ресурсы системе, когда это будет сделано, состоит в том, чтобы такое использование происходило в подпроцессе, который выполняет работу, требующую памяти, а затем завершается. В таких условиях операционная система выполнит свою работу и с удовольствием утилизирует все ресурсы, которые подпроцесс мог поглотить. К счастью, multiprocessing модуль делает такую ​​операцию (которая раньше была довольно болезненной) не слишком плохой в современных версиях Python.

В вашем случае использования кажется, что лучший способ для подпроцессов накапливать некоторые результаты и при этом гарантировать, что эти результаты доступны для основного процесса, - это использовать временные файлы (я имею в виду под временными, а НЕ файлы, которые автоматически уходить при закрытии, просто обычные файлы, которые вы явно удаляете, когда все с ними закончено).

Я пытаюсь получить результат работы нейронной сети, которую я уже обучил. На входе находится изображение размером 300х300. Я использую размер пакета 1, но я все еще получаю ошибку CUDA error: out of memory после того, как я успешно получил выходные данные для 25 изображений.

Я искал некоторые решения в интернете и наткнулся на torch.cuda.empty_cache() . Но это все еще не решает проблему.

Это код, который я использую.

Этот for loop выполняется в течение 25 раз каждый раз, прежде чем выдавать ошибку памяти.

Каждый раз я посылаю новое изображение в сеть для вычисления. Таким образом, мне действительно не нужно хранить предыдущие результаты вычислений в GPU после каждой итерации цикла. Есть ли какой-то способ достичь этого?

Любая помощь будет оценена по достоинству. Спасибо.

1 ответ

Я пытаюсь следовать этому учебнику и сделать простое расширение c++ с бэкэндом CUDA. Моя реализация CPU, кажется, работает нормально. У меня возникли проблемы с поиском примеров и документации (похоже, что все постоянно меняется). Конкретно, Я вижу, что функции pytorch cuda получают аргумент.

Я тренирую PyTorch модели глубокого обучения на ноутбуке Jupyter-Lab, используя CUDA на Tesla K80 GPU для обучения. При выполнении обучающих итераций используется память 12 GB из GPU. Я заканчиваю обучение, сохраняя контрольную точку модели, но хочу продолжить использовать блокнот для дальнейшего.

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

В принципе, то, что делает PyTorch, заключается в том, что он создает вычислительный график всякий раз, когда я передаю данные через свою сеть, и сохраняет вычисления в памяти GPU, на случай, если я захочу вычислить градиент во время обратного распространения. Но поскольку я хотел выполнить только прямое распространение, мне просто нужно было указать torch.no_grad() для моей модели.

Таким образом, for loop в моем коде может быть переписан как:

Указание no_grad() в моей модели говорит PyTorch, что я не хочу хранить какие-либо предыдущие вычисления, тем самым освобождая пространство GPU.

Похожие вопросы:

Я запускаю программу C, где дважды вызываю функцию хоста cuda. Я хочу очистить память устройства между этими 2 вызовами. Есть ли способ очистить память устройства GPU?? Я нахожусь на Tesla M2050 с.

Я установил pytorch через conda с cuda 7.5 conda install pytorch=0.3.0 cuda75 -c pytorch >>> import torch >>> torch.cuda.is_available() True Я не делал никаких других установок для.

Я следую учебнику CIFAR-10 PyTorch на этой странице pytorch и не могу заставить PyTorch работать на GPU. Код точно такой же, как в учебнике. Ошибка, которую я получаю, такова Traceback (most recent.

Я пытаюсь следовать этому учебнику и сделать простое расширение c++ с бэкэндом CUDA. Моя реализация CPU, кажется, работает нормально. У меня возникли проблемы с поиском примеров и документации.

Я тренирую PyTorch модели глубокого обучения на ноутбуке Jupyter-Lab, используя CUDA на Tesla K80 GPU для обучения. При выполнении обучающих итераций используется память 12 GB из GPU. Я заканчиваю.

Я работал над геометрическим проектом PyTorch, используя Google Colab для поддержки CUDA. Поскольку его библиотека по умолчанию отсутствует, я выполняю: !pip install --upgrade torch-scatter !pip.

Как следует из названия, у меня есть предустановленные CUDA и cudnn (мой Tensorflow использует их). Версия CUDA-это 10.0 из nvcc --version . В versiuon из cudnn является 7.4 . Я пытаюсь установить.

Когда переменной Cuda в PyTorch присваивается новое значение, она снова становится переменной CPU (как показано в приведенном ниже коде). В этом случае память, удерживаемая переменной на GPU ранее.

В этом руководстве мы узнаем, как Python управляет памятью или как обрабатывает наши данные внутренне. Это руководство даст глубокое понимание основ управления памятью Python. Когда мы выполняем наш скрипт, в его памяти запускается много логических процессов, чтобы сделать код эффективным.

Вступление

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

В Python памятью управляет менеджер Python, который определяет, куда поместить данные приложения в память.

Предположим, что память выглядит как пустая книга, и мы хотим написать что-нибудь на странице книги. Когда мы записываем любые данные, менеджер находит свободное место в книге и передает их приложению. Процедура предоставления памяти объектам называется распределением.

С другой стороны, когда данные больше не используются, они могут быть удалены менеджером памяти Python. Но вопрос в том, как? А откуда взялось это воспоминание?

Распределение памяти Python

  • Распределение статической памяти
  • Распределение динамической памяти

Статической памяти

Размещение стека

Структура данных Stack используется для хранения статической памяти. Это необходимо только внутри конкретной функции или вызова метода. Функция добавляется в стек вызовов программы всякий раз, когда мы ее вызываем. Назначение переменной внутри функции временно сохраняется в стеке вызова функции; функция возвращает значение, и стек вызовов переходит к текстовой задаче. Компилятор обрабатывает все эти процессы, поэтому нам не нужно об этом беспокоиться.

Стек вызовов(структура данных стека) содержит рабочие данные программы, такие как подпрограммы или вызовы функций, в порядке их вызова. Эти функции выходят из стека при вызове.

Динамической памяти

Как мы знаем, все в Python является объектом, а это означает, что динамическое распределение памяти вдохновляет управление памятью Python. Диспетчер памяти Python автоматически исчезает, когда объект больше не используется.

Распределение памяти в куче

Структура данных кучи используется для динамической памяти, которая не имеет отношения к именованию аналогов. Это тип памяти, который используется вне программы в глобальном пространстве. Одним из лучших преимуществ кучи памяти является то, что она освобождает пространство памяти, если объект больше не используется или узел удален.

В приведенном ниже примере мы определяем, как переменная функции хранится в стеке и куче.

Реализация Python по умолчанию

В основном язык Python написан на английском языке. Однако это определено в справочном руководстве, что само по себе бесполезно. Итак, нам нужен код, основанный на интерпретаторе правила в руководстве.

Преимущество реализации по умолчанию заключается в том, что она выполняет код Python на компьютере, а также преобразует наш код Python в инструкции. Итак, мы можем сказать, что реализация Python по умолчанию удовлетворяет обоим требованиям.

Программа, которую мы пишем на языке Python, сначала преобразует в байт-код компьютерных инструкций. Виртуальная машина интерпретирует этот байт-код.

Сборщик мусора Python

Как мы объясняли ранее, Python удаляет те объекты, которые больше не используются, можно сказать, что освобождает пространство памяти. Этот процесс удаления ненужного пространства памяти объекта называется сборщиком мусора. Сборщик мусора Python запускает свое выполнение вместе с программой и активируется, если счетчик ссылок падает до нуля.

Когда мы назначаем новое имя или помещаем его в контейнеры, такие как словарь или кортеж, счетчик ссылок увеличивает свое значение. Если мы переназначаем ссылку на объект, счетчик ссылок уменьшает свое значение. Он также уменьшает свое значение, когда ссылка на объект выходит за пределы области видимости или объект удаляется.

Как мы знаем, Python использует динамическое выделение памяти, которым управляет структура данных Heap. Куча памяти содержит объекты и другие структуры данных, которые будут использоваться в программе. Диспетчер памяти Python управляет выделением или отменой выделения пространства кучи с помощью функций API.

Объекты Python в памяти

Как мы знаем, в Python все является объектом. Объект может быть простым(содержащий числа, строки и т. д.) или контейнером(словарь, списки или определенные пользователем классы). В Python нам не нужно объявлять переменные или их типы перед их использованием в программе.

Давайте разберемся в следующем примере.

Как видно из вышеприведенного вывода, мы присвоили значение объекту x и распечатали его. Когда мы удаляем объект x и пытаемся получить доступ в дальнейшем коде, будет ошибка, утверждающая, что переменная x не определена.

Следовательно, сборщик мусора Python работает автоматически, и программистам не нужно беспокоиться об этом, в отличие от C.

Подсчет ссылок в Python

Подсчет ссылок показывает, сколько раз другие объекты ссылаются на данный объект. Когда назначается ссылка на объект, счетчик объектов увеличивается на единицу. Когда ссылки на объект удаляются, количество объектов уменьшается. Когда счетчик ссылок становится равным нулю, диспетчер памяти Python выполняет освобождение. Давайте сделаем это простым для понимания.

Предположим, есть две или более переменных, которые содержат одно и то же значение, поэтому виртуальная машина Python скорее создает другой объект с таким же значением в частной куче. Фактически это указывает на то, что вторая переменная указывает на изначально существующее значение в частной куче.

Это очень полезно для сохранения памяти, которая может использоваться другой переменной.

Подсчет ссылок

Когда мы присваиваем значение переменной x, целочисленный объект 10 создается в памяти кучи, и его ссылка присваивается x.

y = x

В приведенном выше коде мы присвоили y = x, что означает, что объект y будет ссылаться на тот же объект, потому что Python выделил ту же ссылку на объект новой переменной, если объект уже существует с тем же значением.

Теперь посмотрим на другой пример.

Новый объект ссылки

Переменные x и y не ссылаются на один и тот же объект, потому что x увеличивается на единицу, x создает новый объект ссылки, а y по-прежнему ссылается на 10.

Преобразование сборщика мусора

Сборщик мусора Python классифицировал объекты с помощью своей генерации. Сборщик мусора Python имеет три поколения. Когда мы определяем новый объект в программе, его жизненный цикл обрабатывается первым поколением сборщика мусора. Если объект используется в другой программе, он будет стимулироваться до следующего поколения. У каждого поколения есть порог.

Сборщик мусора вступает в действие, если превышен порог количества выделений за вычетом числа отмененных.

Мы можем изменить пороговое значение вручную с помощью модуля GC. Этот модуль предоставляет метод get_threshold() для проверки порогового значения другого поколения сборщика мусора. Давайте разберемся в следующем примере.

Пороговое значение для запуска сборщика мусора можно изменить с помощью метода set_threshold().

В приведенном выше примере значение порога увеличилось для всех трех поколений. Это повлияет на частоту запуска сборщика мусора. Программистам не нужно беспокоиться о сборщике мусора, но он играет важную роль в оптимизации среды выполнения Python для целевой системы.

Сборщик мусора Python обрабатывает низкоуровневые детали для разработчика.

Важность выполнения ручной сборки мусора

Как мы обсуждали ранее, интерпретатор Python обрабатывает ссылку на объект, используемый в программе. Он автоматически освобождает память, когда счетчик ссылок становится равным нулю. Это классический подход для подсчета ссылок, если он не работает, когда программа ссылается на циклы. Цикл ссылок происходит, когда один или несколько объектов ссылаются друг на друга. Следовательно, счетчик ссылок никогда не становится нулевым.

Мы создали справочный цикл. Объект list1 ссылается на сам объект list1. Когда функция возвращает объект list1, память для объекта list1 не освобождается. Так что подсчет ссылок не подходит для решения ссылочного цикла. Но мы можем решить эту проблему, изменив сборщик мусора или производительность сборщика мусора.

Для этого мы будем использовать функцию gc.collect() для модуля gc.

Приведенный выше код даст количество собранных и освобожденных объектов.

Метод gc.collect() используется для выполнения сборки мусора по времени. Этот метод вызывается через фиксированный интервал времени для выполнения сборки мусора на основе времени.

При четной сборке мусора функция gc.collect() вызывается после возникновения события. Давайте разберемся в следующем примере.

В приведенном выше коде мы создали объект list1, на который ссылается переменная list. Первый элемент объекта списка ссылается на себя. Счетчик ссылок на объект списка всегда больше нуля, даже если он удален или выходит за рамки программы.

Управление памятью в Python

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

Python использует часть памяти для внутреннего использования и необъектную память. Другая часть памяти используется для объекта Python, такого как int, dict, list и т. д.

CPython содержит распределитель объектов, который выделяет память в области объекта. Распределитель объектов получает вызов каждый раз, когда новому объекту требуется место. Распределитель предназначен для небольшого количества данных, потому что Python не задействует слишком много данных за раз. Он выделяет память, когда это абсолютно необходимо.

Стратегия выделения памяти CPython состоит из трех основных компонентов.

Распространенные способы уменьшить сложность пространства

Мы можем следовать некоторым передовым методам, чтобы уменьшить сложность пространства. Эти методы должны сэкономить достаточно места и сделать программу эффективной. Ниже приведены несколько практик в Python для распределителей памяти.

Мы определяем список на Python; распределитель памяти распределяет память кучи соответственно индексации списка. Предположим, нам нужен подсписок к данному списку, затем мы должны выполнить нарезку списка. Это простой способ получить подсписок из исходного списка. Он подходит для небольшого количества данных, но не подходит для емких данных.

Следовательно, нарезка списка генерирует копии объекта в списке, просто копирует ссылку на них. В результате распределитель памяти Python создает копию объекта и выделяет ее. Поэтому нам нужно избегать разрезания списка.

Лучший способ избежать этого: разработчик должен попытаться использовать отдельную переменную для отслеживания индексов, а не нарезать список.

Разработчик должен попытаться использовать «для элемента в массиве» вместо «для индекса в диапазоне(len(array))» для экономии места и времени. Если программе не нужна индексация элемента списка, не используйте ее.

Конкатенация строк не подходит для экономии места и времени. По возможности, мы должны избегать использования знака «+» для конкатенации строк, потому что строки неизменяемы. Когда мы добавляем новую строку к существующей строке, Python создает новую строку и присваивает ее новому адресу.

Каждая строка требует фиксированного размера памяти в зависимости от символа и его длины. Когда мы меняем строку, ей требуется другой объем памяти и ее необходимо перераспределить.

Мы создали переменную a для ссылки на строковый объект, который является информацией о строковом значении.

Затем мы добавляем новую строку с помощью оператора «+». Python перераспределяет новую строку в памяти в зависимости от ее размера и длины. Предположим, что размер памяти исходной строки равен n байтам, тогда новая строка будет размером m байтов.

Вместо использования конкатенации строк мы можем использовать «.join(iterable_object)» или формат или%. Это оказывает огромное влияние на экономию памяти и времени.

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

В следующем примере мы реализуем итератор, который вызывает специальную функцию генератора. Ключевое слово yield возвращает текущее значение, переходя к следующему значению только на следующей итерации цикла.

  • По возможности используйте встроенную библиотеку

Если мы используем методы, которые уже были предопределены в библиотеке Python, импортируйте соответствующую библиотеку. Это сэкономит много места и времени. Мы также можем создать модуль, чтобы определить функцию и импортировать ее в текущую рабочую программу.

Заключение

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

когда загружается первая модель, она предварительно выделяет всю память GPU (которую я хочу для работы с первой партией данных). Но он не разгружает память, когда закончит. Когда загружается вторая модель, используя оба tf.reset_default_graph() и with tf.Graph().as_default() память GPU по-прежнему полностью потребляется из первой модели, а вторая модель тогда изголодалась по памяти.

есть ли способ решить эту проблему, кроме использования подпроцессов Python или многопроцессорной обработки для решения проблемы (единственное решение, которое я нашел в Google searches)?

В настоящее время распределитель в GPUDevice принадлежит ProcessState, который, по сути, является глобальным синглтоном. Первый сеанс с использованием GPU инициализирует его и освобождается, когда процесс завершается.

таким образом, единственным обходным путем было бы использование процессов и выключение их после вычисление.

Пример Кода:

поэтому, если вы вызовете функцию run_tensorflow() в процессе, который вы создали и завершили процесс (опция 1), память освобождается. Если вы просто запустите run_tensorflow() (вариант 2) память не освобождается после вызова функции.

память GPU, выделенная тензорами, освобождается (обратно в пул памяти TensorFlow), как только тензор больше не нужен (до .запуск вызова завершается). Память GPU, выделенная для переменных, освобождается при уничтожении контейнеров переменных. В случае DirectSession (т. е. sess=tf.Session ("")) это когда сеанс закрыт или явно сброшен (добавлен в 62c159ff)

теперь, похоже, есть два способа решить итеративную модель обучения или если вы используете будущий пул multipleprocess для обслуживания обучения модели, где процесс в пуле не будет убит, если будущее завершено. Вы можете применить два метода в процессе обучения, чтобы освободить память GPU, в то время как вы хотите сохранить основной процесс.

  1. вызов подпроцесса для запуска обучения модели. когда одна фаза обучения завершена, подпроцесс выйдет и освободит память. Это легко получить возвращаемое значение.
  2. звонок многопроцессорных.Процесс (p) для запуска обучения модели(p.start), и p.join будет указывать на выход процесса и свободную память.

вот вспомогательная функция, использующая multiprocess.Процесс, который может открыть новый процесс для запуска вашей написанной функции python и значения reture вместо использования подпроцесса,

Читайте также: