I Exported Them Again From Matlab

Last year I wrote an article on improving the operation of the save function. The commodity discussed various ways by which we tin can store Matlab data on disk. However, in many cases nosotros are interested in a byte-stream serialization, in social club to transmit information to external processes.
The asking to get a serialized byte-stream of Matlab data has been around for many years (case), but MathWorks has never released a documented way of serializing and unserializing data, except by storing onto a disk file and later loading it from file. Naturally, using a disk file significantly degrades performance. Nosotros could always use a RAM-disk or wink memory for improved performance, but in any case this seems like a major overkill to such a simple requirement.
In last yr's article, I presented a File Substitution utility for such generic serialization/deserialization. However, that utility is express in the types of data that it supports, and while it is relatively fast, there is a much better, more generic and faster solution.
The solution appears to employ the undocumented built-in functions getByteStreamFromArray and getArrayFromByteStream , which are apparently used internally by the save and load functions. The usage is very simple:

byteStream = getByteStreamFromArray(anyData);              % 1xN uint8 array              anyData = getArrayFromByteStream(byteStream);

byteStream = getByteStreamFromArray(anyData); % 1xN uint8 array anyData = getArrayFromByteStream(byteStream);

Many Matlab functions, documented and undocumented alike, are divers in XML files within the %matlabroot%/bin/registry/ binder; our specific functions can be found in %matlabroot%/bin/registry/hgbuiltins.xml. While other functions include information nearly their location and number of input/output args, these functions do non. Their only XML attribute is type = ":all:", which seems to indicate that they take all data types as input. Despite the fact that the functions are defined in hgbuiltins.xml, they are not limited to HG objects – we can serialize basically whatsoever Matlab data: structs, form objects, numeric/cell arrays, sparse data, Coffee handles, timers, etc. For case:

              % Simple Matlab data              >> byteStream = getByteStreamFromArray(              pi              )              % 1x72 uint8 array              byteStream =   Columns              1              through              19              0              i              73              77              0              0              0              0              14              0              0              0              56              0              0              0              half dozen              0              0              Columns              20              through              38              0              8              0              0              0              6              0              0              0              0              0              0              0              v              0              0              0              8              0              Columns              39              through              57              0              0              1              0              0              0              ane              0              0              0              one              0              0              0              0              0              0              0              9              Columns              58              through              72              0              0              0              8              0              0              0              24              45              68              84              251              33              9              64              >> getArrayFromByteStream(byteStream)              ans              =              3.14159265358979              % A cell assortment of several data types              >> byteStream = getByteStreamFromArray(              {              pi,              'abc',              struct              (              'a',5              )              }              );              % 1x312 uint8 assortment              >> getArrayFromByteStream(byteStream)              ans              =              [              3.14159265358979              ]              'abc'              [1x1              struct              ]              % A Java object              >> byteStream = getByteStreamFromArray(coffee.awt.Color.cherry-red              );              % 1x408 uint8 assortment              >> getArrayFromByteStream(byteStream)              ans              = coffee.awt.Color              [r=255,g=0,b=0              ]              % A Matlab timer              >> byteStream = getByteStreamFromArray(timer);              % 1x2160 uint8 assortment              >> getArrayFromByteStream(byteStream)              Timer Object: timer-two              Timer Settings       ExecutionMode: singleShot              Menses:              1              BusyMode: drib             Running: off    Callbacks            TimerFcn:              ''              ErrorFcn:              ''              StartFcn:              ''              StopFcn:              ''              % A Matlab class object              >> byteStream = getByteStreamFromArray(matlab.Arrangement              );              % 1x1760 uint8 array              >> getArrayFromByteStream(byteStream)              ans              =   System: matlab.Arrangement            

% Unproblematic Matlab data >> byteStream = getByteStreamFromArray(pi) % 1x72 uint8 assortment byteStream = Columns ane through xix 0 i 73 77 0 0 0 0 14 0 0 0 56 0 0 0 6 0 0 Columns twenty through 38 0 8 0 0 0 6 0 0 0 0 0 0 0 five 0 0 0 8 0 Columns 39 through 57 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 nine Columns 58 through 72 0 0 0 viii 0 0 0 24 45 68 84 251 33 9 64 >> getArrayFromByteStream(byteStream) ans = 3.14159265358979 % A cell array of several data types >> byteStream = getByteStreamFromArray({pi, 'abc', struct('a',5)}); % 1x312 uint8 array >> getArrayFromByteStream(byteStream) ans = [iii.14159265358979] 'abc' [1x1 struct] % A Java object >> byteStream = getByteStreamFromArray(coffee.awt.Color.blood-red); % 1x408 uint8 assortment >> getArrayFromByteStream(byteStream) ans = java.awt.Colour[r=255,g=0,b=0] % A Matlab timer >> byteStream = getByteStreamFromArray(timer); % 1x2160 uint8 array >> getArrayFromByteStream(byteStream) Timer Object: timer-2 Timer Settings ExecutionMode: singleShot Period: 1 BusyMode: driblet Running: off Callbacks TimerFcn: '' ErrorFcn: '' StartFcn: '' StopFcn: '' % A Matlab class object >> byteStream = getByteStreamFromArray(matlab.System); % 1x1760 uint8 array >> getArrayFromByteStream(byteStream) ans = Organisation: matlab.System

Serializing HG objects

Of course, we can also serialize/deserialize also HG controls, plots/axes and even entire figures. When doing and so, information technology is important to serialize the handle of the object, rather than its numeric handle, since we are interested in serializing the graphic object, not the scalar numeric value of the handle:

              % Serializing a simple figure with toolbar and menubar takes most 0.5 MB !              >> hFig = handle(              figure              );              % a new default Matlab figure              >>              length              (getByteStreamFromArray(hFig)              )              ans              =              479128              % Removing the menubar and toolbar removes much of this amount:              >>              set up              (hFig,              'menuBar','none',              'toolbar','none'              )              >>              length              (getByteStreamFromArray(hFig)              )              ans              =              11848              %!!!              % Plot lines are not about as "expensive" as the toolbar/menubar              >> x=0:.01:five; hp=plot              (10,sin              (x)              ); >> byteStream = getByteStreamFromArray(hFig); >>              length              (byteStream)              ans              =              33088              >>              delete              (hFig); >> hFig2 = getArrayFromByteStream(byteStream)              hFig2 =              figure            

% Serializing a unproblematic figure with toolbar and menubar takes nearly 0.5 MB ! >> hFig = handle(figure); % a new default Matlab figure >> length(getByteStreamFromArray(hFig)) ans = 479128 % Removing the menubar and toolbar removes much of this amount: >> set(hFig, 'menuBar','none', 'toolbar','none') >> length(getByteStreamFromArray(hFig)) ans = 11848 %!!! % Plot lines are not nearly every bit "expensive" as the toolbar/menubar >> ten=0:.01:five; hp=plot(x,sin(x)); >> byteStream = getByteStreamFromArray(hFig); >> length(byteStream) ans = 33088 >> delete(hFig); >> hFig2 = getArrayFromByteStream(byteStream) hFig2 = figure

The interesting thing hither is that when we deserialize a byte-stream of an HG object, it is automatically rendered onscreen. This could be very useful for persistence mechanisms of GUI applications. For example, we tin can relieve the figure handles in file so that if the awarding crashes and relaunches, information technology simply loads the file and we get exactly the same GUI state, consummate with graphs and what-not, simply equally before the crash. Although the figure was deleted in the concluding instance, deserializing the data caused the figure to reappear.
Nosotros practice not demand to serialize the entire figure. Instead, we could choose to serialize only a specific plot line or axes. For example:

>> 10=0:0.01:5; hp=plot              (x,sin              (10)              ); >> byteStream = getByteStreamFromArray(handle(hp)              );              % 1x13080 uint8 array              >> hLine = getArrayFromByteStream(byteStream)              ans              = 	graph2d.lineseries            

>> x=0:0.01:5; hp=plot(ten,sin(x)); >> byteStream = getByteStreamFromArray(handle(hp)); % 1x13080 uint8 array >> hLine = getArrayFromByteStream(byteStream) ans = graph2d.lineseries

This could too be used to easily clone (copy) whatsoever figure or other HG object, by simply calling getArrayFromByteStream (note the corresponding copyobj function, which I bet uses the same underlying mechanism).
Also notation that unlike HG objects, deserialized timers are NOT automatically restarted; perhaps the Running property is labeled transient or dependent. Properties defined with these attributes are apparently not serialized.

Performance aspects

Using the builtin getByteStreamFromArray and getArrayFromByteStream functions tin provide meaning performance speedups when caching Matlab data. In fact, it could be used to shop otherwise unsupported objects using the salve -v6 or savefast alternatives, which I discussed in my save operation article. Robin Ince has shown how this can exist used to reduce the combined caching/uncaching run-time from 115 secs with plain-vanilla salvage , to just 11 secs using savefast . Robin hasn't tested this in his post, just since the serialized data is a elementary uint8 array, it is intrinsically supported by the save -v6 option, which is the fastest alternative of all:

>> byteStream = getByteStreamFromArray(hFig); >>              tic,              save              (              'examination.mat','-v6','byteStream'              );              toc              Elapsed time              is              0.001924              seconds. >>              load              (              'test.mat'              )              >> data =              load              (              'exam.mat'              )              information =     byteStream:              [1x33256              uint8              ]              >> getArrayFromByteStream(data.byteStream              )              ans              =              figure            

>> byteStream = getByteStreamFromArray(hFig); >> tic, save('test.mat','-v6','byteStream'); toc Elapsed time is 0.001924 seconds. >> load('test.mat') >> data = load('test.mat') data = byteStream: [1x33256 uint8] >> getArrayFromByteStream(data.byteStream) ans = figure

Moreover, nosotros can now use coffee.util.Hashtable to store a cache map of whatever Matlab data, rather than use the much slower and more limited containers.Map class provided in Matlab.
Finally, note that equally built-in functions, these functions could change without prior find on any future Matlab release.

MEX interface – mxSerialize/mxDeserialize

To complete the picture, MEX includes a couple of undocumented functions mxSerialize and mxDeserialize, which correspond to the above functions. getByteStreamFromArray and getArrayFromByteStream apparently call them internally, since they provide the same results. Back in 2007, Brad Phelan wrote a MEX wrapper that could exist used straight in Matlab (mxSerialize.c, mxDeserialize.c). The C interface was very elementary, and then was the usage:

              #include "mex.h"              EXTERN_C mxArray*              mxSerialize(mxArray              const              *              )              ;              EXTERN_C mxArray*              mxDeserialize(              const              void              *,              size_t              )              ;              void              mexFunction(              int              nlhs, mxArray              *plhs[              ],              int              nrhs,              const              mxArray              *prhs[              ]              )              {              if              (nlhs              &&              nrhs)              {              plhs[              0              ]              =              (mxArray              *              )              mxSerialize(prhs[              0              ]              )              ;              //plhs[0] = (mxArray *) mxDeserialize(mxGetData(prhs[0]), mxGetNumberOfElements(prhs[0]));              }              }            

#include "mex.h" EXTERN_C mxArray* mxSerialize(mxArray const *); EXTERN_C mxArray* mxDeserialize(const void *, size_t); void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if (nlhs && nrhs) { plhs[0] = (mxArray *) mxSerialize(prhs[0]); //plhs[0] = (mxArray *) mxDeserialize(mxGetData(prhs[0]), mxGetNumberOfElements(prhs[0])); } }

Unfortunately, MathWorks has removed the C interface for these functions from libmx in R2014a, keeping simply their C++ interfaces:

mxArray*              matrix::              detail              ::              noninlined              ::              mx_array_api              ::              mxSerialize              (mxArray              const              *anyData)              mxArray*              matrix::              detail              ::              noninlined              ::              mx_array_api              ::              mxDeserialize              (              void              const              *byteStream,              unsigned              __int64 numberOfBytes)              mxArray*              matrix::              particular              ::              noninlined              ::              mx_array_api              ::              mxDeserializeWithTag              (              void              const              *byteStream,              unsigned              __int64 numberOfBytes,              char              const              *              *tagName)            

mxArray* matrix::detail::noninlined::mx_array_api::mxSerialize(mxArray const *anyData) mxArray* matrix::detail::noninlined::mx_array_api::mxDeserialize(void const *byteStream, unsigned __int64 numberOfBytes) mxArray* matrix::detail::noninlined::mx_array_api::mxDeserializeWithTag(void const *byteStream, unsigned __int64 numberOfBytes, char const* *tagName)

These are not the but MEX functions that were removed from libmx in R2014a. Hundreds of other C functions were too removed with them, some of them quite of import (e.g., mxCreateSharedDataCopy). A few hundred new C++ functions were added in their place, but I fearfulness that these are non attainable to MEX users without a lawmaking alter (meet beneath). libmx has e'er changed between Matlab releases, only not so drastically for many years. If you rely on any undocumented MEX functions in your lawmaking, at present would be a good time to recheck information technology, earlier R2014a is officially released.
Thanks to Bastian Ebeling, we tin can still use these interfaces in our MEX code by just renaming the MEX file from .c to .cpp and modifying the lawmaking as follows:

              #include "mex.h"              // MX_API_VER has unfortunately not changed between R2013b and R2014a,              // so nosotros apply the new MATRIX_DLL_EXPORT_SYM as an ugly hack instead              #if divers(__cplusplus) && divers(MATRIX_DLL_EXPORT_SYM)              #define EXTERN_C extern              namespace              matrix{              namespace              particular{              namespace              noninlined{              namespace              mx_array_api{              

0 Response to "I Exported Them Again From Matlab"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel