MATLAB PARFOR和C ++类MEX包装(拷贝构造函数需要?)(MATLAB parfor

2019-08-04 00:10发布

我想换一个C ++类在MATLAB MEX包装用概括的方法在这里 。 基本上,我有,它返回一个C ++对象句柄初始化MEX文件:

handle = myclass_init()

然后我可以通过这个另一MEX文件(例如myclass_amethod ),其使用该句柄调用类的方法,然后最终以myclass_delete释放C ++对象:

retval = myclass_amethod(handle, parameter)
myclass_delete(handle)

我在易于使用的一类MATLAB这个包裹起来:

classdef myclass < handle
    properties(SetAccess=protected)
        cpp_handle_
    end
    methods
        % class constructor
        function obj = myclass()
            obj.cpp_handle_ = myclass_init();
        end
        % class destructor
        function delete(obj)
            myclass_delete(obj.cpp_handle_);
        end
        % class method
        function amethod(parameter)
            myclass_amethod(obj.cpp_handle_, parameter);
        end
    end
end

问题:这并不在并行代码的工作

这在非并行代码工作正常。 但是,只要我把它从内parfor

cls = myclass();
parfor i = 1:10
    cls.amethod(i)
end

我得到一个段错误,作为类的副本在由parfor循环(每个工人)但作为每个工人是一个独立的进程C ++对象实例被复制,导致了无效的指针。

我试图最初当每个类的方法是在一个PARFOR循环中运行,以检测,并且在这些情况下重新分配C ++对象太。 但是,因为没有办法检查对象是否已经被分配给当前的工作却还是没有,这导致了多个重新分配,然后只有一个删除(当工人退出)导致内存泄漏(见附录在底部对于细节题)。

尝试的解决方案:拷贝构造函数和使用matlab.mixin.Copyable

在C ++中,的方式来处理,这将是复制构造(以使得当包装MATLAB类被复制的C ++对象只重新分配一次)。 快速搜索带来了matlab.mixin.Copyable这似乎提供所需的功能(MATLAB手柄类,即深层副本)类类型。 因此,我已经试过如下:

classdef myclass < matlab.mixin.Copyable
    properties(SetAccess=protected)
        cpp_handle_
    end
    methods
        function obj = myclass(val)
            if nargin < 1
                % regular constructor
                obj.cpp_handle_ = rand(1);
                disp(['Initialized myclass with handle: ' num2str(obj.cpp_handle_)]);
            else
                % copy constructor
                obj.cpp_handle_ = rand(1);
                disp(['Copy initialized myclass with handle: ' num2str(obj.cpp_handle_)]);
            end
        end
        % destructor
        function delete(obj)
            disp(['Deleted myclass with handle: ' num2str(obj.cpp_handle_)]);
        end
        % class method
        function amethod(obj)
            disp(['Class method called with handle: ' num2str(obj.cpp_handle_)]);
        end
    end
end

测试此类如上述,即:

cls = myclass();
parfor i = 1:10
    cls.amethod(i)
end

结果在输出:

Initialized myclass with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Class method called with handle: 0.65548
Deleted myclass with handle: 0.65548
Deleted myclass with handle: 0.65548
Deleted myclass with handle: 0.65548

换句话说, 似乎对PARFOR产卵工人当拷贝构造函数没有被调用 。 有没有人有任何指针,以什么我做错了,或者是否有某种方式来实现时,MATLAB包装类被复制重新初始化C ++对象句柄的期望行为?


替代做法:对工人运行时检测

仅供参考,这里是我使用的工人重新分配时的另一种方法:

classdef myclass < handle
    properties(SetAccess=protected)
        cpp_handle_
    end
    methods
        function obj = myclass(val)
            obj.cpp_handle_ = rand(1);
            disp(['Initialized myclass with handle: ' num2str(obj.cpp_handle_)]);
        end
        % destructor
        function delete(obj)
            disp(['Deleted myclass with handle: ' num2str(obj.cpp_handle_)]);
        end
        % class method
        function amethod(obj)
            obj.check_handle()
            disp(['Class method called with handle: ' num2str(obj.cpp_handle_)]);
        end
        % reinitialize cpp handle if in a worker:
        function check_handle(obj)
            try
                t = getCurrentTask();
                % if 'getCurrentTask()' returns a task object, it means we
                % are running in a worker, so reinitialize the class
                if ~isempty(t)
                    obj.cpp_handle_ = rand(1);
                    disp(['cpp_handle_ reinitialized to ' num2str(obj.cpp_handle_)]);
                end
            catch e
                % in case of getCurrentTask() being undefined, this
                % probably simply means the PCT is not installed, so
                % continue without throwing an error
                if ~strcmp(e.identifier, 'MATLAB:UndefinedFunction')
                    rethrow(e);
                end
            end
        end
    end
end

和输出:

Initialized myclass with handle: 0.034446
cpp_handle_ reinitialized to 0.55625
Class method called with handle: 0.55625
cpp_handle_ reinitialized to 0.0048098
Class method called with handle: 0.0048098
cpp_handle_ reinitialized to 0.58711
Class method called with handle: 0.58711
cpp_handle_ reinitialized to 0.81725
Class method called with handle: 0.81725
cpp_handle_ reinitialized to 0.43991
cpp_handle_ reinitialized to 0.79006
cpp_handle_ reinitialized to 0.0015995
Class method called with handle: 0.0015995
cpp_handle_ reinitialized to 0.0042699
cpp_handle_ reinitialized to 0.51094
Class method called with handle: 0.51094
Class method called with handle: 0.0042699
Class method called with handle: 0.43991
cpp_handle_ reinitialized to 0.45428
Deleted myclass with handle: 0.0042699
Class method called with handle: 0.79006
Deleted myclass with handle: 0.43991
Deleted myclass with handle: 0.79006
Class method called with handle: 0.45428

从上面可以看出,在工人中运行时确实现在发生再分配。 然而, 析构函数只为每个工人调用一次,无论在C ++类是有多少时间重新分配,导致内存泄漏。


解决方案:使用loadobj

以下工作:

classdef myclass < handle
    properties(SetAccess=protected, Transient=true)
        cpp_handle_
    end
    methods(Static=true)
        function obj = loadobj(a)
            a.cpp_handle_ = rand(1);
            disp(['Load initialized encoder with handle: ' num2str(a.cpp_handle_)]);
            obj = a;
        end
    end
    methods
        function obj = myclass(val)
            obj.cpp_handle_ = rand(1);
            disp(['Initialized myclass with handle: ' num2str(obj.cpp_handle_)]);
        end
        % destructor
        function delete(obj)
            disp(['Deleted myclass with handle: ' num2str(obj.cpp_handle_)]);
        end
        % class method
        function amethod(obj)
            disp(['Class method called with handle: ' num2str(obj.cpp_handle_)]);
        end
    end
end

Answer 1:

当你传递一个对象实例进入体内PARFOR循环,行为是一样的,如果你想它保存到一个文件,然后再加载它。 最简单的解决方案可能是纪念你cpp_handle_Transient 。 然后,你需要实现SAVEOBJLOADOBJ安全地传输数据。 请参见本页面获取更多有关自定义类的SAVE / LOAD行为。



文章来源: MATLAB parfor and C++ class mex wrappers (copy constructor required?)