efficient idiomatic queue?

Delaney, Timothy tdelaney at avaya.com
Wed Jan 16 18:19:47 EST 2002


> >class DictFifo:
> >    def __init__(self):
> >        self.data = {}
> >        self.nextin = 0
> >        self.nextout = 0
> >    def append(self, value):
> >        self.data[self.nextin] = value
> >        self.nextin += 1
> >    def pop(self):
> >        value = self.data[self.nextout]
> >        del self.data[self.nextout]
> >        self.nextout += 1
           return value

:)

>     def __init__(self):
>         self.data = {}
>         self.nextin = 0L
>         self.nextout = 0L
> 
> to make sure that you can use it for more than two billion 
> entries.  ;-)
> 
> I haven't tested this, but I think that if you expect queues to be
> large, this might actually be a fast, clear implementation.

I just tested it with the following code. Everything is wrapped in classes
to maintain similar overhead. Note that for DictIntFifo, OverflowError is
caught to allow conversion to long in any version of Python. I've also
included a normal list and array for comparison.

import time
import string
import array

class DictIntFifo:

	def __init__(self):
		self.data = {}
		self.nextin = 0
		self.nextout = 0

	def append(self, value):

		self.data[self.nextin] = value

		try:
			self.nextin += 1
		except OverflowError:
			self.nextin += long(1)

	def pop(self):
		value = self.data[self.nextout]
		del self.data[self.nextout]

		try:
			self.nextout += 1
		except OverflowError:
			self.nextout += long(1)

		return value

class DictLongFifo:

	def __init__(self):
		self.data = {}
		self.nextin = long(0)
		self.nextout = long(0)

	def append(self, value):

		self.data[self.nextin] = value
		self.nextin += 1

	def pop(self):
		value = self.data[self.nextout]
		del self.data[self.nextout]
		self.nextout += 1
		return value

class ListPrependFifo:
	""""""
	
	def __init__(self):
		""""""
		self.data = []

	def append(self, value):
		self.data.insert(0, value)

	def pop(self):
		return self.data.pop()

class ListAppendFifo:
	""""""
	
	def __init__(self):
		""""""
		self.data = []

	def append(self, value):
		self.data.append(value)

	def pop(self):
		return self.data.pop(0)

def test (fifo, iterations):
	""""""

	start = time.clock()

	for i in xrange(iterations):
		fifo.append(i)
		j = fifo.pop()

	end = time.clock()

	try:
		name = fifo.__class__.__name__
	except:
		name = type(fifo)

	print "%-8s %-20s %-06f" % (iterations, name, end - start,)

for i in xrange(4):
	
	print
	test([], 10**(i + 3))
	test(array.array('i'), 10**(i + 3))
	test(DictIntFifo(), 10**(i + 3))
	test(DictLongFifo(), 10**(i + 3))
	test(ListPrependFifo(), 10**(i + 3))
	test(ListAppendFifo(), 10**(i + 3))

1000     <type 'list'>        0.003968
1000     <type 'array'>       0.004607
1000     DictIntFifo          0.012154
1000     DictLongFifo         0.015508
1000     ListPrependFifo      0.010499
1000     ListAppendFifo       0.010357

10000    <type 'list'>        0.039399
10000    <type 'array'>       0.046923
10000    DictIntFifo          0.120931
10000    DictLongFifo         0.156747
10000    ListPrependFifo      0.105777
10000    ListAppendFifo       0.105253

100000   <type 'list'>        0.394604
100000   <type 'array'>       0.459258
100000   DictIntFifo          1.214058
100000   DictLongFifo         1.568331
100000   ListPrependFifo      1.060692
100000   ListAppendFifo       1.046310

1000000  <type 'list'>        4.071518
1000000  <type 'array'>       4.640713
1000000  DictIntFifo          12.192316
1000000  DictLongFifo         15.778213
1000000  ListPrependFifo      10.700729
1000000  ListAppendFifo       10.636400

So, it looks like there is a definite speed penalty with the dict versions,
although the majority of that is probably namespace lookup time (more
operations). A C version would probably do quite well.

Nonetheless, it is a good example of separating interface from
implementation.

Tim Delaney




More information about the Python-list mailing list