Suppose we have a recurrence relation
A[0] = a
A[i+1] = f(A[i],c)
where c is a parameter and f
is some function, say
def f(x, c):
return sin(x) + c
Assume that, for a given a
, we want to evaluate A[i] for i in range(0,n)
for c in Cs = [c[j] for j in range(0, m)]
, where n
and m
are fairly large.
Question in a few words: Should I use multithreading for each c or list comprehension for each i.
Let me explain. I am considering the following two approaches:
Approach 1:
Store the A[i]
in a 2D array with each row containing the values of the sequence for a fixed c[i]. But store the array in column-major order, such that the A[i] for a constant i are contiguous.
Then use list comprehension to compute them
for i in range(0,n):
A[:,i+1] = [f(A[i],c) for c in Cs]
Approach 2:
Store the sequences as before in a 2D array, but in row-major order this time.
Have a function that given a row fills it up with the values of the sequence for a given c.
def sequence(j):
A[j, 0] = a
for i in range(0, n - 1):
A[j, i+1] = f(A[j, i], Cs[j])
And then call sequence
for different j
s in different threads processes using, say multiprocessing.Pool
.
Which of the two approaches should I prefer?
Experiment:
I tried the following test
import numpy
from multiprocessing.dummy import Pool
from multiprocessing import cpu_count
import time
def func(x):
N = 400
A = numpy.array([[i*j for i in range(0,N)] for j in range(0, N)])
h = numpy.array([x for i in range(0, N)])
y = numpy.dot(A, h.transpose())
return y[-1]
start_time = time.time()
def multiproc():
print('Multiple processes')
print(cpu_count())
mypool = Pool(cpu_count())
print(mypool.map(func, [i for i in range(0,100)]))
def multiproc2():
print('Multiple processes 2')
pool = Pool(cpu_count())
res = numpy.empty(100)
for i in range(0,100):
res[i] = pool.apply_async(func, (i,)).get()
pool.close()
pool.join()
print(res)
def singleproc():
for i in range(0,100):
print(func(i))
print('Single process')
funcs = [multiproc, singleproc, multiproc2]
funcs[1]()
print("%.6f seconds" % (time.time() - start_time))
Changing the call funcs[1]()
for funcs[0]()
or funcs[2]()
we get pretty much the same time in every case.