Reputation: 4388
I've been playing with the Birthday Problem, and came up with the code below. The problem is that, even for a small 1 million runs, it can take up quite some time. Since tick()
is the most expensive function, and is well isolated (I think), how would I multi-thread the while
loop section?
Coming from a c++ openmp background, I would probably be using #pragma omp parallel
, but I don't know how to do that in python, especially with the GIL
issue
Are there any other ways I can increase the performance?
ncalls tottime percall cumtime percall filename:lineno(function)
1000000 10.320 0.000 21.736 0.000 sim.py:29(tick)
24617823 6.931 0.000 9.209 0.000 sim.py:8(getday)
24617823 2.278 0.000 2.278 0.000 {method 'randint' of 'mtrand.RandomState' objects}
23617823 2.115 0.000 2.115 0.000 {method 'add' of 'set' objects}
1 0.983 0.983 22.967 22.967 sim.py:12(main)
999999 0.105 0.000 0.105 0.000 {method 'append' of 'list' objects}
import numpy as np
def getday():
return np.random.randint(1, 366)
def main():
# Multithread this section
popSize = [tick()]
while len(popSize) < 1000000:
popSize.append(tick())
print "Iterations %d" % len(popSize)
print "Average pop size %d" % (sum(popSize) / len(popSize))
print "Min pop size %d" % min(popSize)
print "Max pop size %d" % max(popSize)
def tick():
days = set()
day = getday()
while day not in days:
days.add(day)
day = getday()
return len(days)
if __name__ == '__main__':
main()
Upvotes: 0
Views: 114
Reputation: 2560
Try this code where the if
clause under main() selects original or mp versions...
import numpy as np
import multiprocessing as mp
def getday():
return np.random.randint(1, 366)
def main():
size = 1000000
if 0: # original version
popSize = [tick()]
while len(popSize) < size:
popSize.append(tick())
else: # multithreaded
pool = mp.Pool()
popSize = pool.map(tick, range(size))
print "Iterations %d" % len(popSize)
print "Average pop size %d" % (sum(popSize) / len(popSize))
print "Min pop size %d" % min(popSize)
print "Max pop size %d" % max(popSize)
def tick(dummy=None):
days = set()
day = getday()
while day not in days:
days.add(day)
day = getday()
return len(days)
if __name__ == '__main__':
main()
On my 14 core machine run-time goes from 21 seconds to 1.75 seconds which is fairly close to the theoretical max.
There's a few different parallel methods in multiprocessing that you can try. See https://docs.python.org/2/library/multiprocessing.html 16.6.2.9. Process Pools
Note that the optional chunksize parameter can greatly impact performance. With map
it doesn't seem to be required for a speedup but if you use imap
you need to set chunksize
to something like 100 to see the speedup.
Upvotes: 2