MATLAB parfor和C ++类mex包装器(需要复制构造函数吗?) [英] MATLAB parfor and C++ class mex wrappers (copy constructor required?)

查看:84
本文介绍了MATLAB parfor和C ++类mex包装器(需要复制构造函数吗?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用此处概述的方法,将C ++类包装在matlab mex包装器中.基本上,我有一个初始化的mex文件,该文件返回一个C ++对象句柄:

I'm trying to wrap a C++ class in a matlab mex wrapper using the approach outlined here. Basically, I have an initialization mex file which returns a C++ object handle:

handle = myclass_init()

然后我可以将其传递给另一个mex文件(例如myclass_amethod),该文件使用该句柄来调用类方法,然后最终传递给myclass_delete以释放C ++对象:

I can then pass this to another mex file (e.g. myclass_amethod) which use the handle to call class methods, then ultimately to myclass_delete to free the C++ object:

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

为了便于使用,我将其包装在MATLAB类中

I've wrapped this up in a MATLAB class for ease of use:

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 ++对象实例是 not 复制的,导致指针无效.

I get a segfault, as a copy of the class is made in the parfor loop (in each worker) but as each worker is a separate process the C++ object instance is not copied, leading to an invalid pointer.

我最初尝试检测每个类方法何时在parfor循环中运行,在那些情况下也重新分配C ++对象.但是,由于尚无法检查是否已为当前工作程序分配了一个对象,因此会导致多个重新分配,然后只有一个删除操作(当工作程序退出时)会导致内存泄漏(请参阅附录底部的附录).询问详情).

I tried initially to detect when each class method was running in a parfor loop, and in those cases reallocate the C++ object too. However, as there is no way to check whether an object has been allocated for the current worker yet or not, this results in multiple reallocations and then just one delete (when the worker exits) resulting in a memory leak (see appendix at bottom of question for details).

在C ++中,处理此问题的方法是复制构造函数(这样,在复制包装MATLAB类时,C ++对象仅被重新分配一次).快速搜索显示 matlab.mixin.Copyable 类类型似乎提供了所需的功能(即MATLAB句柄类的深层副本).因此,我尝试了以下方法:

In C++, the way to handle this would be copy constructors (so that the C++ object is only reallocated once when the wrapper MATLAB class is copied). A quick search brings up the matlab.mixin.Copyable class type which seems to provide the required functionality (i.e. deep copies of MATLAB handle classes). Therefore, I've tried the following:

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

如上所述测试该类,即:

testing this class as above, i.e.:

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 ++对象句柄的行为?

In other words, it seems that the copy constructor is not called when spawning workers for the parfor. Does anyone have any pointers as to what I am doing wrong, or whether there is some way to achieve the desired behaviour of reinitializing the C++ object handle when the MATLAB wrapper class is copied?

仅供参考,这是我在工作进程中时使用的另一种替代方法:

Just for reference, here is the alternative approach I'm using reallocating when in a worker:

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

从上面可以看出,当在worker中运行时,确实确实发生了重新分配.但是,无论重新分配C ++类多少次,每个工作线程都只会调用 destructor 一次,从而导致内存泄漏.

As can be seen above, a reallocation does indeed now occur when running in a worker. However, the destructor is only called once for every worker regardless of how many time the C++ class was reallocated, resulting in a memory leak.

以下作品:

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

推荐答案

将对象实例传递到PARFOR循环的正文中时,其行为与将其保存到文件中相同,并且然后再次加载.最简单的解决方案可能是将您的cpp_handle_标记为Transient.然后,您需要实现SAVEOBJLOADOBJ以安全地传输数据.请参阅此页面,以了解有关自定义类的保存/加载行为的更多信息.

When you pass an object instance into the body of a PARFOR loop, the behaviour is the same as if you'd saved it to a file, and then loaded it again. The simplest solution is probably to mark your cpp_handle_ as Transient. Then, you need to implement SAVEOBJ and LOADOBJ to safely transport your data. See this page for more about customising the save/load behaviour of your class.

这篇关于MATLAB parfor和C ++类mex包装器(需要复制构造函数吗?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆