[Python-Dev] FIFO data structure?
Agthorr
agthorr@barsoom.org
Mon, 21 Apr 2003 22:42:18 -0700
On Sun, Apr 20, 2003 at 09:31:04PM -0400, Tim Peters wrote:
> I'm opposed to this. The purpose of Queue is to mediate communication among
> threads, and a Queue.Queue rarely gets large because of its intended
> applications. As other recent timing posts have shown, you simply can't
> beat the list.append + list.pop(0) approach until a queue gets quite large
> (relative to the intended purpose of a Queue.Queue).
Out of curiosity, I ran some tests, comparing:
list.append + list.pop(0)
Queue.Queue
my modified Queue.Queue
The test adds n integers to the Queue, then removes them. I use the
timeit module to perform the measurements, and do not count the
loading of the module or creating of the list/queue object (since
presumably the user will do this extremely infrequently).
What I found was that for small n, list.append/pop is much faster than
either Queue implementation. I assume this means that the bulk of the
time is spent dealing with thread synchronization issues and with the
overhead of using a class. It takes around one twentieth the time to
complete the list.append/pop compared to either Queue implementation.
For small n, the two Queue implementations were at least in the same
ballpark. Mine was roughly 25% slower for n < 10, and around 10%
slower for 10 < n < 100. After that, the difference gradually
declined until the circular array took the lead somewhere in the
vicinity of n=2000. The performance difference didn't become large
until n=10000 where the O(n^2) growth finally began to kill the
list.append/pop.
Disappointed with these results, I spent some time tweaking my
modified Queue.Queue to improve the performance. I create local
variables in a few places, perform a bitwise-AND instead of a modulus,
and initialize the circular buffer with 8 elements instead of just 1.
I also now grow the circular buffer more efficiently.
This made a huge difference. My implementation now outperforms the
current Queue.Queue for n > 1! It does around 1% to 4% better up
until around n=500, then the advantage starts to slowly ramp up.
My updated Queue implement is here:
http://www.cs.uoregon.edu/~agthorr/QueueNew.py
and my test program is here:
http://www.cs.uoregon.edu/~agthorr/test.py
> If you have an unusual application for a Queue.Queue where it's actually
> faster to do a circular-buffer gimmick (and don't believe that you do before
> you time it),
My application is a little program that sends simulation jobs to a
small server farm. I have one thread per server that grabs jobs off
the Queue and starts the remote simulation. I have a fair number of
simulation parameters, and this translates into thousands of jobs
getting added to the Queue. So, yes, for my particular application,
the O(n^2) behavior really is a genuine problem ;)
If circular-array Queue.Queue was significantly slower for low n, I'd
agree with you that the current implementation should not be changed.
It doesn't appear to be a problem, though.
However, speaking of subclassing Queue: is it likely there are many
user applications that subclass it in a way that would break? (i.e.,
they override some, but not all, of the functions intended for
overriding).
-- Agthorr