Форум сайта python.su
Всем привет. Столкнулся с таким поведением интерактивной оболочки ipython: экспериментирую с данными и мне иногда приходится делать вещи вроде np.random.random(100_000_000). После того, как ввожу данную команду в ipython в интерактивном режиме (без присваивания результата какой-либо переменной) системной памяти становится меньше на ~750 мб (os windows7, python 3.6.4 64-bit). При повторном вводе еще на -750 мб - в диспетчере задач эта память потребляется процессом python (не ipython). И так пока вся память не заканчивается, и я не получаю ошибку MemoryError. Для того, чтобы освободить память приходится перезапускать ipython.
Если использовать стандартный интерпретатор python, то поведение иное. При первом вводе выражения под его результат аллоцируется память и не очищается, но при вводе следующего выражения (любого) - память от предыдущего выражения очищается и процесс python занимает ровно столько памяти сколько требует последнее выражение.
Если вначале присваивать выражение требующее много памяти переменной, например a = np.random.random(100_000_000) и затем руками писать del a, тогда память корректно освобождается и в python, и в ipython. Но так делать неудобно по понятным причинам - часто для выражений не нужны никакие переменные, а интересует только их результат в repl.
gc.collect() в ipython тоже ничего не очищает.
В соответствии с этим у меня 2 вопроса:
1. Как привести поведение ipython хотя бы к поведению стандартного интерпретатора python, чтобы не было утечек и необходимости перезапускать ipython?
2. Можно ли как-то настроить стандартный python-shell так, чтобы после того как он вычислил выражение, то сразу же подчищал за ним память, если на него не ссылается ни одна переменная?
Отредактировано xzvf (Июнь 2, 2018 12:49:56)
Офлайн
Пока что нагуглились только вариант удалять кеш (_, _1, _3, … _N) вручную с помощью %xdel или полностью очищать неймспейс текущей конфигурации с помощью команды get_ipython().reset(), которая собирает (collect) все пользовательские переменные. Но это все не то.
Офлайн
? (справка)
* Output caching system:
For output that is returned from actions, a system similar to the input
cache exists but using _ instead of _i. Only actions that produce a result
(NOT assignments, for example) are cached. If you are familiar with
Mathematica, IPython's _ variables behave exactly like Mathematica's %
variables.
The following GLOBAL variables always exist (so don't overwrite them!):
_ (one underscore): previous output.
__ (two underscores): next previous.
___ (three underscores): next-next previous.
Global variables named _<n> are dynamically created (<n> being the prompt
counter), such that the result of output <n> is always available as _<n>.
Finally, a global dictionary named _oh exists with entries for all lines
which generated output.
Офлайн
>> Зачем такие большие данные без присваивания?
Без присваивания удобно, чтобы посмотреть сразу результат, а не использовать потом еще print() и подобное. Так я могу и в ide без ipython. Кроме того, лишние переменные мне в неймспейсе не особо нужны. И память занятую ими все равно придется освобождать вручную (del, %xdel, .collect).
random.random - приведен для примера, это может быть и ф-ция, которая применяется к такому большому датасету и возвращает 1 или 0. Или если ты хочешь взять срез с этого датасета, чтобы посмотреть какие-то данные - память расходуется в полном объеме, даже если это первые 10 элементов.
Сейчас вот как раз пробую настроить систему кеширования. Пока что получилось добиться следующего поведения - сохраняются и, соответственно, занимают память только последние 3 выражения (даже если они одинаковые). Для этого необходимо в конфигурационном файле ipython прописать: c.TerminalInteractiveShell.cache_size = 0. Но не совсем понятно, почему кеш все-таки не 0, а по факту получается 3. В доке указано, что все значения меньше 3х приводятся к нулю. Вероятно, это как раз остались _, __, ___.
Опять же их можно удалять вручную с помощью %xdel. Но хотелось бы все-таки как-то настроить ipython-shell так, чтобы в памяти ну хотя бы сохранялась только _, как это реализовано, например, в стандартном python-shell (не ipython). А в идеале избавиться и от этой переменной :-)
Отредактировано xzvf (Июнь 3, 2018 00:31:51)
Офлайн
Удалось добиться поведения стандартного python-shell заменой self.__ и self.___ на None в исходниках IPython/core/displayhook.py. Наверное, если еще немного повозиться, можно организовать и очистку памяти от последнего выражения (_), но пока и так уже лучше, чем было.
Офлайн
xzvfКешируется только вывод. Какая функция тормозит, если вывода этих данных нет?
random.random - приведен для примера, это может быть и ф-ция, которая применяется к такому большому датасету и возвращает 1 или 0.
xzvfТак в numpy не итераторы же, сначала создаётся всё полностью и потом для него создаётся срез.
Или если ты хочешь взять срез с этого датасета, чтобы посмотреть какие-то данные - память расходуется в полном объеме, даже если это первые 10 элементов.
xzvfОчень сомнительно, что ты сможешь просмотреть 100000000 элементов в выводе. Наверное, поэтому там и не сделано ничего, чтобы это можно было просматривать, чтобы это не кешировалось.
Без присваивания удобно, чтобы посмотреть сразу результат, а не использовать потом еще print() и подобное.
Отредактировано py.user.next (Июнь 3, 2018 05:08:02)
Офлайн
>> Кешируется только вывод. Какая функция тормозит, если вывода этих данных нет?
Есть вывод других более мелких данных, вывод которых накапливается и отжирает память. Ну и срез я уже приводил как пример.
>> сначала создаётся всё полностью и потом для него создаётся срез.
Очевидно. Но результат выражения срез, а не изначальный набор данных. Поэтому после применения среза можно было бы сразу освобождать память от остальных данных, если они не привязаны к переменной.
>> К переменным _ __ ___ цепляется только то, что ты выводил в консоль, а не то, что ты там вообще создавал.
Я в курсе.
>> Поэтому используй присваивание и ничего не будет цепляться к _ , потому что оно не будет выводиться при присваивании.
Как я уже писал ранее, этот вариант не подходит. Меня устраивает даже вариант поведения стандартного python-интерпретатора. Там ведь все реализовано как надо, почему в ipython это поведение не унаследовали?
>> Очень сомнительно, что ты сможешь просмотреть 100000000 элементов в выводе
Я хочу просмотреть первые 10, или с 10000982 элемента по 10001182. А память будет резервироваться как для изначального набора данных. 2-3 раза посмотрел данные частично (например у тебя на графике аномалии, ты знаешь, где они и хочешь детализировать данные, которые находятся рядом) - и память закончилась. Это несерьезно.
>> Наверное, поэтому там и не сделано ничего, чтобы это можно было просматривать, чтобы это не кешировалось.
Ну конечно. А в python-shell тогда почему сделано нормально? :-)
Офлайн
Кстати, данный вопрос как оказалось волнует не меня одного: https://stackoverflow.com/questions/20814887/completely-disable-ipython-output-caching.
Офлайн
xzvfНакапливается, отжирает
Есть вывод других более мелких данных, вывод которых накапливается и отжирает память.
In [1]: list(range(5))
Out[1]: [0, 1, 2, 3, 4]
In [2]: list(range(5))
Out[2]: [0, 1, 2, 3, 4]
In [3]: list(range(5))
Out[3]: [0, 1, 2, 3, 4]
In [4]: list(range(5))
Out[4]: [0, 1, 2, 3, 4]
In [5]: list(range(5))
Out[5]: [0, 1, 2, 3, 4]
In [6]: list(range(5))
Out[6]: [0, 1, 2, 3, 4]
In [7]: list(range(5))
Out[7]: [0, 1, 2, 3, 4]
In [8]: list(range(5))
Out[8]: [0, 1, 2, 3, 4]
In [9]: list(range(5))
Out[9]: [0, 1, 2, 3, 4]
In [10]: list(range(5))
Out[10]: [0, 1, 2, 3, 4]
In [11]: list(range(5))
Out[11]: [0, 1, 2, 3, 4]
In [12]: list(range(5))
Out[12]: [0, 1, 2, 3, 4]
In [13]: list(range(5))
Out[13]: [0, 1, 2, 3, 4]
In [14]: list(range(5))
Out[14]: [0, 1, 2, 3, 4]
In [15]: list(range(5))
Out[15]: [0, 1, 2, 3, 4]
In [16]: _oh
Out[16]:
{1: [0, 1, 2, 3, 4],
2: [0, 1, 2, 3, 4],
3: [0, 1, 2, 3, 4],
4: [0, 1, 2, 3, 4],
5: [0, 1, 2, 3, 4],
6: [0, 1, 2, 3, 4],
7: [0, 1, 2, 3, 4],
8: [0, 1, 2, 3, 4],
9: [0, 1, 2, 3, 4],
10: [0, 1, 2, 3, 4],
11: [0, 1, 2, 3, 4],
12: [0, 1, 2, 3, 4],
13: [0, 1, 2, 3, 4],
14: [0, 1, 2, 3, 4],
15: [0, 1, 2, 3, 4]}
In [17]: id(_oh[1])
Out[17]: 140185325829128
In [18]: id(_oh[2])
Out[18]: 140185325829960
In [19]: id(_oh[15])
Out[19]: 140185323832840
In [20]:
xzvfНу там вообще по-тупому сделано. Кеш, удерживающий каждый выведенный список, даже непонятно, зачем нужен. Но с другой стороны там можно редактировать прошлый ввод прямо с переводами строк (чего нет в стандартном репле), что ускоряет работу при демонстрациях и разных проверках кода.
Я хочу просмотреть первые 10, или с 10000982 элемента по 10001182. А память будет резервироваться как для изначального набора данных.
Отредактировано py.user.next (Июнь 3, 2018 08:15:43)
Офлайн
Ну, проблему я собственно решил, процентов на 90%. На этом, наверное, и остановлюсь. В любом случае, спасибо за помощь :-)
Офлайн