【问题标题】:Cleanest way to cache function results in MATLAB在 MATLAB 中缓存函数结果的最简洁方法
【发布时间】:2014-10-02 14:26:43
【问题描述】:

我在 MATLAB 中有相当繁重的功能:

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

经常使用相同的参数调用它。 该函数是确定性的,因此对于给定的输入参数,其输出将始终相同。

将计算输入的结果存储在函数中的最简单方法是什么,这样如果再次调用该函数并使用相同的输出,它将能够快速响应?

将(使用containers.Map 或其他类)输入集<in1, in2, in3> 映射到结果的持久变量是要走的路吗?

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

【问题讨论】:

  • 我看到了(并在问题中提出了建议),但问题是我找不到合理的方法来散列 3 个输入参数,因此它适合该类。
  • 你的三个输入是什么类型的?它们是标量吗?向量?矩阵?它们的正常值范围是多少?您希望缓存多少个值?
  • @Shai- 三个输入是多维矩阵,输出也是。几乎所有值都是 0 或 1,尽管有些可能在 (0,1) 中。
  • 不要重新发明轮子——你所追求的就是“记忆化”。谷歌matlab memoization,你会发现一些有用的结果。一些示例(未检查代码有效性): - Loren Shure on memoization - A matlab method on Matlab File Exchange

标签: matlab caching


【解决方案1】:

下面是一个 CacheableFunction 类的想法

  • 您的主要问题的所有答案似乎都指向同一个方向 - 持久映射是缓存结果的共识方式,我也这样做。
  • 如果输入是数组,则需要将它们散列为字符串或标量以用作映射键。有很多方法可以将您的 3 个输入数组散列为一个键,我在下面的解决方案中使用了 DataHash
  • 我选择将其设为类而不是 memoize 之类的函数,以便可以一次性动态指定输入哈希函数,而不是硬编码。
  • 根据您的输出形式,它还使用dzip/dunzip 来减少已保存输出的占用空间。
  • 潜在的改进:当内存占用接近某个限制时,一种聪明的方法来决定从持久映射中删除哪些元素。

类定义

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

【讨论】:

  • 你选择的命名约定有点笨拙,开头的描述有点厚,否则这个答案会得到更多的关注。
【解决方案2】:

MATLAB 现在附带了一个专门用于此目的的函数。 使用的技术称为“memoization”,函数名称为“memoize”。

查看:https://www.mathworks.com/help/matlab/ref/memoize.html

【讨论】:

【解决方案3】:

持久映射确实是实现缓存结果的好方法。我能想到的优点:

  • 无需为每种数据类型实现哈希函数。
  • Matlab 矩阵是写时复制的,可以提供一定的内存效率。
  • 如果内存使用是一个问题,可以控制要缓存多少结果。

有一个文件交换提交,A multidimensional map class by David Young,带有一个函数 memoize() 正是这样做的。它的实现使用了一些不同的机制(引用局部变量),但想法大致相同。与每个函数内部的持久化映射相比,这个 memoize() 函数允许在不修改的情况下对现有函数进行记忆。并且正如 Oleg 所指出的,使用 DataHash(或等价物)可以进一步减少内存使用量。

PS:我已经广泛使用了 MapN 类,它非常可靠。其实我已经提交了一个bug报告,作者及时修复了。

【讨论】:

  • 将 DataHash 添加到 memoize() 应该会减少内存占用。
  • 这是非常好的观点,当然也是正确的。性能权衡是哈希函数需要在每次调用的所有输入上进行评估。我猜它是否会更快取决于哈希函数的具体用例和性能。
猜你喜欢
  • 2012-06-26
  • 1970-01-01
  • 2017-02-02
  • 2013-03-22
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多