parfor中不能使用global或persistent变量咋办

前言

在MATLAB中使用parfor,提高彼此不相关的程序运行的效率(比如一些参数扫描),是简单方便的,但是parfor中也存在一些限制。最近遇到这样一种情形:一个描述线性时变系统的ode方程,要对它进行数值积分,其中包含较多的参数,还有几个300kHz采样率的输入信号。最开始没有考虑并行的事,我不想在ode45等函数每次调用ode方程时都传入如此大量的参数(一秒传入个10^510^6次,顶不住),于是使用了最简单的global传递输入信号,用persistent避免每次都初始化系统参数。而后出现的一些问题和新的需求使我开始思考(折腾)。

问题

我有时会用几个不同的参数来做些简单对比,但是由于函数已经运行过,persistent声明的静态变量已经被初始化,这导致每次都要clear一些变量,而我又是个粗心的人,这样的做法存在隐患,而且我确实遇到了很多和它相关的错误。既然提到参数扫描,那自然想到parfor来提高效率,这时矛盾又进一步发展,parfor是不支持global和persistent声明的变量的。

解决思路

前些天学习Python,并把这个ode方程改成了Python代码,当时就发现Python没有global、persistent声明。逛逛搜索引擎,有人提到可以定义类来实现类似静态变量的作用。于是在Python中为这个线性时变系统定义了类,并将ode方程作为类的方法。MATLAB也是可以面向对象的,那就在MATLAB中也做同样的事不就ok?(其实也可以尝试嵌套函数,应该也可以达成目的,但我就想面向对象)。

一个简单的例子(CSDN的编辑器好像没有MATLAB的语法高亮,随便选一个) 

% 整一个匀速直线运动的玩具车
classdef CAR
    properties
        % 车的动力(加速度)
        accelerate
        % 初始的速度
        init_velocity
        % 初始的位置
        int_position
    end
    methods
        function obj = CAR(accelerate, init_velocity, init_position)
            obj.accelerate = accelerate;
            obj.init_velocity = init_velocity;
            obj.init_position = init_position;
        end
        function dydt = sys_odes(obj, t, y)
            if y(2) > 5
                % 做法过于粗糙
                % 咱这玩具车速度最大只能到5m/s
                dydt = [5; 0];
            else
                % 全力前进
                dydt = [y(2); obj.accelerate];
            end
        end
    end
end

没了global和persistent,这下应该可以了,但是坑这就出现了。。。正常for循环可以这么写的(注意:直接用{}取元胞数组的元素,然后用dot notation似乎是取不到句柄的,原因我暂时不明白):

T_span = (0:0.2:30)';% 30秒定时赛
a0 = 1:7;
a1 = 2:8;
for ind = 1:7
    scan = my_cars{ind};% 我有很多不同马力的车
    [~,y] = ode45(@scan.sys_odes, T_span, [scan.init_position; scan.init_velocity]);
    result{ind} = y;
end

但是如果只是把for改成parfor,那么MATLAB提示:透明度违例错误。查看文档,寥寥数语,几个简单例子,愣是没看明白。。但我觉得其中最重要的一句话是

In general, the requirement for transparency restricts all dynamic access to variables, because the entire variable might not be present in any given worker. In a transparent workspace, you cannot create, delete, modify, access, or query variables if you do not explicitly specify these variables in the code.

worker的工作空间已经不是我们能看到的那个工作空间了,它具有特殊性,我猜是为了避免几个worker之间的干扰。于是再度尝试,终于明白:

[~,y] = ode45(@(t,y)scan.sys_odes(t,y), T_span, [scan.init_position; scan.init_velocity]);

至此柳暗花明,parfor顺利启动。

缺点

虽然创建类的实例避免了大量传入参数以及易错的persistent参数重复初始化过程,但是不同的实例之间由于不再共享原来global声明的变量,所以占用内存会大几倍,也无法对global变量进行修改。但是在内存不是主要考虑因素时,内存占用是可以接受的。另外parfor本身要求各次循环间相对独立,各个worker对切片类变量的访问有规则制约,所以变量不共享也是parfor需要的。

补充

当初我用global的目的之一是减少参数传递的次数,后来为了使用parfor将原来的global和persistent参数都作为了类的properties,而类的methods总是要求传入对象实例(除非是静态类),这就又让我感到对大量传入(复制)参数的忧虑。实际上,如果不仔细考量这个问题,这个担心确实会变成现实。下面提供两个参考信息:

  1. MATLAB有一套关于内存使用的优化策略,其中一些旨在减少函数调用时的内存操作,如果你遵循这些规则,并据此优化类的methods,那函数调用时即使传入大量参数,也不会成为问题。这也是我选择的方法。
  2. MATLAB可以定义值类(Value Class)也可以定义句柄类(Handle Class),句柄类实现了类似C++的引用的能力。这也许可以解决大量传参的内存复制问题。但我没有在parfor中试过。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值