Skip to content

Commit fd400b4

Browse files
committed
* MappedTensor can now read and write an entire tensor in an accelerated fashion, with a smaller memory footprint than previously
* Changed the calling semantics for `MappedTensor/SliceFunction`, to permit specifying a "write-only" function
1 parent 8e8f723 commit fd400b4

File tree

1 file changed

+120
-72
lines changed

1 file changed

+120
-72
lines changed

MappedTensor.m

Lines changed: 120 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ function delete(mtVar)
314314
end
315315
end
316316

317-
%% Overloaded subsref, subsasg and end
317+
%% Overloaded subsref, subsasgn and end
318318
function [varargout] = subsref(mtVar, subs)
319319
% - More than one return argument means cell or dot referencing was
320320
% used
@@ -351,67 +351,70 @@ function delete(mtVar)
351351
nNumTotalDims = numel(mtVar.vnDimensionOrder);
352352
vnReferencedTensorSize = size(mtVar);
353353

354-
% - Handle different numbers of referencing dimensions
355-
if (nNumDims == 1)
356-
% - Translate from linear refs to indices
357-
nNumDims = nNumTotalDims;
358-
359-
% - Translate colon indexing
360-
if (iscolon(S.subs{1}))
361-
S.subs{1} = (1:numel(mtVar))';
362-
end
363-
364-
% - Get equivalent subscripted indexes
365-
vnTensorSize = size(mtVar);
366-
[cIndices{1:nNumDims}] = ind2sub(vnTensorSize, S.subs{1});
367-
368-
% - Permute indices and convert back to linear indexing
369-
vnInvOrder(mtVar.vnDimensionOrder(1:nNumTotalDims)) = 1:nNumTotalDims;
370-
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder);
371-
372-
try
373-
S.subs{1} = sub2ind(mtVar.vnOriginalSize, cIndices{vnInvOrder});
374-
catch
375-
error('MappedTensor:badsubscript', ...
354+
% - Catch "read entire stack" condition
355+
if (~all(cellfun(@iscolon, S.subs)))
356+
% - Handle different numbers of referencing dimensions
357+
if (nNumDims == 1)
358+
% - Translate from linear refs to indices
359+
nNumDims = nNumTotalDims;
360+
361+
% - Translate colon indexing
362+
if (iscolon(S.subs{1}))
363+
S.subs{1} = (1:numel(mtVar))';
364+
end
365+
366+
% - Get equivalent subscripted indexes
367+
vnTensorSize = size(mtVar);
368+
[cIndices{1:nNumDims}] = ind2sub(vnTensorSize, S.subs{1});
369+
370+
% - Permute indices and convert back to linear indexing
371+
vnInvOrder(mtVar.vnDimensionOrder(1:nNumTotalDims)) = 1:nNumTotalDims;
372+
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder);
373+
374+
try
375+
S.subs{1} = sub2ind(mtVar.vnOriginalSize, cIndices{vnInvOrder});
376+
catch
377+
error('MappedTensor:badsubscript', ...
376378
'*** MappedTensor: Subscript out of range.');
379+
end
380+
381+
elseif (nNumDims < nNumTotalDims)
382+
% - Wrap up trailing dimensions, matlab style, using linear indexing
383+
vnReferencedTensorSize(nNumDims) = prod(vnReferencedTensorSize(nNumDims:end));
384+
vnReferencedTensorSize = vnReferencedTensorSize(1:nNumDims);
385+
386+
% - Inverse permute index order
387+
vnInvOrder(mtVar.vnDimensionOrder(1:nNumDims)) = 1:nNumDims;
388+
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder(vnInvOrder ~= 0));
389+
S.subs = S.subs(vnInvOrder(vnInvOrder ~= 0));
390+
391+
elseif (nNumDims == nNumTotalDims)
392+
% - Simply permute and access tensor
393+
394+
% - Permute index order
395+
vnInvOrder(mtVar.vnDimensionOrder(1:nNumTotalDims)) = 1:nNumTotalDims;
396+
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder);
397+
S.subs = S.subs(vnInvOrder);
398+
399+
else % (nNumDims > nNumTotalDims)
400+
% - Check for non-colon references
401+
vbNonColon = ~cellfun(@iscolon, S.subs);
402+
403+
% - Check only trailing dimensions
404+
vbNonColon(1:nNumTotalDims) = false;
405+
406+
% - Check trailing dimensions for non-'1' indices
407+
if (any(cellfun(@(c)(~isequal(c, 1)), S.subs(vbNonColon))))
408+
% - This is an error
409+
error('MappedTensor:badsubscript', ...
410+
'*** MappedTensor: Subscript out of range.');
411+
end
412+
413+
% - Permute index order
414+
vnInvOrder(mtVar.vnDimensionOrder(1:nNumTotalDims)) = 1:nNumTotalDims;
415+
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder);
416+
S.subs = S.subs(vnInvOrder);
377417
end
378-
379-
elseif (nNumDims < nNumTotalDims)
380-
% - Wrap up trailing dimensions, matlab style, using linear indexing
381-
vnReferencedTensorSize(nNumDims) = prod(vnReferencedTensorSize(nNumDims:end));
382-
vnReferencedTensorSize = vnReferencedTensorSize(1:nNumDims);
383-
384-
% - Inverse permute index order
385-
vnInvOrder(mtVar.vnDimensionOrder(1:nNumDims)) = 1:nNumDims;
386-
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder(vnInvOrder ~= 0));
387-
S.subs = S.subs(vnInvOrder(vnInvOrder ~= 0));
388-
389-
elseif (nNumDims == nNumTotalDims)
390-
% - Simply permute and access tensor
391-
392-
% - Permute index order
393-
vnInvOrder(mtVar.vnDimensionOrder(1:nNumTotalDims)) = 1:nNumTotalDims;
394-
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder);
395-
S.subs = S.subs(vnInvOrder);
396-
397-
else % (nNumDims > nNumTotalDims)
398-
% - Check for non-colon references
399-
vbNonColon = ~cellfun(@iscolon, S.subs);
400-
401-
% - Check only trailing dimensions
402-
vbNonColon(1:nNumTotalDims) = false;
403-
404-
% - Check trailing dimensions for non-'1' indices
405-
if (any(cellfun(@(c)(~isequal(c, 1)), S.subs(vbNonColon))))
406-
% - This is an error
407-
error('MappedTensor:badsubscript', ...
408-
'*** MappedTensor: Subscript out of range.');
409-
end
410-
411-
% - Permute index order
412-
vnInvOrder(mtVar.vnDimensionOrder(1:nNumTotalDims)) = 1:nNumTotalDims;
413-
vnReferencedTensorSize = vnReferencedTensorSize(vnInvOrder);
414-
S.subs = S.subs(vnInvOrder);
415418
end
416419

417420
% - Reference the tensor data element
@@ -876,7 +879,7 @@ function disp(mtVar)
876879
% - Which dimension should we go along?
877880
if (nargin < 3)
878881
% - Find the first non-singleton dimension
879-
[nul, nDim] = find(vnSize > 1, 1, 'first');
882+
[nul, nDim] = find(vnSize > 1, 1, 'first'); %#ok<ASGLU>
880883
else
881884
nDim = varargin{2};
882885
end
@@ -905,7 +908,7 @@ function disp(mtVar)
905908
% - Which dimension should we go along?
906909
if (nargin < 3)
907910
% - Find the first non-singleton dimension
908-
[nul, nDim] = find(vnSize > 1, 1, 'first');
911+
[nul, nDim] = find(vnSize > 1, 1, 'first'); %#ok<ASGLU>
909912
else
910913
nDim = varargin{2};
911914
end
@@ -921,11 +924,11 @@ function disp(mtVar)
921924
end
922925

923926
%% SliceFunction - METHOD Execute a function on the entire tensor, in slices
924-
function [mtNewVar] = SliceFunction(mtVar, fhFunction, nSliceDim, vnSliceSize, varargin)
927+
function [mtNewVar] = SliceFunction(mtVar, fhFunction, nSliceDim, vnSliceSize, bWriteOnly, varargin)
925928
% SliceFunction - METHOD Execute a function on the entire tensor, in slices
926929
%
927930
% Usage: [<mtNewVar>] = SliceFunction(mtVar,
928-
% fhFunctionHandle, nSliceDim <, vnSliceSize,> ...)
931+
% fhFunctionHandle, nSliceDim <, vnSliceSize, bWriteOnly,> ...)
929932
%
930933
% 'mtVar' is a MappedTensor. This tensor will be sliced up along
931934
% dimensions 'nSliceDim', with each slice passed individually to
@@ -1011,6 +1014,14 @@ function disp(mtVar)
10111014
end
10121015
end
10131016

1017+
% - Do we need to read from the source tensor, or does the slice
1018+
% function only write?
1019+
if (nargin(fhFunction) == 0) || exist('bWriteOnly', 'var') || (bWriteOnly == true)
1020+
bWriteOnly = true;
1021+
else
1022+
bWriteOnly = false;
1023+
end
1024+
10141025
% - If an explicit return argument is requested, construct a new tensor
10151026
if (nargout == 1)
10161027
bNewTensor = true;
@@ -1075,8 +1086,14 @@ function disp(mtVar)
10751086
mnTheseDestChunks = bsxfun(@plus, mnDestChunkIndices, [(nIndex-1) * nDestWindowStep 0 0]);
10761087

10771088
% - Handle a "slice assign" function with no input arguments efficiently
1078-
if (nargin(fhFunction) == 0)
1079-
tData = fhFunction();
1089+
if (bWriteOnly)
1090+
% - Call function
1091+
if (nargin(fhFunction) == 0)
1092+
tData = fhFunction();
1093+
else
1094+
tData = fhFunction([], nIndex, varargin{:});
1095+
end
1096+
10801097
mtVar.hShimFunc('write_chunks', mtNewVar.hRealContent, mnTheseDestChunks, 1:numel(tData), size(tData), mtNewVar.strClass, mtNewVar.nHeaderBytes, tData ./ mtVar.fRealFactor, mtVar.bBigEndian);
10811098

10821099
else
@@ -1539,31 +1556,62 @@ function isvalidsubscript(oRefs)
15391556

15401557
% mt_read_data - FUNCTION Read a set of indices from the file, in an optimsed fashion
15411558
function [tData] = mt_read_data(hShimFunc, hDataFile, sSubs, vnTensorSize, strClass, nHeaderBytes, bBigEndian, hRepSumFunc, hChunkLengthFunc)
1559+
1560+
% - Catch "read whole tensor" condition
1561+
if (all(cellfun(@iscolon, sSubs.subs)))
1562+
nNumStackElems = prod(vnTensorSize);
1563+
vnFileChunkIndices = [1 1 nNumStackElems];
1564+
tData = 1:nNumStackElems; % Use a pre-allocated vector to save memory
1565+
1566+
% - Read data
1567+
tData = hShimFunc('read_chunks', hDataFile, vnFileChunkIndices, ...
1568+
tData, tData, vnTensorSize, ...
1569+
strClass, nHeaderBytes, double(bBigEndian));
1570+
1571+
% - Reshape stack and return
1572+
tData = reshape(tData, vnTensorSize);
1573+
return;
1574+
end
1575+
15421576
% - Check referencing and convert to linear indices
15431577
[vnLinearIndices, vnDataSize] = ConvertColonsCheckLims(sSubs.subs, vnTensorSize, hRepSumFunc);
15441578

15451579
% - Maximise chunk probability and minimise number of reads by reading
15461580
% only sorted unique entries
1547-
[vnUniqueIndices, nul, vnReverseSort] = unique_accel(vnLinearIndices);
1581+
[vnLinearIndices, nul, vnReverseSort] = unique_accel(vnLinearIndices); %#ok<ASGLU>
15481582

15491583
% - Split into readable chunks
1550-
mnFileChunkIndices = SplitFileChunks(vnUniqueIndices, hChunkLengthFunc);
1584+
mnFileChunkIndices = SplitFileChunks(vnLinearIndices, hChunkLengthFunc);
15511585

15521586
% - Call shim read function
1553-
tData = hShimFunc('read_chunks', hDataFile, mnFileChunkIndices, vnUniqueIndices, vnReverseSort, vnDataSize, strClass, nHeaderBytes, double(bBigEndian));
1587+
tData = hShimFunc('read_chunks', hDataFile, mnFileChunkIndices, vnLinearIndices, vnReverseSort, vnDataSize, strClass, nHeaderBytes, double(bBigEndian));
15541588
end
15551589

15561590
% mt_write_data - FUNCTION Read a set of indices from the file, in an optimsed fashion
15571591
function mt_write_data(hShimFunc, hDataFile, sSubs, vnTensorSize, strClass, nHeaderBytes, tData, bBigEndian, hRepSumFunc, hChunkLengthFunc)
1592+
1593+
% - Catch "read whole tensor" condition
1594+
if (all(cellfun(@iscolon, sSubs.subs)))
1595+
nNumStackElems = prod(vnTensorSize);
1596+
vnFileChunkIndices = [1 1 nNumStackElems];
1597+
vnLinearIndices = 1:nNumStackElems;
1598+
1599+
% - Write data and return
1600+
hShimFunc('write_chunks', hDataFile, vnFileChunkIndices, ...
1601+
vnLinearIndices, vnTensorSize, ...
1602+
strClass, nHeaderBytes, cast(tData, strClass), double(bBigEndian));
1603+
return;
1604+
end
1605+
15581606
% - Check referencing and convert to linear indices
15591607
[vnLinearIndices, vnDataSize] = ConvertColonsCheckLims(sSubs.subs, vnTensorSize, hRepSumFunc);
15601608

15611609
% - Maximise chunk probability and minimise number of writes by writing
15621610
% only sorted unique entries
1563-
[vnUniqueIndices, vnUniqueDataIndices] = unique_accel(vnLinearIndices);
1611+
[vnLinearIndices, vnUniqueDataIndices] = unique_accel(vnLinearIndices);
15641612

15651613
% - Split into readable chunks
1566-
mnFileChunkIndices = SplitFileChunks(vnUniqueIndices, hChunkLengthFunc);
1614+
mnFileChunkIndices = SplitFileChunks(vnLinearIndices, hChunkLengthFunc);
15671615

15681616
% - Call shim writing function
15691617
hShimFunc('write_chunks', hDataFile, mnFileChunkIndices, vnUniqueDataIndices, vnDataSize, strClass, nHeaderBytes, cast(tData, strClass), double(bBigEndian));
@@ -1790,7 +1838,7 @@ function mt_write_data_chunks(hDataFile, mnFileChunkIndices, vnUniqueDataIndices
17901838
case 'open'
17911839
if (nargin == 2)
17921840
[varargout{1}] = fopen(varargin{1}, 'r+');
1793-
[nul, nul, varargout{2}, nul] = fopen(varargout{1}); %#ok<NASGU>
1841+
[nul, nul, varargout{2}, nul] = fopen(varargout{1}); %#ok<ASGLU,NASGU>
17941842
else
17951843
varargout{1} = fopen(varargin{1}, 'r+', varargin{2});
17961844
end

0 commit comments

Comments
 (0)