Parfor не распространяет изменения для обработки класса - PullRequest
0 голосов
/ 18 февраля 2019

В R2018b рассмотрим следующий класс игрушек:

classdef MyObj < handle

    properties
        use_parallel = true
        test_value   = NaN
    end

    methods
        function myMethod(obj)

            % Call one of the nested functions below:
            if all([obj.use_parallel])
                parallel();                
                disp('Parallel (inside myMethod):')
                [obj.test_value]
            else
                sequential();
                disp('Sequential (inside myMethod):')
                [obj.test_value]
            end

            % Sequentially assign some values
            function sequential()                
                for ii = 1:numel(obj)
                    obj(ii).test_value = ii; end
            end

            % Assign some values in parallel
            function parallel()
                parfor ii = 1:numel(obj)
                    set_value(obj(ii),labindex());
                    obj_copy(ii) = obj(ii);
                end
                obj = obj_copy;
            end

        end
    end
end

% parfor requires subfunction (and not nested function):
function set_value(obj,index)
    obj.test_value = index;
end

Хотя этот вопрос очень напоминает этот и этот вопрос , их основная проблема была по сути неким вариантомИз ограничений, изложенных в документации :

Вы можете отправлять объекты дескриптора в качестве входных данных в тело цикла parfor.Однако любые изменения, сделанные для обработки объектов на рабочих во время итераций цикла, автоматически не передаются обратно клиенту.То есть изменения, сделанные внутри цикла, не отражаются автоматически после цикла

Однако, насколько я вижу, класс игрушек, приведенный выше, соответствует правилам нарезки parfor, а такжеподробности, касающиеся дескрипторов классов.Насколько я понимаю, он должен правильно скопировать измененное obj обратно в myMethod's рабочее пространство.

Однако, выполнив следующее:

clc

% Assign sequentially
M(3) = MyObj();
[M.use_parallel] = deal(false);
M.myMethod();
disp('Sequential (outside class):')
[M.test_value]

disp(' ')

% Assign in parallel 
N(3) = MyObj();
[N.use_parallel] = deal(true);
N.myMethod();
disp('Parallel (outside class):')
[N.test_value]

дает на мои parpool из 6 рабочих:

Sequential (inside myMethod):
ans =
     1     2     3   % <- OK
Sequential (outside class):
ans =
     1     2     3   % <- OK. Nothing unexpected

Parallel (inside myMethod):
ans =
     1     1     1   % <- OK, apparently, lab 1 did everything
Parallel (outside class):
ans =
   NaN   NaN   NaN   % <- hmmm...changes did not propagate

Это означает, что obj.test_value назначается правильно,и измененный obj действительно правильно скопирован в myMethod's рабочее пространство.Тем не менее, каким-то образом , этот модифицированный obj отличается от obj до модификации, потому что изменения не распространяются выше по стеку ...

Изменение parallel()функция к подфункции (вместо вложенной функции) и явная передача параметра obj, не влияет на этот результат.

Оооочень ... что здесь происходит?

1 Ответ

0 голосов
/ 18 февраля 2019

Я могу свести проблему к следующему:

classdef MyObj < matlab.mixin.Copyable

    properties
        test_value = NaN
    end

    methods
        function myMethod(obj)
            obj = obj.copy();
            obj.test_value = rand;
            disp('Inside method:')
            obj.test_value
        end
    end
end

Удаление вложенных функций, подфункций и parfor из уравнения.Выполнение кода (после явных изменений), приведенного выше, приводит к:

Inside method:
ans =
     4.8089e-01   % <- obj re-initialized OK
Outside class:
ans =
     NaN          % <- but this does NOT modify the handle class!

Это означает, что это «особенность» языка, о которой я не знал!Очевидно, что методы могут модифицировать существующих объектов, но не переопределять их полностью.Я попытаюсь покопаться в документации, чтобы узнать больше об этом, но это создает совершенно новую проблему: как скопировать изменения, сделанные с помощью parfor, обратно в объект?

TL; DR:

function myMethod(obj)
%   ↓ new object   ↓ old object
    obj       =    obj.copy();

Крис был прав;хотя новые и старые объекты имеют одинаковые имена, они разные вещи - они тень друг друга.Любые изменения, внесенные в новый obj, не изменят старый obj.

...