缓存Matlab函数结果到文件

时间:2012-02-14 21:01:47

标签: matlab caching simulation bitstring

我正在用Matlab编写模拟。 我最终将运行这个模拟数百次。 在每次模拟运行中,都有数百万个模拟周期。 在每个循环中,我计算一个非常复杂的函数,需要~0.5秒才能完成。 函数输入是长位数组(> 1000位) - 这是01的数组。 我将位数组保存在01的矩阵中,对于它们中的每一个,我只运行一次函数 - 因为我将结果保存在不同的数组(res)中并检查是否在运行函数之前,位数组在矩阵中:

for i=1:1000000000
    %pick a bit array somehow
    [~,indx] = ismember(bit_array,bit_matrix,'rows');
    if indx == 0
        indx = length(results) + 1;
        bit_matrix(indx,:) = bit_array;
        res(indx) = complex_function(bit_array);
    end
    result = res(indx)
    %do something with result
end

我有两个问题,真的:

  1. 是否有更有效的方法在矩阵中找到行的索引然后'ismember'?

  2. 由于我多次运行模拟,并且我得到的位数组有很大的重叠,我想在运行之间缓存矩阵,这样我就不会重新计算相同的函数比特阵列一遍又一遍。我怎么做?

3 个答案:

答案 0 :(得分:5)

这两个问题的答案是使用地图。有几个步骤可以做到这一点。

  1. 首先,您需要一个函数将bit_array转换为数字或字符串。例如,将[0 1 1 0 1 0]转为'011010'。 (Matlab仅支持标量或字符串键,这就是需要此步骤的原因。)

  2. 定义地图对象

    cachedRunMap = containers.Map;  %See edit below for more on this
    
  3. 要检查是否已运行特定案例,请使用iskey

    cachedRunMap.isKey('011010');
    
  4. 要添加运行结果,请使用附加语法

    cachedRunMap('011010') = [0 1 1 0 1];  %Or whatever your result is.  
    
  5. 要检索缓存结果,请使用获取语法

    tmpResult = cachedRunMap.values({'011010'});
    
  6. 这应该有效地存储和检索值,直到你的系统内存不足为止。


    把这些放在一起,现在你的代码看起来像这样:

    %Hacky magic function to convert an array into a string of '0' and '1'
    strFromBits = @(x) char((x(:)'~=0)+48); %'
    
    %Initialize the map
    cachedRunMap = containers.Map;
    
    %Loop, computing and storing results as needed
    for i=1:1000000000
        %pick a bit array somehow
        strKey = strFromBits(bit_array);
        if cachedRunMap.isKey(strKey)
            result = cachedRunMap(strKey);
        else
            result = complex_function(bit_array);
            cachedRunMap(strKey) = reult;
        end
        %do something with result
    end
    

    如果您想要一个不是字符串的键,则需要在步骤2中声明。例如:

    cachedRunMap = containers.Map('KeyType', 'char', 'ValueType', 'any');
    cachedRunMap = containers.Map('KeyType', 'double', 'ValueType', 'any');
    cachedRunMap = containers.Map('KeyType', 'uint64', 'ValueType', 'any');
    cachedRunMap = containers.Map('KeyType', 'uint64', 'ValueType', 'double');
    

    设置KeyType 'char'将地图设置为使用字符串作为键。所有其他类型必须是标量。


    关于问题的扩展(根据您最近的评论)

    • 在会话之间保存数据:将此地图保存到* .mat文件应该没有问题,直到系统内存的限制

    • 清除旧数据:我不知道将LRU功能添加到此地图的简单方法。如果你能找到一个Java实现,你可以很容易地在Matlab中使用它。否则,需要考虑确定跟踪上次使用密钥的最有效方法。

    • 在并发会话之间共享数据:正如您所指出的,这可能需要数据库才能有效执行。 DB表将是两列(如果要实现LRU功能,则为3),键,值,(如果需要,可以使用上次使用的时间)。如果您的“结果”不是一种容易适合SQL的类型(例如,非统一大小的数组或复杂的结构),那么您需要对如何存储它进行额外的考虑。您还需要一种方法来访问数据库(例如,数据库工具箱或Mathworks文件交换上的各种工具)。最后,您需要在服务器上实际设置一个数据库(例如MySql,如果你便宜,像我一样,或者你最有经验的任何东西,或者可以找到最多的帮助。)这实际上并不那么难,但它第一次花费一些时间和精力。

      另一种需要考虑的方法(效率低得多,但不需要数据库)是将数据存储分解为大量(例如1000或数百万)的地图。将每个文件保存到一个单独的* .mat文件中,文件名基于该映射中包含的键(例如字符串键的前N个字符),然后根据需要在会话之间加载/保存这些文件。这将非常慢......根据您的使用情况,每次从源函数重新计算可能会更快......但这是我能想到的最好的方式而不设置数据库(显然是一个更好的答案)。

答案 1 :(得分:0)

  1. 对于大型列表,手动编码的二进制搜索可以击败成员,如果按排序顺序维护它并不太昂贵。如果这真的是你的瓶颈。使用探查器查看会员实际上花了多少钱。如果没有太多不同的值,您也可以将它们存储在containers.Map中,方法是将bit_matrix打包到char数组中并将其用作键。

  2. 如果它足够小以适合内存,您可以使用saveload将其存储在MAT文件中。他们可以存储任何基本的Matlab数据类型。模拟save在其运行结束时累计resbit_matrix,并在下次调用时重新load

答案 2 :(得分:0)

我认为您应该使用containers.Map()来加速。

一般的想法是保存包含所有哈希值的地图。如果您的位数组在散列函数下具有均匀分布,则大多数情况下您不需要调用ismember

由于key type不能是Matlab中的数组,因此可以在比特数组上计算一些哈希函数。

例如:

 function s = GetHash(bitArray)
      s = mod( sum(bitArray), intmax('uint32'));          
 end

这是一个糟糕的哈希函数,但足以理解原理。 然后代码看起来像:

map = containers.Map('KeyType','uint32','ValueType','any');
for i=1:1000000000
    %pick a bit array somehow
    s = GetHash(bit_array);   
    if isKey  %Do the slow check.
        [~,indx] = ismember(bit_array,bit_matrix,'rows');
    else
       map(s) = 1;
       continue;
    end
    if indx == 0
        indx = length(results) + 1;
        bit_matrix(indx,:) = bit_array;
        res(indx) = complex_function(bit_array);
    end
    result = res(indx)
    %do something with result
end