4
$\begingroup$

Bug introduced in 8 or earlier and persisting through 13.0.1


I came across behaviour of ParallelEvaluate that I do not understand. In a loop I define five functions labelled with 1,2,....

imax = 5;
Do[(myfunc[i] := $KernelID), {i, imax}]; (* On purpose not myfunc[i_] *)

Executing

ParallelEvaluate[{myfunc[1], $KernelID}]

returns

{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, {8, 8}, 
 {9, 9}, {10, 10}, {11, 11}, {12, 12}, {13, 13}, {14, 14}, {15, 15}, 
 {16,16}}

As expected, both parts of the expression {myfunc[1], $KernelID} were executed on the sixteen available subkernels. This works like this up to imax=17 on my machine (Windows Server 64 bit. Mathematica 13.01). But then...

Clear[myfunc];
imax = 18; (*or larger*)
Do[(myfunc[i] := $KernelID), {i, imax}]; (* On purpose not myfunc[i_].*)
ParallelEvaluate[{myfunc[1], $KernelID}]

returns

{{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8},
 {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}, {0, 16}}

myfunc[1] is always executed on kernel 0, i.e., on the main kernel attributed to the notebook. Only the second part of {myfunc[1], $KernelID} is excecuted on the subkernels. Is this expected?

Instead of integer I can also use strings as labels like:

f["a"]:=$KernelID; f["b"]:=$KernelID; ...

I get the same problem. I would like to define more than 17 labelled functions and execute anyone of them in parallel on all subkernels. Is the number of labelled functions and the number of kernels somehow connected?

Thank you for your help.

$\endgroup$
3
  • $\begingroup$ @AlexeyPopkov unfortunately, I can't recall whether I reported it. Might be worth to simply report it again anyway, since that should push it up on their priority list if they see that more people are running into this. $\endgroup$
    – Lukas Lang
    Commented May 10, 2022 at 13:40
  • $\begingroup$ @LukasLang Certainly. I'm surprised that such annoying and simple-to-fix bug persists at least from version 8. Probably too little reports. I'll report it with a link to your answer. $\endgroup$ Commented May 10, 2022 at 13:45
  • 1
    $\begingroup$ @LukasLang Reported as [CASE:4938187]. $\endgroup$ Commented May 10, 2022 at 14:15

1 Answer 1

2
$\begingroup$

I reproduce the issue on Windows 10 Pro x64 with Mathematica 13.0.1, 12.3.1 and 8.0.4. My laptop has only 4 physical cores, but the "special" number is also 18.

Looks like it is an evaluation leak in DistributeDefinitions:

Clear[myfunc];
imax = 18; i = 0;
Do[(myfunc[i] := Echo[{++i, $KernelID}]), {i, imax}];

DistributeDefinitions[myfunc];

screenshot

Echo is printed 36 times. Hence every definition is evaluated twice in the main kernel. It is a bug, please report it to the support.

As a workaround I can suggest creating definitions in the parallel kernels without using DistributeDefinitions:

ParallelEvaluate[imax = 18; Do[(myfunc[i] := $KernelID), {i, imax}];, DistributedContexts -> None];
ParallelEvaluate[{myfunc[1], $KernelID}, DistributedContexts -> None]
{{1, 1}, {2, 2}, {3, 3}, {4, 4}}

Another workaround is to copy DownValues from the main kernel to subkernels manually:

imax = 18; Do[(myfunc[i] := $KernelID), {i, imax}];
With[{downValues = DownValues[myfunc]}, 
  ParallelEvaluate[DownValues[myfunc] = downValues, DistributedContexts -> None]];
ParallelEvaluate[{myfunc[1], $KernelID}, DistributedContexts -> None]
{{1, 1}, {2, 2}, {3, 3}, {4, 4}}

If the situation is more complicated, and the Symbol to distribute contains different kinds of *Values, one can use undocumented Language`ExtendedDefinition and Language`ExtendedFullDefinition for copying them all at once.

Also related:

$\endgroup$
6
  • 1
    $\begingroup$ Thank you for looking into this! Yes, also in my opinion this is bug. Following your suggestion I reported it today to [email protected] . $\endgroup$
    – dnet
    Commented May 10, 2022 at 8:03
  • $\begingroup$ Thank you also for the workaround. However, for my real case this is not very elegant. I have a large notebook for analysing experimental data sets. Each data set gets a label like: "Experiment1",... And I define functions like setup["Experiment1"]:=(SetDirectory["experiment1location"];...) These functions are defined in different places of the notebook. I could manually wrap around ParallelEvaluate for all these definition, but then these functions are not defined for the main kernel. So, I would need to define them twice, I suppose. $\endgroup$
    – dnet
    Commented May 10, 2022 at 8:19
  • 1
    $\begingroup$ @dnet I've added another method which should fit your purposes better. $\endgroup$ Commented May 10, 2022 at 9:43
  • $\begingroup$ @dnet Thank you for reporting! $\endgroup$ Commented May 10, 2022 at 11:49
  • $\begingroup$ Great! I did not know about DownValues. Very helpful. Thanks! $\endgroup$
    – dnet
    Commented May 10, 2022 at 12:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.