MATLAB中缓存函数结果的最简洁方法

13

我在MATLAB中有一个非常复杂的函数:

function [out] = f ( in1, in2, in3)

这个函数经常使用相同的参数进行调用。

这个函数是确定性的,因此对于给定的输入参数,其输出将始终相同。

最简单的存储计算结果的方法是什么?以便如果函数再次使用相同的输出进行调用,它可以快速回答?

使用持久变量映射(使用 containers.Map 或其他类)输入集合 <in1, in2, in3> 以获得结果是否是正确的做法?

请注意,在我的应用程序中,任何需要将数据保存到磁盘的方法都不可行。


请将 MATLAB 函数结果缓存到文件中。 - marsei
我看到了这个(并在问题中提出了建议),但问题是我找不到一个合理的方法来哈希3个输入参数,使其适合该类。 - R B
你的三个输入是什么类型?它们是标量、向量还是矩阵?它们的正常值范围是多少?你预计要缓存多少个值? - Shai
@Shai- 这三个输入是多维矩阵,输出也是如此。几乎所有的值都是0或1,尽管有些可能在(0,1)之间。 - R B
不要重复造轮子 - 你需要的是所谓的“记忆化”。在谷歌上搜索“matlab memoization”,你会找到一些有用的结果。以下是一些示例(未检查代码有效性):- Loren Shure on memoization - Matlab文件交换中的一个Matlab方法 - bavaza
3个回答

7
以下是一个可缓存函数类的想法。
  • 似乎所有主要问题的答案都指向同一方向 - 持久 Map 是缓存结果的共识方式,我也这样做。
  • 如果输入是数组,则需要将它们哈希为字符串或标量以用作 Map 键。有很多种方法可以将您的三个输入数组哈希成键,我在下面的解决方案中使用了DataHash
  • 我选择将其变成一个类,而不是像 memoize 一样的函数,这样输入哈希函数就可以动态指定一次,而不是硬编码。
  • 根据输出的形式,它还使用dzip/dunzip来减少保存输出的占用空间。
  • 潜在改进:聪明地决定何时从持久 Map 中删除哪些元素,当它的内存占用接近某个限制时。
类定义
classdef CacheableFunction < handle
    properties
        exeFun
        hashFun
        cacheMap
        nOutputs
        zipOutput
    end

    methods
        function obj = CacheableFunction(exeFun, hashFun, nOutputs)
            obj.exeFun = exeFun;
            obj.hashFun = hashFun;
            obj.cacheMap = containers.Map;
            obj.nOutputs = nOutputs;
            obj.zipOutput = [];
        end

        function [result] = evaluate(obj, varargin)

            thisKey = obj.hashFun(varargin);

            if isKey(obj.cacheMap, thisKey)
                if obj.zipOutput
                    result = cellfun(@(x) dunzip(x), obj.cacheMap(thisKey), 'UniformOutput', false);
                else
                    result = obj.cacheMap(thisKey);
                end
            else
                [result{1:obj.nOutputs}] = obj.exeFun(varargin);

                if isempty(obj.zipOutput)
                    obj.zipCheck(result);
                end

                if obj.zipOutput
                    obj.cacheMap(thisKey) = cellfun(@(x) dzip(x), result, 'UniformOutput', false);
                else
                    obj.cacheMap(thisKey) = result;
                end
            end
        end


        function [] = zipCheck(obj,C)
            obj.zipOutput = all(cellfun(@(x) isreal(x) & ~issparse(x) & any(strcmpi(class(x), ...
                {'double','single','logical','char','int8','uint8',...
                 'int16','uint16','int32','uint32','int64','uint64'})), C));
        end

    end
end

测试一下...

function [] = test_caching_perf()

A = CacheableFunction(@(x) long_annoying_function(x{:}), @(x) DataHash(x), 3);

B = rand(50, 50);
C = rand(50, 50);
D = rand(50, 50);

tic;
myOutput = A.evaluate(B, C, D);
toc

tic;
myOutput2 = A.evaluate(B, C, D);
toc

cellfun(@(x, y) all(x(:) == y(:)), myOutput, myOutput2)

end

function [A, B, C] = long_annoying_function(A, B, C)

    for ii = 1:5000000
        A = A+1;
        B = B+2;
        C = C+3;
    end
end

并产生结果

>> test_caching_perf
Elapsed time is 16.781889 seconds.
Elapsed time is 0.011116 seconds.
ans =
    1     1     1

您选择的命名约定有点笨重,开头的描述有点繁琐,否则这个答案会得到更多应有的关注。 - Oleg

6

不错的发现,这是R2017a版本中的新功能。现在应该是最佳答案了。 - Andras Deak -- Слава Україні
2
来自Loren的全新升级版memoize博客文章!(https://blogs.mathworks.com/loren/2018/07/05/memoize-functions-in-matlab/) - gnovice

5
持久映射确实是实现缓存结果的好方法。我能想到的优点如下:
  • 不需要为每种数据类型实现哈希函数。
  • Matlab矩阵是写时复制,这可以提供一定的内存效率。
  • 如果内存使用是一个问题,则可以控制缓存多少结果。
有一个文件交换提交,由David Young提供的多维映射类带有一个memoize()函数,正是这样做的。它的实现使用了一个略微不同的机制(引用本地变量),但思路是相同的。与每个函数内部的持久映射相比,这个memoize()函数允许现有函数在不修改的情况下被记忆。正如Oleg指出的那样,使用DataHash(或等价物)可以进一步减少内存使用。
PS:我广泛使用MapN类,它非常可靠。事实上,我已经提交了一个错误报告,作者迅速解决了它。

将DataHash添加到memoize()中应该会减少内存占用。 - Oleg
这是非常好的观点,而且肯定是正确的。性能上的权衡是哈希函数需要在每次调用时对所有输入进行评估。我猜它是否更快取决于特定的用例和哈希函数的性能。 - xmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接