Кеширование результатов функции Matlab в файл - PullRequest
3 голосов
/ 15 февраля 2012

Я пишу симуляцию в Matlab.В конце концов я буду запускать эту симуляцию сотни раз.В каждом прогоне симуляции миллионы циклов симуляции.В каждом из этих циклов я вычисляю очень сложную функцию, для завершения которой требуется ~0.5 сек.Ввод функции представляет собой массив длинных битов (> 1000 бит), который представляет собой массив 0 и 1.Я храню битовые массивы в матрицах 0 и 1, и для каждого из них я запускаю функцию только один раз - поскольку я сохраняю результат в другом массиве (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 ]

5 голосов
/ 15 февраля 2012

Ответ на оба вопроса заключается в использовании карты.Для этого есть несколько шагов:

  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'});
    

Это должно эффективно хранить и извлекать значения до тех пор, пока не закончится системная память.


Если сложить все вместе, теперь ваш код будет выглядеть следующим образом:

%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 довольно легко.В противном случае потребовалось бы некоторое время, чтобы определить наиболее эффективный метод отслеживания времени последнего использования ключа.

  • Обмен данными между одновременными сеансами: как вы указали, это, вероятно, требуетБаза данных для эффективного выполнения.Таблица БД будет состоять из двух столбцов (3, если вы хотите реализовать функции LRU), ключ, значение (и время последнего использования при желании).Если ваш «результат» не является типом, который легко вписывается в SQL (например, массив с неоднородным размером или сложная структура), вам нужно будет подумать, как его хранить.Вам также понадобится метод для доступа к базе данных (например, набор инструментов базы данных или различные инструменты для обмена файлами Mathworks).Наконец, вам нужно будет на самом деле настроить базу данных на сервере (например, MySql, если вы дешевы, как я, или что-то, с чем у вас больше всего опыта, или вы можете найти большую помощь.) На самом деле это не так уж сложно, но этоВ первый раз отнимает немного времени и усилий.

    Другой подход, который нужно рассмотреть (гораздо менее эффективный, но не требующий базы данных), состоит в том, чтобы разбить хранилище данных на большое (например, 1000 или миллионы).количество карт.Сохраните каждый файл в отдельный файл * .mat с именем файла на основе ключей, содержащихся в этой карте (например, первые N символов вашего строкового ключа), а затем загрузите / сохраните эти файлы между сеансами по мере необходимости.Это будет довольно медленно ... в зависимости от вашего использования может быть быстрее пересчитывать из исходной функции каждый раз ... но это лучший способ, который я могу придумать без настройки БД (очевидно, лучший ответ).

0 голосов
/ 15 февраля 2012

Я думаю, что вы должны использовать 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
0 голосов
/ 15 февраля 2012
  1. Для большого списка двоичный поиск с ручным кодированием может превзойти ismember, если поддержание его в отсортированном порядке не слишком дорого. Если это действительно ваше узкое место. Используйте профилировщик, чтобы увидеть, сколько ismember действительно стоит вам. Если не так много различных значений, вы также можете сохранить их в контейнерах. Создайте карту bit_matrix в массиве char и используйте его в качестве ключа.

  2. Если он достаточно мал, чтобы поместиться в памяти, вы можете сохранить его в файле MAT, используя save и load. Они могут хранить любой базовый тип данных Matlab. Имейте симуляцию save накопленных res и bit_matrix в конце своего прогона и повторно load их при следующем вызове.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...