Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Speeding up Python's exit

Reply
Thread Tools

Speeding up Python's exit

 
 
Steven D'Aprano
Guest
Posts: n/a
 
      02-28-2013
I just quit an interactive session using Python 2.7 on Linux. It took in
excess of twelve minutes to exit, with the load average going well past 9
for much of that time.

I think the reason it took so long was that Python was garbage-collecting
a giant dict with 10 million entries, each one containing a list of the
form [1, [2, 3], 4]. But still, that's terribly slow -- ironically, it
took longer to dispose of the dict (12+ minutes) than it took to create
it in the first place (approx 3 minutes, with a maximum load of 4).

Can anyone explain why this was so painfully slow, and what (if anything)
I can do to avoid it in the future?

I know there is a function os._exit which effectively kills the Python
interpreter dead immediately, without doing any cleanup. What are the
consequences of doing this? I assume that the memory used by the Python
process will be reclaimed by the operating system, but other resources
such as opened files may not be.


--
Steven
 
Reply With Quote
 
 
 
 
Neil Cerutti
Guest
Posts: n/a
 
      02-28-2013
On 2013-02-28, Steven D'Aprano <steve+> wrote:
> Can anyone explain why this was so painfully slow, and what (if
> anything) I can do to avoid it in the future?


I think your explanation makes sense. Maybe the nested nature of
the strings was causing it to churn looking for circular
references?

Disabling gc before exiting might do the trick, assuming you're
assiduously managing other resources with context managers.

gc.disable()
exit()


--
Neil Cerutti
 
Reply With Quote
 
 
 
 
Chris Angelico
Guest
Posts: n/a
 
      02-28-2013
On Fri, Mar 1, 2013 at 3:49 AM, Steven D'Aprano
<steve+> wrote:
> I think the reason it took so long was that Python was garbage-collecting
> a giant dict with 10 million entries, each one containing a list of the
> form [1, [2, 3], 4]. But still, that's terribly slow -- ironically, it
> took longer to dispose of the dict (12+ minutes) than it took to create
> it in the first place (approx 3 minutes, with a maximum load of 4).


Leaving the question of just *why* you have so much in your dict.....
but anyway.

Is it any different if you create a deliberate reference loop and then
stuff it into some module somewhere? That would force it to be kept
until interpreter shutdown, and then a cyclic garbage collection after
that, which quite probably would be never run. A stupid trick,
perhaps, but it might work; I tested it with a dummy class with a
__del__ method and it wasn't called. Putting it into some other module
may not be necessary, but I don't know what happens with the
interactive interpreter and what gets freed up when.

ChrisA
 
Reply With Quote
 
Devin Jeanpierre
Guest
Posts: n/a
 
      02-28-2013
On Thu, Feb 28, 2013 at 12:06 PM, Chris Angelico <> wrote:
> Is it any different if you create a deliberate reference loop and then
> stuff it into some module somewhere? That would force it to be kept
> until interpreter shutdown, and then a cyclic garbage collection after
> that, which quite probably would be never run. A stupid trick,
> perhaps, but it might work; I tested it with a dummy class with a
> __del__ method and it wasn't called. Putting it into some other module
> may not be necessary, but I don't know what happens with the
> interactive interpreter and what gets freed up when.


__del__ is never called for cyclic references.

-- Devin
 
Reply With Quote
 
Devin Jeanpierre
Guest
Posts: n/a
 
      02-28-2013
On Thu, Feb 28, 2013 at 12:31 PM, Devin Jeanpierre
<> wrote:
> On Thu, Feb 28, 2013 at 12:06 PM, Chris Angelico <> wrote:
>> Is it any different if you create a deliberate reference loop and then
>> stuff it into some module somewhere? That would force it to be kept
>> until interpreter shutdown, and then a cyclic garbage collection after
>> that, which quite probably would be never run. A stupid trick,
>> perhaps, but it might work; I tested it with a dummy class with a
>> __del__ method and it wasn't called. Putting it into some other module
>> may not be necessary, but I don't know what happens with the
>> interactive interpreter and what gets freed up when.

>
> __del__ is never called for cyclic references.


Sorry, I posted too early. Not only is __del__ never called, but
__del__ is the reason the cycles aren't collected. I don't know if
your trick will work without __del__.

-- Devin
 
Reply With Quote
 
Chris Angelico
Guest
Posts: n/a
 
      02-28-2013
On Fri, Mar 1, 2013 at 4:31 AM, Devin Jeanpierre <> wrote:
> On Thu, Feb 28, 2013 at 12:06 PM, Chris Angelico <> wrote:
>> Is it any different if you create a deliberate reference loop and then
>> stuff it into some module somewhere? That would force it to be kept
>> until interpreter shutdown, and then a cyclic garbage collection after
>> that, which quite probably would be never run. A stupid trick,
>> perhaps, but it might work; I tested it with a dummy class with a
>> __del__ method and it wasn't called. Putting it into some other module
>> may not be necessary, but I don't know what happens with the
>> interactive interpreter and what gets freed up when.

>
> __del__ is never called for cyclic references.


D'oh. Test is flawed, then. But is the theory plausible? That the
cycle detector won't be called on exit after other modules get freed?

ChrisA
 
Reply With Quote
 
Grant Edwards
Guest
Posts: n/a
 
      03-01-2013
On 2013-02-28, Steven D'Aprano <steve+> wrote:

> I know there is a function os._exit which effectively kills the
> Python interpreter dead immediately, without doing any cleanup. What
> are the consequences of doing this?


You loose any data you haven't saved to disk.

> I assume that the memory used by the Python process will be reclaimed
> by the operating system, but other resources such as opened files may
> not be.


All open files (including sockets, pipes, serial ports, etc) will be
flushed (from an OS standpoint) and closed. If you've closed all the
files you've written to, there should be no danger in just pulling the
plug.

--
Grant

 
Reply With Quote
 
Antoine Pitrou
Guest
Posts: n/a
 
      03-01-2013
Steven D'Aprano <steve+comp.lang.python <at> pearwood.info> writes:
>
> I just quit an interactive session using Python 2.7 on Linux. It took in
> excess of twelve minutes to exit, with the load average going well past 9
> for much of that time.
>
> I think the reason it took so long was that Python was garbage-collecting
> a giant dict with 10 million entries, each one containing a list of the
> form [1, [2, 3], 4]. But still, that's terribly slow -- ironically, it
> took longer to dispose of the dict (12+ minutes) than it took to create
> it in the first place (approx 3 minutes, with a maximum load of 4).
>
> Can anyone explain why this was so painfully slow, and what (if anything)
> I can do to avoid it in the future?


You are basically asking people to guess where your performance problem
comes from, without even providing a snippet so that people can reproduce

> I know there is a function os._exit which effectively kills the Python
> interpreter dead immediately, without doing any cleanup. What are the
> consequences of doing this? I assume that the memory used by the Python
> process will be reclaimed by the operating system, but other resources
> such as opened files may not be.


The OS always disposes of per-process resources when the process terminates
(except if the OS is buggy ). However, file buffers will not be flushed,
atexit handlers and other destructors will not be called, database
transactions will be abandoned (rolled back), etc.

Regards

Antoine.


 
Reply With Quote
 
Antoine Pitrou
Guest
Posts: n/a
 
      03-01-2013
Grant Edwards <invalid <at> invalid.invalid> writes:
>
> > I assume that the memory used by the Python process will be reclaimed
> > by the operating system, but other resources such as opened files may
> > not be.

>
> All open files (including sockets, pipes, serial ports, etc) will be
> flushed (from an OS standpoint) and closed.


According to POSIX, no, open files will not be flushed:

“The _Exit() and _exit() functions shall not call functions registered with
atexit() nor any registered signal handlers. Open streams shall not be flushed.
Whether open streams are closed (without flushing) is implementation-defined.”

http://pubs.opengroup.org/onlinepubs...ons/_exit.html

(under the hood, os._exit() calls C _exit())

Regards

Antoine.


 
Reply With Quote
 
Dave Angel
Guest
Posts: n/a
 
      03-01-2013
On 03/01/2013 02:10 PM, Antoine Pitrou wrote:
> Grant Edwards <invalid <at> invalid.invalid> writes:
>> <snip>
>> All open files (including sockets, pipes, serial ports, etc) will be
>> flushed (from an OS standpoint) and closed.

>
> According to POSIX, no, open files will not be flushed:
>
> “The _Exit() and _exit() functions shall not call functions registered with
> atexit() nor any registered signal handlers. Open streams shall not be flushed.
> Whether open streams are closed (without flushing) is implementation-defined.”
>


Note he didn't say the python buffers would be flushed. It's the OS
buffers that are flushed.


--
DaveA
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Exit code of a batch (using exit /B) Joe Smith Java 4 11-08-2006 12:25 PM
Difference between exit(0) & exit (1) Vicky C Programming 6 08-08-2006 09:11 PM
Code to Exit Web App and Exit Internet Explorer =?Utf-8?B?U2FuZHk=?= ASP .Net 7 08-05-2005 01:55 AM
exit after process exit ajikoe@gmail.com Python 2 05-31-2005 08:11 PM
What's the difference of return 0; exit(0);exit(1) QQ C Programming 5 05-10-2005 10:11 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57