在Matlab中使用parfor循环内的映射

时间:2013-08-08 17:56:55

标签: matlab parallel-processing memoization

我目前使用Matlab代码执行以下操作:

map = collections.Map;
for i = 1:N
    key = getKey(i);
    if isKey(map, key)
        % Return the value stored at key.
    else
        % Calculate a new value, store it in the map using key.
    end
end

这个循环需要很长时间才能运行,我想使用parfor来帮助提高效率。但是,我似乎无法为parfor循环内的地图赋值。关于我能做什么的任何想法? collections.Map的用法并不牢固,我愿意接受并行备忘的替代建议,只要它们快速有效(并且线程安全,我意识到Map可能不是)。

从下面的评论中添加:我希望有更多的线程安全方法在循环期间向地图添加新值,以便任何后续循环都可以使用预先计算的值。时间计算相当昂贵。

2 个答案:

答案 0 :(得分:1)

“线程安全”是错误的关注点。通常,“线程安全”不适用于Matlab对象,因为Matlab在M代码级别是单线程的。每个Matlab实例/进程只有一个Matlab解释器线程。并行化parfor迭代发生在单独的进程中,甚至发生在不同的机器上,而不是单独的线程,因此这将比多线程更多地是进程间通信问题。并且你不能通过赋值在parfor工作循环传递的状态和彼此之间或封闭的工作空间之间进行双向“通信”;我很确定数据传输只发生在循环开始和循环结束。

如果你真的想在parfor内部进行memoization,你需要某种类型的客户端 - 服务器存储机制。您需要做的是在所有Matlab池工作者可以看到的服务器上的进程中设置memoization缓存,但是在M代码中涉及的Matlab工作空间外部,并在循环内使用客户端代理对象通过某种类型的进程间通信查询缓存,如RMI或套接字调用。主“服务器”缓存对象甚至可以在主Matlab进程内部运行带有parfor循环的代码,只要它是一个不由M代码解释器管理的Java或C结构。缓存的服务器端将处理安全的并发访问(可能使用线程安全映射和多个工作线程,可能通过序列化请求)。客户端可以拥有本地有状态代理,只要它不是基于M代码索引来更新其元素。

更一般地说,如果您想要parfor次传递中的工作人员之间的交互,您需要一个共享数据存储,如数据库或文件系统,他们都可以访问。

也许你可以设置一个小的memcached实例,或者一个轻量级的内存数据库(可能是Derby,它随JDK提供?),并将其用作服务器端缓存,创建数据库句柄在所有工人中访问它。或者可能有一组较轻的Java对象,它们为ConcurrentMapsynchronized Map提供了一个瘦RMI接口?您还可以使用Matlab对象(如containers.Map)创建一个缓存服务器,方法是让一个单独的Matlab进程充当缓存服务器,在套接字上侦听请求并对其进行序列化,并让parfor个工作者通过它来访问它具有get()set()方法的客户端代理对象,它们使用缓存服务器进程上的IPC调用来完成工作。

这可能是一个很大的开销,所以如果计算确实非常昂贵,那将是值得的。

答案 1 :(得分:0)

显然你引用了containers.map。我会使用临时数组/单元格数组来存储parfor的产品,并在循环后分配所有新内容。

% original map
keySet = {'a','b','c','f'};
map = containers.Map(keySet, 1:length(keySet));

% simulates your keys/generated values 
getKey = {'f','g','h'};
getVal = (5:7);

% Parfor body
parfor i = 1:3
    key = getKey{i};
    if isKey(map, key)
        % Return the value stored at key.
        map(key);
    else
        % Calculate a new value, store it in a temp array/cell-array 
        keyNew{i} = key;
        valNew(i) = getVal(i);
    end
end

% assign to map the new pairs of keys/values
indNew = ~cellfun(@isempty, keyNew); % clean up empty cells
newMap = containers.Map(keyNew(indNew), valNew(indNew));
map = [map; newMap];

修改:您无法使用parfor并动态地为后续迭代提供值,因为subsequent iterations循环中没有parfor这样的内容。

来自parfor文档:

  

注意:由于迭代顺序的独立性,parfor的执行确实如此   不保证确定性结果。

这意味着(正如这个SO问题/答案很好地强调),并行执行中的迭代是AND必须被认为是独立的,而不是常规for循环。结果,它们的执行/完成顺序是不可预测的,并且它们对其他迭代的输出的访问/依赖是不可行的。