Ask Your Question
2

Problem with precompute and append in parallel computing

asked 2021-07-14 10:13:53 +0100

updated 2021-07-14 14:21:31 +0100

Here is a parallel computation involving precompute and append:

sage: L=[]
sage: @cached_function
....: def function(i):
....:     L.append(i)
....:     print(L)
....:
sage: function.precompute(range(5),num_processes=2)
[0]
[1]
[2]
[3]
[4]

The problem is that the list L is still empty at the end, the action L.append(i) was not done globally, whereas the list L is global.

sage: L
[]

How to solve this problem?

edit retag flag offensive close merge delete

1 Answer

Sort by » oldest newest most voted
1

answered 2021-07-14 20:03:51 +0100

mwageringel gravatar image

Presumably, you want to make available the partially computed results to speed up computations in the body of the function. This is not possible using precompute, but we can use the @parallel decorator for that.

Behind the scenes, parallel computations in Sage spawn new subprocesses for each call of the function. The local state from the main process is copied to the subprocess upon creation, but only the return value of the function is transferred back to the main process. Therefore, the call

L.append(i)

in the subprocess will have no effect on L in the main process. Instead, the function must return i (possibly together with other values you want to compute).

With the @parallel decorator, we obtain an iterator over the results. Once this iterator returns an element, we can add it to L in the main process, so that subsequent evaluations of the function body see this larger L.

L = []
@parallel(ncpus=2)
def fun(i):
  set_random_seed()
  sleep(float(RDF.random_element(0,1)))  # for illustration
  print((L, i))
  #L.append(i)  # has no effect on L in main process
  return i

it = fun(list(range(10)))
for args, i in it:
    L.append(i)

I have added a delay to the function body in order to show that the output does not have the same order. The iterator also returns the input arguments, which can be used if the order is important.

Output:

([], 1)
([1], 2)
([], 0)
([1, 2], 3)
([1, 2, 0], 4)
([1, 2, 0, 3], 5)
([1, 2, 0, 3, 4, 5], 7)
([1, 2, 0, 3, 4], 6)
([1, 2, 0, 3, 4, 5, 7], 8)
([1, 2, 0, 3, 4, 5, 7, 6], 9)

Of course, if some subprocess is already running, it will not be able to see the new elements of L. For example, if you set ncpus=10, all the subprocesses start before the first one returns a result, so they will see an empty list and you get an output like this:

([], 8)
([], 4)
([], 2)
([], 6)
([], 9)
([], 0)
([], 1)
([], 5)
([], 7)
([], 3)
edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2021-07-14 10:13:53 +0100

Seen: 247 times

Last updated: Jul 14 '21