Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

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)