Самый чистый способ кэширования результатов функции в MATLAB

У меня довольно тяжелая функция в MATLAB:

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

Это часто называют одинаковые параметры. Функция детерминирована, поэтому при заданных входных параметрах ее выход всегда будет одинаковым.

Каким будет самый простой способ хранения результатов вычисляемых входов в функции, так что если функция будет вызываться снова с тем же выходом, она сможет быстро ответить?

Является ли постоянная переменная, которая сопоставляет (используя containers.Mapили какой-либо другой класс) вход, который задает <in1, in2, in3>результат?

Обратите внимание, что любой метод, который требует сохранения данных на диске, в моем приложении не может быть и речи.

Грейдл, Н? N, N, N, АННА ?? N, N, N, N, N,

13

Ответов: 3


MATLAB теперь поставляется с функцией только для этой цели. Используемая техника называется «memoization», а имя функции «memoize».

Выезд: https://www.mathworks.com/help/matlab/ref/memoize.html


Ниже приведена идея класса 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

Стойкая карта - действительно хороший способ реализовать результаты кэширования. Преимущества, о которых я могу думать:

  • Нет необходимости реализовывать хеш-функцию для каждого типа данных.
  • Матрицы Matlab - это копирование на запись, что может обеспечить определенную эффективность памяти.
  • Если использование памяти является проблемой, можно контролировать, сколько результатов будет кэшировано.

Существует представление об обмене файлами, многомерный класс карты Дэвидом Янга, поставляется с функцией memoize () делает именно это. Реализация использует бит другой механизм (ссылка на локальную переменную), но идея примерно такая же. По сравнению с постоянной картой внутри каждой функции эта функция memoize () позволяет сохранить существующую функцию без изменений. И, как отметил Олег, использование DataHash (или эквивалента) может еще больше уменьшить использование памяти.

PS: Я широко использовал класс MapN, и он достаточно надежный. На самом деле я представил отчет об ошибке, и автор исправил его быстро.

MATLAB, кэширование,