[Python-Dev] extremely slow exit for program having huge (45G) dict (python 2.5.2)
Alexandre Vassalotti
alexandre at peadrop.com
Tue Dec 23 04:26:29 CET 2008
On Mon, Dec 22, 2008 at 7:34 PM, Antoine Pitrou <solipsis at pitrou.net> wrote:
>
>> Now, we should find a way to benchmark this without having to steal Mike's
>> machine and wait 30 minutes every time.
>
> So, I seem to reproduce it. The following script takes about 15 seconds to
> run and allocates a 2 GB dict which it deletes at the end (gc disabled of
> course).
> With 2.4, deleting the dict takes ~1.2 seconds while with 2.5 and higher
> (including 3.0), deleting the dict takes ~3.5 seconds. Nothing spectacular
> but the difference is clear.
>
I modified your script to delete the dictionary without actually
deallocating the items in it. You can speed up a dictionary
deallocation significantly if you keep a reference to its items and
delete the dictionary before deleting its items. In Python 2.4, the
same behavior exists, but is not as strongly marked as in Python 2.6
with pymalloc enabled.
I can understand that deallocating the items in the order (or
actually, the reverse order) they were allocated is faster, than doing
so in a rather haphazard manner (i.e., like dict). However, I am not
sure why pymalloc accentuate this behavior.
-- Alexandre
Python 2.6 with pymalloc, without pydebug
alex at helios:~$ python2.6 dict_dealloc_test.py
creating 397476 items...
-> 6.613 s.
building dict...
-> 0.230 s.
deleting items...
-> 0.059 s.
deleting dict...
-> 2.299 s.
total deallocation time: 2.358 seconds.
alex at helios:~$ python2.6 dict_dealloc_test.py
creating 397476 items...
-> 6.530 s.
building dict...
-> 0.228 s.
deleting dict...
-> 0.089 s.
deleting items...
-> 0.971 s.
total deallocation time: 1.060 seconds.
Python 2.6 without pymalloc, without pydebug
alex at helios:release26-maint$ ./python /home/alex/dict_dealloc_test.py
creating 397476 items...
-> 5.921 s.
building dict...
-> 0.244 s.
deleting items...
-> 0.073 s.
deleting dict...
-> 1.502 s.
total deallocation time: 1.586 seconds.
alex at helios:release26-maint$ ./python /home/alex/dict_dealloc_test.py
creating 397476 items...
-> 6.122 s.
building dict...
-> 0.237 s.
deleting dict...
-> 0.092 s.
deleting items...
-> 1.238 s.
total deallocation time: 1.330 seconds.
alex at helios:~$ python2.4 dict_dealloc_test.py
creating 397476 items...
-> 6.164 s.
building dict...
-> 0.218 s.
deleting items...
-> 0.057 s.
deleting dict...
-> 1.185 s.
total deallocation time: 1.243 seconds.
alex at helios:~$ python2.4 dict_dealloc_test.py
creating 397476 items...
-> 6.202 s.
building dict...
-> 0.218 s.
deleting dict...
-> 0.090 s.
deleting items...
-> 0.852 s.
total deallocation time: 0.943 seconds.
######
import random
import time
import gc
# Adjust this parameter according to your system RAM!
target_size = int(2.0 * 1024**3) # 2.0 GB
pool_size = 4 * 1024
# This is a ballpark estimate: 60 bytes overhead for each
# { dict entry struct + float object + tuple object header },
# 1.3 overallocation factor for the dict.
target_length = int(target_size / (1.3 * (pool_size + 60)))
def make_items():
print ("creating %d items..." % target_length)
# 1. Initialize a set of pre-computed random keys.
keys = [random.random() for i in range(target_length)]
# 2. Build the values that will constitute the dict. Each value will, as
# far as possible, span a contiguous `pool_size` memory area.
# Over 256 bytes per alloc, PyObject_Malloc defers to the system malloc()
# We avoid that by allocating tuples of smaller longs.
int_size = 200
# 24 roughly accounts for the long object overhead (YMMV)
int_start = 1 << ((int_size - 24) * 8 - 7)
int_range = range(1, 1 + pool_size // int_size)
values = [None] * target_length
# Maximize allocation locality by pre-allocating the values
for n in range(target_length):
values[n] = tuple(int_start + j for j in int_range)
return list(zip(keys,values))
if __name__ == "__main__":
gc.disable()
t1 = time.time()
items = make_items()
t2 = time.time()
print " -> %.3f s." % (t2 - t1)
print "building dict..."
t1 = time.time()
testdict = dict(items)
t2 = time.time()
print " -> %.3f s." % (t2 - t1)
def delete_testdict():
global testdict
print "deleting dict..."
t1 = time.time()
del testdict
t2 = time.time()
print " -> %.3f s." % (t2 - t1)
def delete_items():
global items
print "deleting items..."
t1 = time.time()
del items
t2 = time.time()
print " -> %.3f s." % (t2 - t1)
t1 = time.time()
# Swap these, and look at the total time
delete_items()
delete_testdict()
t2 = time.time()
print "total deallocation time: %.3f seconds." % (t2 - t1)
More information about the Python-Dev
mailing list